SpringBoot 2.x系列:整合Mybatis(下)

1,839 阅读8分钟

概览

上篇我们介绍Spring Boot是如何集成Mybatis来完成数据库的访问,实际项目开发过程中,常用的一共有4种组合,分别是:

  • MyBatis + XML
  • MyBatis + 注解
  • MyBatisPlus
  • TkMyBatis

上篇文章我们把前两种方式做了一个入门教程,如有需要可以参看:juejin.cn/post/692241…,接下来我们继续介绍MyBatisPlus和 TkMyBatis

MyBatis-Plus

作为一名Java开发工程师,想必对大名鼎鼎的MyBatis-Plus并不陌生,作者的理念是【为简化开发而生】,实际使用确实大大简化的Mybatis开发,提升了开发效率。官网地址: https://mybatis.plus/

实例代码对应的仓库地址:github.com/dragon8844/…

本小节,我们会使用 mybatis-plus-boot-starter 自动化配置 MyBatis-Plus 的配置。同时,演示如何使用 MyBatis-Plus 实现各种CRUD的操作。

引入依赖

<!-- 实现对数据库连接池的自动化配置 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 本示例,我们使用 MySQL -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<!-- 实现对 MyBatis Plus 的自动化配置 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.2.0</version>
</dependency>

<!-- 方便用单元测试验证-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

<!-- lombok简化代码-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

添加配置

  • 应用配置

    在resources目录下创建应用的配置文件application.yml,添加如下配置内容:`

    spring:
      # datasource 数据源配置内容
      datasource:
        url: jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: root
    
    # mybatis-plus 配置内容
    mybatis-plus:
      configuration:
        map-underscore-to-camel-case: true
      global-config:
        db-config:
          id-type: auto # ID 主键自增
          logic-delete-value: 1 # 逻辑已删除值(默认为 1)
          logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
      mapper-locations: classpath*:mapper/*.xml
      type-aliases-package: com.dragon.mybatisplus.entity
    
  • 配置类

    在config包下创建MybatisPlusConfig配置类,添加如下配置内容:

    /**
     * @author lilong
     */
    @MapperScan("com.dragon.mybatisplus.mapper")
    @Configuration
    public class MybatisPlusConfig {
    }
    

    因为这是个入门的demo,这里我们只需要配置的Mybatis的包扫描路径

编写代码

  • 编写实体类

    @Data
    @TableName(value = "user")
    public class User {
        /**
         * 主键
         */
        private Integer id;
        /**
         * 用户名
         */
        private String username;
        /**
         * 密码
         */
        private String password;
        /**
         * 创建时间
         */
        private Date createTime;
        /**
         * 是否删除
         */
        @TableLogic
        private Integer deleted;
    }
    

    实体类对应的DDL语句:

    CREATE TABLE `user` (
      `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
      `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用户名',
      `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',
      `create_time` datetime DEFAULT NULL COMMENT '创建时间',
      `deleted` tinyint(1) DEFAULT NULL COMMENT '是否删除  0-未删除;1-删除',
      PRIMARY KEY (`id`),
      UNIQUE KEY `idx_username` (`username`)
    ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
    
  • 编写Mapper类

    com.dragon.mybatisplus.mapper包下创建接口UserMapper,这个时候我们需要继承MyBatis-Plus的基础Mapper,

    com.baomidou.mybatisplus.core.mapper.BaseMapper接口,代码如下:

    /**
     * @author lilong
     */
    @Repository
    public interface UserMapper extends BaseMapper<User> {
        /**
         * 根据username查询
         *
         * @param username
         * @return
         */
        default User selectByUsername(@Param("username") String username) {
            LambdaQueryWrapper<User> wrapper = new QueryWrapper<User>().lambda();
            return selectOne(wrapper.eq(User::getUsername, username));
        }
    
    }
    
  • 对比下MyBatis + XML 这种方式,我们发现,通过继承com.baomidou.mybatisplus.core.mapper.BaseMapper接口,对数据库单表的CRUD操作MyBatis-Plus替我们自动完成了,分析源码如下:

    public interface BaseMapper<T> extends Mapper<T> {
        int insert(T entity);
    
        int deleteById(Serializable id);
    
        int deleteByMap(@Param("cm") Map<String, Object> columnMap);
    
        int delete(@Param("ew") Wrapper<T> wrapper);
    
        int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);
    
        int updateById(@Param("et") T entity);
    
        int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);
    
        T selectById(Serializable id);
    
        List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);
    
        List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);
    
        T selectOne(@Param("ew") Wrapper<T> queryWrapper);
    
        Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);
    
        List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);
    
        List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);
    
        List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);
    
        IPage<T> selectPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
    
        IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
    }
    

    通过查看源码,我们发现Mybatis-plus自动帮助我们生成的方法相当全面,一般来说,开发业务代码的时候,最耗时最枯燥的莫过于单表的CURD的操作,Mybatis-plus确实能大大简化我们开发。

  • 对于 #selectByUsername(@Param("username") String username) 方法,我们使用了 com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<T> 构造相对灵活的条件,这样一些动态 SQL 我们就无需在 XML 中编写。

    代码如下:

    default User selectByUsername(@Param("username") String username) {
            LambdaQueryWrapper<User> wrapper = new QueryWrapper<User>().lambda();
            return selectOne(wrapper.eq(User::getUsername, username));
        }
    

    QueryWrapper 已经提供好的非常多的拼接方法,具体的使用方式:《MyBatis-Plus 文档 —— 条件构造器》

单元测试

走一个单元测试,代码如下:

@SpringBootTest
@Slf4j
class UserMapperTest {

    @Resource
    UserMapper userMapper;

    @Test
    void insert() {
        User user = new User();
        user.setUsername("张三");
        user.setPassword("123456");
        user.setCreateTime(new Date());
        Integer count = userMapper.insert(user);
        log.info("count:{}", count);
    }

    @Test
    void selectById() {
        User user = userMapper.selectById(13);
        log.info("user:{}", user.toString());
    }

    @Test
    void selectByUsername() {
        User user = userMapper.selectByUsername("张三");
        log.info("user:{}", user.toString());
    }

    @Test
    void updateById() {
        User user = new User();
        user.setId(13);
        user.setUsername("李四");
        user.setPassword("111111");
        Integer count = userMapper.updateById(user);
        log.info("count:{}", count);
    }

    @Test
    void deleteById() {
        Integer count = userMapper.deleteById(13);
        log.info("count:{}", count);
    }
}

tkMybatis

tkMybatis我在实际工作的中使用的并不是很多,我基本上是从Mybatis + XML直接过度到MyBatis-Plus,中间也只有一个项目使用了tkMybatis, tk我理解的应该是toolkit的缩写,就是工具包的意思,主要是有两个开源的项目组合而来的

更多的资料可以参考官方:mybatis.io/

实例代码对应的仓库地址:github.com/dragon8844/…

本小节,我们会使用 tkmybatis 实现我们各种 CRUD 操作。

引入依赖

在pom.xml文件中,引入相关依赖,依赖稍微有点多 如下:

<!-- 实现对数据库连接池的自动化配置 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 本示例,我们使用 MySQL -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<!-- 实现对 MyBatis 的自动化配置 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
</dependency>

<!-- 实现对 Mapper 的自动化配置-->
<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>2.1.5</version>
</dependency>

<!-- 实现对 PageHelper 的自动化配置-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.5</version>
    <exclusions>
        <exclusion>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<!-- 方便用单元测试验证-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

<!-- lombok简化代码-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
  • 引入 mybatis-spring-boot-starter 依赖,自动化配置 MyBatis 。
  • 引入 mapper-spring-boot-starter 依赖,自动化配置 Mapper 。
  • 引入 pagehelper-spring-boot-starter 依赖,自动化配置 PageHelper 。

添加配置

  • 应用配置

    在resources目录下创建应用的配置文件application.yml,添加如下配置内容:

    spring:
      # datasource 数据源配置内容
      datasource:
        url: jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: root
    
    # mybatis 配置内容
    mybatis:
      config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径
      mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址
      type-aliases-package: com.dragon.tkmybatis.entity # 配置数据库实体包路径
    
    # mapper 配置内容
    mapper:
      not-empty: true # 在 INSERT 和 UPDATE 操作时,是否会判断字段是否为空。即 <if test='xxx != null' />
      identity: MYSQL
    
    # PageHelper 配置内容
    # 具体的参数作用,看 https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
    pagehelper:
      helperDialect: mysql # 分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置helperDialect属性来指定分页插件使用哪种方言。
      reasonable: true # 分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。
      supportMethodsArguments: true # 支持通过 Mapper 接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页
    
  • mybatis的配置

    在resources目录下创建mybatis-config.xml,并且添加如下配置:

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <settings>
            <!-- 使用驼峰命名法转换字段。 -->
            <setting name="mapUnderscoreToCamelCase" value="true"/>
        </settings>
        <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>
    
  • 在config包下创建MyBatisConfig配置类,添加如下配置内容:

    /**
     * @author lilong
     */
    @MapperScan(basePackages = "com.dragon.tkmybatis.mapper")
    @Configuration
    public class MybatisConfig {
    }
    

    来用指定Mybatis的扫描包的基本包路径

编写代码

  • 编写实体类

    @Data
    @Table(name = "user")
    public class User {
        /**
         * 主键
         */
        // 表示该字段为主键 ID
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "JDBC")
        private Integer id;
        /**
         * 用户名
         */
        private String username;
        /**
         * 密码
         */
        private String password;
        /**
         * 创建时间
         */
        private Date createTime;
    
    }
    

    实体类对应的DDL语句:

    CREATE TABLE `user` (
      `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
      `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用户名',
      `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',
      `create_time` datetime DEFAULT NULL COMMENT '创建时间',
      PRIMARY KEY (`id`),
      UNIQUE KEY `idx_username` (`username`)
    ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
    
  • 编写Mapper类

    com.dragon.tkmybatis.mapper包下创建接口UserMapper,同理和MyBatis-Plus一样,这个时候我们需要继承tkMybatis的基础Mapper,

    tk.mybatis.mapper.common.Mapper接口,代码如下:

    /**
     * @author lilong
     */
    @Repository
    public interface UserMapper extends Mapper<User> {
    
        /**
         * 根据username查询
         *
         * @param username
         * @return
         */
        default User selectByUsername(@Param("username") String username){
            Example example = new Example(User.class);
            // 创建 Criteria 对象,设置 username 查询条件
            example.createCriteria().andEqualTo("username", username);
            // 执行查询
            return selectOneByExample(example);
        }
    
        /**
         *  根据创建时间查询
         * @param createTime
         * @return
         */
        default List<User> selectByCreateTime(@Param("createTime") Date createTime) {
            Example example = new Example(User.class);
            // 创建 Criteria 对象,设置 create_time 查询条件
            example.createCriteria().andGreaterThan("createTime", createTime);
            return selectByExample(example);
        }
    }
    
    • 在 MyBatis-Plus 中,我们使用 QueryWrapper 拼装动态 SQL 。而在 tkmybatis 中,通过使用它提供的 Example 和 Criteria 类。

    • 关于分页,PageHelper 的使用方式,在单元测试中会有体现

单元测试

@SpringBootTest
@Slf4j
class UserMapperTest {

    @Resource
    UserMapper userMapper;

    @Test
    void insert() {
        User user = new User();
        user.setUsername("张三");
        user.setPassword("123456");
        user.setCreateTime(new Date());
        Integer count = userMapper.insert(user);
        log.info("count:{}", count);
    }

    @Test
    void selectByPrimaryKey() {
        User user = userMapper.selectByPrimaryKey(14);
        log.info("user:{}", user.toString());
    }

    @Test
    void selectByUsername() {
        User user = userMapper.selectByUsername("李四");
        log.info("user:{}", user.toString());
    }

    @Test
    void updateByPrimaryKey() {
        User user = new User();
        user.setId(14);
        user.setUsername("李四");
        user.setPassword("111111");
        Integer count = userMapper.updateByPrimaryKey(user);
        log.info("count:{}", count);
    }

    @Test
    void deleteByPrimaryKey() {
        Integer count = userMapper.deleteByPrimaryKey(14);
        log.info("count:{}", count);
    }

    @Test // 更多使用,可以参考 https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
    public void testSelectPageByCreateTime() {
        // 设置分页
        PageHelper.startPage(1, 10);
        Date createTime = new Date(2018 - 1990, Calendar.FEBRUARY, 24); // 临时 Demo ,实际不建议这么写
        // 执行列表查询
        // PageHelper 会自动发起分页的数量查询,设置到 PageHelper 中
        List<User> users = userMapper.selectByCreateTime(createTime); // 实际返回的是 com.github.pagehelper.Page 代理对象
        // 转换成 PageInfo 对象,并输出分页
        PageInfo<User> page = new PageInfo<>(users);
        System.out.println(page.getTotal());
    }
}

分页逻辑,在 #testSelectPageByCreateTime() 方法中,我们已经进行实现。

小结

至此SpringBoot整合Mybaits入门的教程已完结,我们来回忆下,整合Mybatis一共有4中组合方式,分别是:

  • MyBatis + XML

  • MyBatis + 注解

  • MyBatisPlus

  • TkMyBatis

XML方式比较原始,需要手写大量的CRUD的SQL,开发效率不太高,需要手写大量的SQL。

注解这种方式虽然说可以省略xml文件的编写,不过把SQL写到java代码中,代码的维护性、可读性比较差,同样避免不了手写大量的SQL。

推荐是用MyBatisPlus,对于单表的CRUD可以自动帮助生成,官方的文档也是比较系统和全面的。

至于TkMyBatis,定位是MyBatis的工具箱,是有通用Mapper和Mybatis-PageHelper两个开源项目组成的,同样可以提供MyBatisPlus相同的功能,官方的文档目前稍微欠缺。

最后说一句

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下,您的支持是我坚持写作最大的动力,多谢支持。

此外,关注公众号:黑色的灯塔,专注Java后端技术分享,涵盖Spring,Spring Boot,SpringCloud,Docker,Kubernetes中间件等技术。