美文网首页Java
SpringBoot + MyBatisPlus基本使用

SpringBoot + MyBatisPlus基本使用

作者: 程序员小杰 | 来源:发表于2020-06-26 01:29 被阅读0次

    前言:
    mybatis在持久层框架中还是比较火的,一般项目都是基于ssm。虽然mybatis可以直接在xml中通过SQL语句操作数据库,很是灵活。但正其操作都要通过SQL语句进行,就必须写大量的xml文件,很是麻烦。mybatis-plus就很好的解决了这个问题。

    简介

    MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
    官网:https://mp.baomidou.com/guide

    整合

    1、创建表

    CREATE TABLE user (
        id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主键',
        user_name VARCHAR(30) DEFAULT NULL COMMENT '姓名',
        age INT(11) DEFAULT NULL COMMENT '年龄',
        email VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
        create_time DATETIME DEFAULT NULL COMMENT '创建时间',
            update_time DATETIME DEFAULT NULL COMMENT '修改时间'
    )  ENGINE=INNODB CHARSET=UTF8;
    

    初始化数据

    
    INSERT INTO user (id, user_name, age, email,create_time,update_time) VALUES
    (1, 'Jone', 18, 'test1@baomidou.com',now(),now()),
    (2, 'Jack', 20, 'test2@baomidou.com',now(),now()),
    (3, 'Tom', 28, 'test3@baomidou.com',now(),now()),
    (4, 'Sandy', 21, 'test4@baomidou.com',now(),now()),
    (5, 'Billie', 24, 'test5@baomidou.com',now(),now());
    

    now()函数获取当前时间

    2、创建springboot项目并导入依赖

    image.png

    修改mysql版本为5.1.47

      <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
                <scope>runtime</scope>
            </dependency>
    

    MyBatis-Plus依赖:

     <dependency>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-boot-starter</artifactId>
                    <version>3.2.0</version>
        </dependency>
    

    官网最新版本已经为: 3.3.2,使用Boot版本为2.2.4,不使用最新的,哈哈。

    3、配置

    1、在 application.yml 配置文件中添加数据库的相关配置:

    server:
      port: 1998
    spring:
      datasource:
        url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf-8
        username: root
        password: 980402
        driver-class-name: com.mysql.jdbc.Driver
    

    mysql5跟mysql8在连接的时候会有点不同:
    (1)、mysql 5 驱动不同 com.mysql.jdbc.Driver
    (2)、mysql 8 驱动不同com.mysql.cj.jdbc.Driver、需要增加时区的配置 serverTimezone=GMT+8

    4、编码

    1、编写实体类 User.java

    //生成getter,setter等函数
    @Data
    //生成全参数构造函数
    @AllArgsConstructor
    //生成无参构造函数
    @NoArgsConstructor
    public class User {
        private Long id;
        private String userName;
        private Integer age;
        private String email;
        private Date createTime;
        private Date updateTime;
    }
    

    2、编写Mapper接口

    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.gongj.mybatisplus.entity.User;
    //继承基本的类 BaseMapper
    public interface UserMapper extends BaseMapper<User> {
    }
    

    3、在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 所在目录

    @SpringBootApplication
    @MapperScan("com.gongj.mybatisplus.mapper")
    public class MybatisPlusApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MybatisPlusApplication.class, args);
        }
    
    }
    
    image.png

    5、测试

    5.1、测试查询全部

    @SpringBootTest
    class MybatisPlusApplicationTests {
    @Autowired
        UserMapper userMapper;
        @Test
        void testSelect(){
            // 参数是一个 Wrapper ,条件构造器,这里我们先不用 null 查询全部用户
            List<User> users = userMapper.selectList(null);
            users.forEach(System.out::println);
        }
    }
    
    image.png

    5.2、在yml配置sql日志

    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    

    再次查询


    image.png

    5.3测试添加

    @Test
        void testInsert() {
            User user = new User();
            user.setUserName("gongj");
            user.setEmail("19908488818@163.com");
            user.setAge(23);
            user.setCreateTime(new Date());
            user.setUpdateTime(new Date());
            int insert = userMapper.insert(user);//帮我们自动生成id
            System.out.println("id=" + user.getId());   //id会自动回填
        }
    
    image.png

    MyBatisPlus主键策略:

    public enum IdType {
        AUTO(0),  //数据库id自增
        NONE(1), // 未设置主键id
        INPUT(2),  // 用户输入id
        //以下三种只有当插入对象ID 为空,才自动填充。
        ID_WORKER(3),  // 默认的全局唯一id
        UUID(4),  //全局唯一uuid
        ID_WORKER_STR(5); //字符串全局唯一ID ID_WORKER的字符串表示方式
    }
    

    修改默认的主键策略
    在实体类字段上 加上注解:@TableId(type = xxx)

    @TableId(type = IdType.AUTO)
    private Long id;
    

    修改了主键策略为自增,数据库字段一定要是自增!


    image.png

    5.4测试自动填充

    在上面添加操作中我们是手动给创建时间、修改时间赋值,能不能自动化完成呢? MyBatisPlus提供了这样的配置。
    1、自定义实现类 MyMetaObjectHandler

    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
        // 插入时的填充策略
        @Override
        public void insertFill(MetaObject metaObject) {
            this.setFieldValByName("createTime",new Date(),metaObject);
            this.setFieldValByName("updateTime",new Date(),metaObject);
        }
    
        // 更新时的填充策略
        @Override
        public void updateFill(MetaObject metaObject) {
            this.setFieldValByName("updateTime",new Date(),metaObject);
        }
    }
    

    2、实体类字段属性增加注解

        @TableField(fill = FieldFill.INSERT)
        private Date createTime;
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private Date updateTime;
    

    填充类型:

    public enum FieldFill {
        /**
         * 默认不处理
         */
        DEFAULT,
        /**
         * 插入填充字段
         */
        INSERT,
        /**
         * 更新填充字段
         */
        UPDATE,
        /**
         * 插入和更新填充字段
         */
        INSERT_UPDATE
    }
    

    再次测试添加操作:

    @Test
        void testInsert() {
            User user = new User();
            user.setUserName("gongj2");
            user.setEmail("19908488818@163.com");
            user.setAge(223);
            int insert = userMapper.insert(user);//帮我们自动生成id
            System.out.println("id=" + user.getId());
        }
    
    image.png

    效果杠杠的。

    5.5修改测试

     @Test
        void testUpdate() {
            User user = new User();
            user.setId(1276042184872341506L);
            user.setUserName("ggggggggggg");
            user.setAge(555);
            int insert = userMapper.updateById(user);
            System.out.println("id=" + user.getId());
        }
    
    image.png

    所有的sql都是自动帮你动态配置的!也可以看到updateTime的自动填充也生效了。

    6、乐观锁

    当要更新一条记录的时候,希望这条记录没有被别人更新

    乐观锁实现方式:
    1、取出记录时,获取当前version
    2、更新时,带上这个version
    3、执行更新时, set version = newVersion where version = oldVersion
    4、如果version不对,就更新失败

    6.1数据库增加version字段!

    image.png

    6.2实体类增加version属性并加注解!

       @Version
        private Integer version;
    

    6.3插件配置

    @Configuration
    public class MybatisPlusConfig {
    
        /**
         * 乐观锁插件
         */
        @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor() {
            return new OptimisticLockerInterceptor();
        }
    }
    

    6.4、测试单线程修改

        @Test
        void testVserion(){
        // 1、查询用户信息
         User user = userMapper.selectById(1276056567094583298L);
         user.setAge(55);
         user.setUserName("yuanj");
         //user 就是前台传入的值
         userMapper.updateById(user);
        }
    
    image.png

    我们注意一下update的sql语句

     UPDATE user SET create_time=?, update_time=?, user_name=?, version=?, email=?, age=? WHERE id=? AND version=?
    

    version做为了修改条件。


    image.png

    特别说明:

    • 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
    • 整数类型下 newVersion = oldVersion + 1
    • newVersion 会回写到 entity
    • 仅支持 updateById(id)update(entity, wrapper) 方法
    • update(entity, wrapper) 方法下, wrapper 不能复用!!!

    7、查询

    7.1、根据id查询

    @Test
        void selectById(){
          
            userMapper.selectById(1276056567094583298L);
        }
    

    7.2、根据id批量查询

     @Test 
        public void selectBatchIds(){
            // 根据id批量查询
            List<User> users = userMapper.selectBatchIds(Arrays.asList(1276056567094583298L, 5, 3));
         }
    

    7.3、根据Map 自定义条件查询

     @Test
        public void selectByMap(){
            // 根据Map 自定义条件查询
            Map map = new HashMap();
            map.put("user_name","yuanj");
            List<User> users = userMapper.selectByMap(map);
        }
    

    8、分页查询

    1、配置拦截器组件

         /**
         * 分页插件
         */
        @Bean
        public PaginationInterceptor paginationInterceptor() {
            return new PaginationInterceptor();
        }
    

    2、编码

      @Test
        public void selectPage(){
            // 根据Wrapper 自定义条件查询
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_name","yuanj");
            Page<User> userPage = new Page<>();
            userPage.setCurrent(1L);  //当前是第几页 默认为1
            userPage.setSize(2);  //每页大小
            IPage<User> userIPage = userMapper.selectPage(userPage, null);
    
            System.out.println("当前页" + userIPage.getCurrent());  //当前页
            System.out.println("总页数" + userIPage.getPages()); //总页数
            System.out.println("返回数据" + userIPage.getRecords());  //返回数据
            System.out.println("每页大小" + userIPage.getSize());  //每页大小
            System.out.println("满足符合条件的条数" + userIPage.getTotal());  //满足符合条件的条数
            System.out.println("下一页" + userPage.hasNext());   //下一页
            System.out.println("上一页" + userPage.hasPrevious());  //上一页
        }
    

    9、删除

    删除分为物理删除和逻辑删除。

    1、物理删除

    1.1根据id物理删除

    @Test
        void deleteById(){
            //根据id物理删除
            int i = userMapper.deleteById(1L);
        }
    
    image.png

    1.2根据id物理批量删除

     @Test
        void deleteBatchIds(){
            //批量删除
            int i = userMapper.deleteBatchIds(Arrays.asList(2L, 4L));
            System.out.println(i);  //受影响的行数为2
        }
    

    2、逻辑删除

    2.1、在数据表中增加一个 deleted 字段

    image.png

    2.2、实体类中增加属性并加上注解

      @TableLogic
        private Integer deleted;
    

    2.3、测试

    再次执行删除操作

    @Test
        void deleteById(){
            //根据id删除
            int i = userMapper.deleteById(3L);
            System.out.println(i);
        }
    
    image.png

    可以看到sql是修改语句。


    image.png

    执行查询:

       @Test
        void testSelect(){
            // 参数是一个 Wrapper ,条件构造器,这里我们先不用 null 查询全部用户
            List<User> users = userMapper.selectList(null);
            users.forEach(System.out::println);
        }
    
    image.png

    可以看到id为3的数据已经查询不出来了。

    10、条件构造器

    1、查询创建时间在2020-6-15到2020-7-5并且邮箱不等于空并不等于null的数据

    @Test
        void select1(){
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            //查询创建时间在2020-6-15到2020-7-5并且邮箱不等于空并不等于null的数据
            queryWrapper.between("create_time","2020-06-01","2020-07-05").ne("email","").isNotNull("email");
            List<User> users = userMapper.selectList(queryWrapper);
            users.forEach((System.out::println));
        }
    
     Preparing: SELECT id,deleted,create_time,update_time,user_name,version,email,age FROM user WHERE deleted=0 AND (create_time BETWEEN ? AND ? AND email <> ? AND email IS NOT NULL)
    
    ==> Parameters: 2020-06-01(String), 2020-07-05(String), (String)
    <==    Columns: id, deleted, create_time, update_time, user_name, version, email, age
    <==        Row: 1, 0, 2020-06-15 23:05:52.0, 2020-06-25 23:05:52.0, Jone, 1, test1@baomidou.com, 18
    <==        Row: 5, 0, 2020-06-25 23:05:52.0, 2020-06-25 23:05:52.0, Billie, 1, test5@baomidou.com, 24
    <==        Row: 1276056567094583298, 0, 2020-06-25 15:36:08.0, 2020-06-25 15:53:16.0, yuanj, 2, 19908488818@163.com, 55
    <==      Total: 3
    

    2、模糊查询

     @Test
        void select2(){
            //模糊查询
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.likeLeft("user_name","J");   //左模糊  %J
            List<User> users = userMapper.selectList(queryWrapper);
            users.forEach((System.out::println));
        }
    

    3、子查询并根据id倒序

     @Test
        void select3(){
            //子查询并根据id倒序
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.inSql("id","select id from user where id < 4");
            queryWrapper.orderByDesc("id");
            List<User> users = userMapper.selectList(queryWrapper);
            users.forEach((System.out::println));
        }
    
     Preparing: SELECT id,deleted,create_time,update_time,user_name,version,email,age FROM user WHERE deleted=0 AND (id IN (select id from user where id < 4)) ORDER BY id DESC
    
    ==> Parameters: 
    <==    Columns: id, deleted, create_time, update_time, user_name, version, email, age
    <==        Row: 3, 0, 2020-09-11 23:05:52.0, 2020-06-25 23:05:52.0, Tom, 1, , 28
    <==        Row: 2, 0, 2025-06-25 23:05:52.0, 2020-06-25 23:05:52.0, Jack, 1, test2@baomidou.com, 20
    <==        Row: 1, 0, 2020-06-15 23:05:52.0, 2020-06-25 23:05:52.0, Jone, 1, test1@baomidou.com, 18
    <==      Total: 3
    

    11、关联查询

    1、新建一个card表

    CREATE TABLE card (
        cid BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主键',
        cname VARCHAR(30) DEFAULT NULL COMMENT '卡片名'
    )  ENGINE=INNODB CHARSET=UTF8;
    
    INSERT INTO card (cid,cname) VALUES
    (1,'健身卡'),
    (2,'KTV卡'),
    (3,'校园卡')
    

    2、在user表中创建cid列


    image.png

    并将user表和card表关联起来


    image.png
    3、编码
    3.1、card实体
    //生成getter,setter等函数
    @Data
    //生成全参数构造函数
    @AllArgsConstructor
    //生成无参构造函数
    @NoArgsConstructor
    public class Card {
        private Long cid;
        private String cname;
    }
    

    3.2、user实体
    加入cid和cards属性

     private Long cid;
    
    private List<Card> cards;
    

    3.3、UserMapper

     List<User> userJoinCardPage();
    

    3.4、UserMapper.xml

    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.gongj.mybatisplus.mapper.UserMapper">
    <resultMap id="userJoinCardPageMap" type="com.gongj.mybatisplus.entity.User">
    
        <id column="id" property="id"/>
        <result property="userName" column="user_name"></result>
        <result property="age" column="age"></result>
        <result property="createTime" column="create_time"></result>
        <result property="updateTime" column="update_time"></result>
        <result property="email" column="email"></result>
        <result property="email" column="email"></result>
        <result property="version" column="version"></result>
        <result property="deleted" column="deleted"></result>
        <result property="cid" column="cid"></result>
        <collection property="cards" column="cid" ofType="com.gongj.mybatisplus.entity.Card">
            <id column="cid" property="cid"></id>
            <result column="cname" property="cname"></result>
        </collection>
    </resultMap>
    
    <select id="userJoinCardPage" resultMap="userJoinCardPageMap">
    select u.id,u.user_name,u.email,u.age,u.version,u.create_time,u.update_time,u.deleted,u.cid,c.cid,c.cname from user u left join card c on u.cid = c.cid
    </select>
    </mapper>
    

    resultMap包含的元素:

    <resultMap id="唯一的标识" type="映射的pojo对象">
      <id column="表的主键字段" jdbcType="字段类型" property="映射pojo对象的主键属性" />
      <result column="表的一个字段" jdbcType="字段类型" property="映射到pojo对象的一个属性"/>
      <association property="pojo的一个对象属性" javaType="pojo关联的pojo对象">
        <id column="关联pojo对象对应表的主键字段" jdbcType="字段类型" property="关联pojo对象的主席属性"/>
        <result  column="任意表的字段" jdbcType="字段类型" property="关联pojo对象的属性"/>
      </association>
      <!-- 集合中的property须为oftype定义的pojo对象的属性-->
      <collection property="pojo的集合属性" ofType="集合中的pojo对象">
        <id column="集合中pojo对象对应的表的主键字段" jdbcType="字段类型" property="集合中pojo对象的主键属性" />
        <result column="可以为任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" />  
      </collection>
    </resultMap>
    

    如果collection标签是使用嵌套查询,格式如下:
    注意:<collection>标签中的column:要传递给select查询语句的参数,如果传递多个参数,格式为column= ” {参数名1=表字段1,参数名2=表字段2} ;

    <collection column="传递给嵌套查询语句的字段参数" property="pojo对象中集合属性" ofType="集合属性中的pojo对象" select="嵌套的查询语句" > 
    </collection>
    

    https://www.cnblogs.com/kenhome/p/7764398.html
    3.5、调用

     @Test
        void joinQuery(){
            List<User> users = userMapper.userJoinCardPage();
            users.forEach((System.out::println));
        }
    
    image.png

    可以看到就是一条关联查询sql,然后在resultMap中进行字段映射。
    3.6、collection的另外一种写法,也可以叫分布查询
    CardMapper

    public interface CardMapper extends BaseMapper<Card> {
    
        public Card selectById();
    }
    

    CardMapper.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.gongj.mybatisplus.mapper.CardMapper">
    
        <resultMap id="CardMap" type="com.gongj.mybatisplus.entity.Card">
            <id column="cid" property="cid"></id>
            <result column="cname" property="cname"></result>
        </resultMap>
    
        <select id="selectById" resultMap="CardMap">
            select * from card where cid = #{cid}
        </select>
    </mapper>
    

    userMapper

      List<User> userJoinCardPage2();
    

    UserMapper.xml

       <resultMap id="userJoinCardPageMap2" type="com.gongj.mybatisplus.entity.User">
    
            <id column="id" property="id"/>
            <result property="userName" column="user_name"></result>
            <result property="age" column="age"></result>
            <result property="createTime" column="create_time"></result>
            <result property="updateTime" column="update_time"></result>
            <result property="email" column="email"></result>
            <result property="email" column="email"></result>
            <result property="version" column="version"></result>
            <result property="deleted" column="deleted"></result>
            <result property="cid" column="cid"></result>
            <collection property="cards" column="cid" ofType="com.gongj.mybatisplus.entity.Card" select="com.gongj.mybatisplus.mapper.CardMapper.selectById">
    
            </collection>
        </resultMap>
    
        <select id="userJoinCardPage2" resultMap="userJoinCardPageMap2">
        select u.id,u.user_name,u.email,u.age,u.version,u.create_time,u.update_time,u.deleted,u.cid from user u
    </select>
    

    select="com.gongj.mybatisplus.mapper.CardMapper.selectById"使用内嵌查询。
    调用

       @Test
        void joinQuery(){
            List<User> users = userMapper.userJoinCardPage2();
            users.forEach((System.out::println));
        }
    
    image.png
    image.png

    11、关联查询并分页

    UserMapper

    List<User> userJoinCardPage2(Page page);
    

    测试

      @Test
        void joinQuery(){
            Page<User> userPage = new Page<>();
            userPage.setSize(1);
            userPage.setCurrent(1);
            List<User> users = userMapper.userJoinCardPage2(userPage);
            users.forEach((System.out::println));
        }
    
    image.png

    还是挺简单的,只需要传入一个Page对象就可以了。

    补充:
    我们发现如果去调用单表查询的结果,也就是自带的查询,就会出现异常。

     @Test
        void testSelect(){
            // 参数是一个 Wrapper ,条件构造器,这里我们先不用 null 查询全部用户
            List<User> users = userMapper.selectList(null);
            users.forEach(System.out::println);
        }
    
    image.png

    那怎么解决呢?在实体类的属性上加入注解,exist 默认为true。

    @TableField(exist = false)
        private List<Card> cards;
    

    注解加载bean属性上,表示当前属性不是数据库的字段,但在项目中必须使用,这样在新增等使用bean的时候,mybatis-plus就会忽略这个,不会报错。

    相关文章

      网友评论

        本文标题:SpringBoot + MyBatisPlus基本使用

        本文链接:https://www.haomeiwen.com/subject/wcfsfktx.html