1. 前言
ORM 框架的目的是简化编程中的数据库操作, 经过这么多年的发展, 基本上活到现在的就剩下两家了, 一个是宣称可以不用写 SQL 的 Hibernate , 一个是对 SQL 非常友好的 Mybaties ,, 两者各有特点, 在企业级系统开发中可以根据需求灵活使用. 发现一个有趣的现象: 传统企业大都喜欢使用 Hibernate , 互联网行业通常使用 Mybatis .
Hibernate 特点就是所有的 SQL 都用 Java 代码来生成, 不用跳出程序去写 (看) SQL , 有着编程的完整性, 发展到最顶端就是 Spring Data Jpa 这种模式了, 基本上根据方法名就可以生成对应的 SQL 了, 有不太了解的可以看笔者的前两篇文章 Spring Boot (三): ORM 框架 JPA 与连接池 Hikari https://www.geekdigging.com/2019/09/19/2405775053/ 和 Spring Boot (六): 为 JPA 插上翅膀的 QueryDSL https://www.geekdigging.com/2019/09/26/1814805575/ .
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.YAML 配置文件如下:
代码清单: spring-boot-mybatis/spring-boot-mybatis-xml/src/main/resources/application.YAML
- ***
- 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.YAML 如下:
代码清单:
- ***
- 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. 示例代码
示例代码 - GitHub
示例代码 - Gitee
5. 参考
http://www.ityouknow.com/springboot/2016/11/06/spring-boot-mybatis.html
来源: https://www.cnblogs.com/babycomeon/p/11610888.html