Spring Boot (七): Mybatis極簡配置
1. 前言
ORM 框架的目的是簡化編程中的數據庫操作,經過這么多年的發展,基本上活到現在的就剩下兩家了,一個是宣稱可以不用寫 SQL 的 Hibernate ,一個是對 SQL 非常友好的 Mybaties ,,兩者各有特點,在企業級系統開發中可以根據需求靈活使用。發現一個有趣的現象:傳統企業大都喜歡使用 Hibernate ,互聯網行業通常使用 Mybatis 。
Hibernate 特點就是所有的 SQL 都用 Java 代碼來生成,不用跳出程序去寫(看) SQL ,有着編程的完整性,發展到最頂端就是 Spring Data Jpa 這種模式了,基本上根據方法名就可以生成對應的 SQL 了,有不太了解的可以看筆者的前兩篇文章 Spring Boot (三): ORM 框架 JPA 與連接池 Hikari 和 Spring Boot (六): 為 JPA 插上翅膀的 QueryDSL。
Mybatis 初期使用比較麻煩,需要各種配置文件、實體類、Dao 層映射關聯、還有一大推其它配置。當然 Mybatis 也發現了這種弊端,初期開發了 generator 可以根據表結果自動生產實體類、配置文件和 Dao 層代碼,可以減輕一部分開發量;后期也進行了大量的優化可以使用注解了,自動管理 Dao 層和配置文件等,發展到最頂端就是今天要講的這種模式了,mybatis-spring-boot-starter 就是 Spring Boot + Mybatis 可以完全注解不用配置文件,也可以簡單配置輕松上手。
2. 工程實戰
首先創建父工程 spring-boot-mybatis
,引入全局依賴包,如下:
代碼清單:spring-boot-mybatis/pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- mybatis-spring-boot-starter :目前最新版本為 2.1.0
2.1 極簡 xml 版
創建子工程 spring-boot-mybatis-xml
2.1.1 配置文件
application.yml 配置文件如下:
代碼清單:spring-boot-mybatis/spring-boot-mybatis-xml/src/main/resources/application.yml
server:
port: 8080
spring:
application:
name: spring-boot-mybatis-xml
datasource:
url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
hikari:
auto-commit: true
minimum-idle: 2
idle-timeout: 60000
connection-timeout: 30000
max-lifetime: 1800000
pool-name: DatebookHikariCP
maximum-pool-size: 5
mybatis:
type-aliases-package: com.springboot.springbootmybatisxml.model
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
- 這里使用 hikari 作為數據庫連接池
- Spring Boot 會自動加載
spring.datasource.*
相關配置,數據源會自動注入到sqlSessionFactory
中,sqlSessionFactory
會自動注入到 Mapper 中。 - 這里需要指定基礎配置文件和實體類映射文件的地址
mybatis-config.xml 配置文件如下:
代碼清單:spring-boot-mybatis/spring-boot-mybatis-xml/src/main/resources/mybatis/mybatis-config.xml
<configuration>
<typeAliases>
<typeAlias alias="Integer" type="java.lang.Integer" />
<typeAlias alias="Long" type="java.lang.Long" />
<typeAlias alias="HashMap" type="java.util.HashMap" />
<typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap" />
<typeAlias alias="ArrayList" type="java.util.ArrayList" />
<typeAlias alias="LinkedList" type="java.util.LinkedList" />
</typeAliases>
</configuration>
2.1.2 Mapper 映射文件
代碼清單:spring-boot-mybatis/spring-boot-mybatis-xml/src/main/resources/mybatis/mapper/UserMapper.xml
<mapper namespace="com.springboot.springbootmybatisxml.mapper.UserMapper" >
<resultMap id="BaseResultMap" type="com.springboot.springbootmybatisxml.model.User" >
<id column="id" property="id" jdbcType="VARCHAR" />
<result column="nick_name" property="nickName" jdbcType="VARCHAR" />
<result column="age" property="age" jdbcType="INTEGER" />
<result column="create_date" property="createDate" jdbcType="TIME"/>
</resultMap>
<sql id="Base_Column_List" >
id, nick_name, age, create_date
</sql>
<select id="getAll" resultMap="BaseResultMap" >
SELECT
<include refid="Base_Column_List" />
FROM user
</select>
<select id="getUser" parameterType="java.lang.String" resultMap="BaseResultMap" >
SELECT
<include refid="Base_Column_List" />
FROM
user
WHERE id = #{id}
</select>
<insert id="insertUser" parameterType="com.springboot.springbootmybatisxml.model.User">
<selectKey keyProperty="id" resultType="java.lang.String" order="BEFORE">
select uuid() as id from dual
</selectKey>
INSERT INTO
user
(id, nick_name, age, create_date)
VALUES
(#{id}, #{nickName}, #{age}, #{createDate})
</insert>
<update id="updateUser" parameterType="com.springboot.springbootmybatisxml.model.User">
UPDATE
user
SET
<if test="nickName != null">nick_name = #{nickName},</if>
<if test="age != null">age = #{age},</if>
<if test="createDate != null">create_date = #{createDate}</if>
WHERE
id = #{id}
</update>
<delete id="deleteUser" parameterType="java.lang.String">
DELETE FROM
user
WHERE
id = #{id}
</delete>
</mapper>
- namespace :需配置對應的接口
- 實現了簡單的 CRUD 操作
- 新增數據時選用 UUID 作為主鍵
- 動態條件可使用
<if>
標簽作判斷
2.1.3 Mapper 層代碼
代碼清單:spring-boot-mybatis/spring-boot-mybatis-xml/src/main/java/com/springboot/springbootmybatisxml/mapper/UserMapper.java
public interface UserMapper {
List<User> getAll();
User getUser(String id);
Long insertUser(User user);
Long updateUser(User user);
Long deleteUser(String id);
}
- 這里僅需定義接口方法, mybaties 會自動幫我們調用 xml 映射文件中的代碼。
2.1.4 啟動主類
代碼清單:spring-boot-mybatis/spring-boot-mybatis-xml/src/main/java/com/springboot/springbootmybatisxml/SpringBootMybatisXmlApplication.java
@SpringBootApplication
@MapperScan("com.springboot.springbootmybatisxml.mapper")
public class SpringBootMybatisXmlApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootMybatisXmlApplication.class, args);
}
}
- 在啟動主類上配置
@MapperScan
或者直接在 Mapper 類上增加注解@Mapper
,兩種方法起到的結果是一樣的。不過建議選擇在啟動主類上配置@MapperScan
,不然在每個 Mapper 類上加注解也麻煩,還容易漏加。
2.2 無配置文件注解版
2.2.1 配置
配置文件 application.yml 如下:
代碼清單:
mybatis:
type-aliases-package: com.springboot.springbootmybatisannotation.model
- 剩余部分和上面一致, mybatis 的配置僅需配置這一條足夠
2.2.2 Mapper 類
注解版的核心就是這個類,所有的 SQL 都在這個類里面,代碼如下:
代碼清單:
public interface UserMapper {
@Select("select * from user")
@Results({
@Result(property = "id", column = "id"),
@Result(property = "nickName", column = "nick_name"),
@Result(property = "age", column = "age"),
@Result(property = "createDate", column = "create_date")
})
List<User> getAll();
@Select("SELECT * FROM user WHERE id = #{id}")
@Results({
@Result(property = "nickName", column = "nick_name")
})
User getUser(String id);
@Insert("INSERT INTO user(id, nick_name, age, create_date) VALUES(#{id}, #{nickName}, #{age}, #{createDate})")
@SelectKey(keyProperty = "id", resultType = String.class, before = true, statement = "select uuid() as id from dual")
Long insertUser(User user);
@Update("UPDATE user SET nick_name = #{nickName}, age = #{age} WHERE create_date = #{createDate}")
Long updateUser(User user);
@Delete("DELETE FROM user WHERE id = #{id}")
Long deleteUser(String id);
}
- @Select 是查詢類的注解,所有的查詢均使用這個
- @Result 修飾返回的結果集,關聯實體類屬性和數據庫字段一一對應,如果實體類屬性和數據庫屬性名保持一致,就不需要這個屬性來修飾。
- @Insert 插入數據庫使用,直接傳入實體類會自動解析屬性到對應的值
- @Update 負責修改,也可以直接傳入對象
- @delete 負責刪除
注意:使用 # 符號和 $ 符號是不同的
#{}
使用 #{}
意味着使用的預編譯的語句,即在使用 jdbc 時的 preparedStatement , sql 語句中如果存在參數則會使用 ? 作占位符。
${}
使用 ${}
時的sql不會當做字符串處理,是什么就是什么,如上邊的語句:select * from table1 where id=${id} 在調用這個語句時控制台打印的為:select * from table1 where id=2 ,假設傳的參數值為2
從上邊的介紹可以看出這兩種方式的區別,最好是能用 #{}
則用它,可以防止 sql 注入,且是預編譯的,在需要原樣輸出時才使用 ${}
。
3. 小結
兩種模式各有特點,注解版適合簡單快速的模式,其實像現在流行的這種微服務模式,一個微服務就會對應一個自已的數據庫,多表連接查詢的需求會大大的降低,會越來越適合這種模式。另外插一句, Hibernate 對單表的支持是非常好的,為什么不選用 Spring Boot JPA + QueryDSL呢?
Xml 配置模式比較適合大型項目,可以酣暢淋漓的寫 SQL ,可以靈活的動態生成 SQL ,方便調整 SQL 。
4. 示例代碼
5. 參考
http://www.ityouknow.com/springboot/2016/11/06/spring-boot-mybatis.html