mybatis-plus-sgg-19-39

通用Service应用 #

  • 这里会出现 publicKey is now allowed ,在数据库连接语句后面加上这句话即可 allowPublicKeyRetrieval=true

    spring:
      #配置数据源
      datasource:
        #配置数据源类型
        type: com.zaxxer.hikari.HikariDataSource
        #配置数据源各个信息
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&&useSSL=false&&allowPublicKeyRetrieval=true
        username: root
        password: 123456
    
  • 查询

        @Test
        public void testList(){
            //List<User> list = userService.list();
            long count = userService.count();
            System.out.println("总条数:"+count);
        }
    

    SQL执行语句

    ==>  Preparing: SELECT COUNT( * ) FROM user
    ==> Parameters: 
    <==    Columns: COUNT( * )
    <==        Row: 5
    <==      Total: 1
    
  • 批量添加

        @Test
        public void batchInsert(){
            List<User> users=new ArrayList<>();
            for(int i=0;i<10;i++){
                User user=new User();
                user.setName("name"+i);
                user.setEmail("email"+i);
                users.add(user);
            }
    
            boolean b = userService.saveBatch(users);
            System.out.println("result:"+b);
        }
    

    sql日志输出

    ==>  Preparing: INSERT INTO user ( id, name, email ) VALUES ( ?, ?, ? )
    ==> Parameters: 1532579686881243138(Long), name0(String), email0(String)
    ==> Parameters: 1532579687124512770(Long), name1(String), email1(String)
    ==> Parameters: 1532579687128707074(Long), name2(String), email2(String)
    ==> Parameters: 1532579687128707075(Long), name3(String), email3(String)
    ==> Parameters: 1532579687132901377(Long), name4(String), email4(String)
    ==> Parameters: 1532579687137095681(Long), name5(String), email5(String)
    ==> Parameters: 1532579687137095682(Long), name6(String), email6(String)
    ==> Parameters: 1532579687141289985(Long), name7(String), email7(String)
    ==> Parameters: 1532579687145484289(Long), name8(String), email8(String)
    ==> Parameters: 1532579687145484290(Long), name9(String), email9(String)
    result:true
    

    注意,这里是一个个的insert into ,而不是一条(单个的sql语句进行循环添加)

MyBatis-Plus常用注解1 #

  • 现在将mysql数据库表user名改为t_user 会提示下面的报错

    Cause: java.sql.BatchUpdateException: Table 'mybatis_plus.user' doesn't exist
    

    说明mybatis plus查询的时候会去找实体类名一样的表

  • 使用@TableName(“t_user”) 设置实体类对应的表名

    
    @Data
    @TableName("t_user")
    public class User {
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }
    
    • 修改后执行成功
  • 统一添加

    mybatis-plus:
      configuration:
      global-config:
        db-config:
          table-prefix: t_
    
  • 指定主键名 假设现在把数据库列名和bean的属性名id改为uid,此时新增一条记录

    Field 'uid' doesn't have a default value
    ; Field 'uid' doesn't have a default value; nested exception is java.sql.SQLException: Field 'uid' doesn't have a default value
    
    • 说明此时没有为uid赋值
  • 使用@TableId告诉mybatis-plus那个字段为主键,让mybatis-plus为他赋默认值

    @Data
    public class User {
        @TableId
        private Long uid;
        private String name;
        private Integer age;
        private String email;
    }
    

    sql打印

    ==>  Preparing: INSERT INTO t_user ( uid, name, age ) VALUES ( ?, ?, ? )
    ==> Parameters: 1532582462671618050(Long), 张三(String), 18(Integer)
    <==    Updates: 1
    

@TableId的value属性 #

  • 用于指定绑定的主键的字段 假设此时将bean的主键属性名为id,数据库主键名是uid

  • 此时运行,会提示

    ### SQL: INSERT INTO t_user  ( id, name, age )  VALUES  ( ?, ?, ? )
    ### Cause: java.sql.SQLSyntaxErrorException: Unknown column 'id' in 'field list'
    

    他会拿bean的属性来生成sql语句

  • 加上@TableId(value=“uid”)后运行正常

@TableId的value属性 #

  • 
    /**
     * 生成ID类型枚举类
     *
     * @author hubin
     * @since 2015-11-10
     */
    @Getter
    public enum IdType {
        /**
         * 数据库ID自增
         * <p>该类型请确保数据库设置了 ID自增 否则无效</p>
         */
        AUTO(0),
        /**
         * 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
         */
        NONE(1),
        /**
         * 用户输入ID
         * <p>该类型可以通过自己注册自动填充插件进行填充</p>
         */
        INPUT(2),
    
        /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
        /**
         * 分配ID (主键类型为number或string),
         * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
         *
         * @since 3.3.0
         */
        ASSIGN_ID(3),
        /**
         * 分配UUID (主键类型为 string)
         * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
         */
        ASSIGN_UUID(4);
    
        private final int key;
    
        IdType(int key) {
            this.key = key;
        }
    }
    
  • //使用自增
    @TableId(value="uid",type = IdType.AUTO )
        private Long id;
    
  • 然后将数据库主键设置为自动递增

  • 新增后id为6

通过全局属性设置主键生成策略 #

  • 全局配置设置

    mybatis-plus: 
      global-config:
        db-config: 
          id-type: auto
    

雪花算法 #

  • 数据库扩展方式:主从复制、业务分库、数据库分表
  • 数据库拆分:水平拆分、垂直拆分
  • 水平分表相对垂直分表,会引入更多的复杂性,比如要求唯一的数据id该怎么处理
    • 可以给每个分表都给定一个范围大小,但是这样分段大小不好取
    • 可以取模,但是如果增加了机器,原来的值主键(怎么处理是个问题
    • 雪花算法,由Twitter公布的分布式主键生成算法 能够保证不同表的主键的不重复性,以及相同表的主键的有序性
    • 核心思想 ly-20241212142150355

MyBatis-Plus常用注解2 #

  • 此时数据库字段名为name,如果现在实体类的名字改为userName,那么会报错

    INSERT INTO t_user  ( user_name, age )  VALUES  ( ?, ? )
    

    又一次证明了MyBatis-plus通过实体类属性猜测数据库表的相关字段

    • 使用@TableFiled来指定对应的字段名

          @TableField(value = "name")
          private String userName;
      
    • 查询

      • 代码

            @Test
            public void selectTest() {
                User user = userService.getById(5L);
                System.out.println("结果:" + user);
            }
        
      • sql执行语句

        ==>  Preparing: SELECT uid AS id,name AS userName,age,email,is_deleted_ly FROM t_user WHERE uid=? AND is_deleted_ly=0
        ==> Parameters: 5(Long)
        <==    Columns: id, userName, age, email, is_deleted_ly
        <==        Row: 5, Billie, 24, email被修改了, 0
        <==      Total: 1
        Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5e048149]
        结果:User(id=5, userName=Billie, age=24, email=email被修改了, isDeletedLy=0)
        
  • 逻辑删除(主要是允许数据的恢复) 这里增加一个isDeletedLy字段(这里为了测试,一般是isDeleted)

    ly-20241212142150529

    • 在User类添加下面的字段

      @TableLogic
      private Integer isDeletedLy;
      
    • 逻辑删除

      • 代码

            @Test
            public void deleteLogic() {
                boolean save = userService.removeBatchByIds(Arrays.asList(1L,2L,3L));
                System.out.println("结果:" + save);
            }
        

        sql执行语句 注意,这里使用了is_deleted_ly=0是因为在下面的步骤加入了逻辑删除注解

        ==>  Preparing: UPDATE t_user SET is_deleted_ly=1 WHERE uid=? AND is_deleted_ly=0
        ==> Parameters: 1(Long)
        ==> Parameters: 2(Long)
        ==> Parameters: 3(Long)
        
      • 结果 ly-20241212142150582

条件构造器 #

  • 结构 ly-20241212142150634

    • 解释 ly-20241212142150684
  • 查看BaseWrapper源码

    
    /**
     * Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
     * <p>这个 Mapper 支持 id 泛型</p>
     *
     * @author hubin
     * @since 2016-01-23
     */
    public interface BaseMapper<T> extends Mapper<T> {
    
        /**
         * 插入一条记录
         *
         * @param entity 实体对象
         */
        int insert(T entity);
    
        /**
         * 根据 ID 删除
         *
         * @param id 主键ID
         */
        int deleteById(Serializable id);
    
        /**
         * 根据实体(ID)删除
         *
         * @param entity 实体对象
         * @since 3.4.4
         */
        int deleteById(T entity);
    
        /**
         * 根据 columnMap 条件,删除记录
         *
         * @param columnMap 表字段 map 对象
         */
        int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
    
        /**
         * 根据 entity 条件,删除记录
         *
         * @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
         */
        int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
        /**
         * 删除(根据ID或实体 批量删除)
         *
         * @param idList 主键ID列表或实体列表(不能为 null 以及 empty)
         */
        int deleteBatchIds(@Param(Constants.COLLECTION) Collection<?> idList);
    
        /**
         * 根据 ID 修改
         *
         * @param entity 实体对象
         */
        int updateById(@Param(Constants.ENTITY) T entity);
    
        /**
         * 根据 whereEntity 条件,更新记录
         *
         * @param entity        实体对象 (set 条件值,可以为 null)
         * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
         */
        int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
    
        /**
         * 根据 ID 查询
         *
         * @param id 主键ID
         */
        T selectById(Serializable id);
    
        /**
         * 查询(根据ID 批量查询)
         *
         * @param idList 主键ID列表(不能为 null 以及 empty)
         */
        List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
    
        /**
         * 查询(根据 columnMap 条件)
         *
         * @param columnMap 表字段 map 对象
         */
        List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
    
        /**
         * 根据 entity 条件,查询一条记录
         * <p>查询一条记录,例如 qw.last("limit 1") 限制取一条记录, 注意:多条数据会报异常</p>
         *
         * @param queryWrapper 实体对象封装操作类(可以为 null)
         */
        default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {
            List<T> ts = this.selectList(queryWrapper);
            if (CollectionUtils.isNotEmpty(ts)) {
                if (ts.size() != 1) {
                    throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records");
                }
                return ts.get(0);
            }
            return null;
        }
    
        /**
         * 根据 Wrapper 条件,判断是否存在记录
         *
         * @param queryWrapper 实体对象封装操作类
         * @return
         */
        default boolean exists(Wrapper<T> queryWrapper) {
            Long count = this.selectCount(queryWrapper);
            return null != count && count > 0;
        }
    
        /**
         * 根据 Wrapper 条件,查询总记录数
         *
         * @param queryWrapper 实体对象封装操作类(可以为 null)
         */
        Long selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
        /**
         * 根据 entity 条件,查询全部记录
         *
         * @param queryWrapper 实体对象封装操作类(可以为 null)
         */
        List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
        /**
         * 根据 Wrapper 条件,查询全部记录
         *
         * @param queryWrapper 实体对象封装操作类(可以为 null)
         */
        List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
        /**
         * 根据 Wrapper 条件,查询全部记录
         * <p>注意: 只返回第一个字段的值</p>
         *
         * @param queryWrapper 实体对象封装操作类(可以为 null)
         */
        List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
        /**
         * 根据 entity 条件,查询全部记录(并翻页)
         *
         * @param page         分页查询条件(可以为 RowBounds.DEFAULT)
         * @param queryWrapper 实体对象封装操作类(可以为 null)
         */
        <P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
        /**
         * 根据 Wrapper 条件,查询全部记录(并翻页)
         *
         * @param page         分页查询条件
         * @param queryWrapper 实体对象封装操作类
         */
        <P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    }
    
  • Wrapper条件组装 ly-20241212142150737

    • queryWrapper测试

          @Test
          public void test01() {
              QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
              //链式结构调用
              userQueryWrapper.like("name", "a")
                      .between("age", 10, 30)
                      .isNotNull("email");
              List<User> users = userMapper.selectList(userQueryWrapper);
              users.forEach(System.out::println);
      
          }
      
      • sql日志打印

        //注意,这里出现了逻辑删除条件
        ==>  Preparing: SELECT uid AS id,name AS userName,age,email,is_deleted_ly FROM t_user WHERE is_deleted_ly=0 AND (name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
        ==> Parameters: %a%(String), 10(Integer), 30(Integer)
        <==    Columns: id, userName, age, email, is_deleted_ly
        <==        Row: 4, Sandy, 21, test4@baomidou.com, 0
        <==        Row: 5, Billiea, 24, email被修改了, 0
        <==      Total: 2
        Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@19650aa6]
        User(id=4, userName=Sandy, age=21, email=test4@baomidou.com, isDeletedLy=0)
        User(id=5, userName=Billiea, age=24, email=email被修改了, isDeletedLy=0)
        
    • 使用排序

          @Test
          public void test02() {
              QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
               userQueryWrapper.orderByDesc("age")
                       .orderByAsc("uid");
              List<User> users = userMapper.selectList(userQueryWrapper);
              users.forEach(System.out::println);
      
          }
      
      • sql日志打印

        ==>  Preparing: SELECT uid AS id,name AS userName,age,email,is_deleted_ly FROM t_user WHERE is_deleted_ly=0 ORDER BY age DESC,uid ASC
        ==> Parameters: 
        <==    Columns: id, userName, age, email, is_deleted_ly
        <==        Row: 7, 张三6, 38, test6@baomidou.com, 0
        <==        Row: 5, Billiea, 24, email被修改了, 0
        <==        Row: 4, Sandy, 21, test4@baomidou.com, 0
        <==        Row: 6, 张三5, 18, test5@baomidou.com, 0
        <==        Row: 8, 张三a, 18, null, 0
        <==      Total: 5
        Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7158daf2]
        User(id=7, userName=张三6, age=38, email=test6@baomidou.com, isDeletedLy=0)
        User(id=5, userName=Billiea, age=24, email=email被修改了, isDeletedLy=0)
        User(id=4, userName=Sandy, age=21, email=test4@baomidou.com, isDeletedLy=0)
        User(id=6, userName=张三5, age=18, email=test5@baomidou.com, isDeletedLy=0)
        User(id=8, userName=张三a, age=18, email=null, isDeletedLy=0)
        
    • 条件逻辑删除

      • 代码

            @Test
            public void test03() {
                QueryWrapper<User> userQueryWrapper=new QueryWrapper<>();
                userQueryWrapper.isNull("email");
                int deleted = userMapper.delete(userQueryWrapper);
                System.out.println(deleted);
            }
        

        sql日志输出

        ==>  Preparing: UPDATE t_user SET is_deleted_ly=1 WHERE is_deleted_ly=0 AND (email IS NULL)
        ==> Parameters: 
        <==    Updates: 1
        
    • 修改

          @Test
          public void test04() {
              QueryWrapper<User> userQueryWrapper=new QueryWrapper<>();
              //(age>23且用户名包含a)  或 (邮箱为null)
              userQueryWrapper.gt("age",23)
                      .like("name","a")
                      .or()
                      .isNull("email");
      
              User user=new User();
              user.setUserName("被修改了");
              int deleted = userMapper.update(user,userQueryWrapper);
              System.out.println(deleted);
          }
      

      sql日志打印

      ==>  Preparing: UPDATE t_user SET name=? WHERE is_deleted_ly=0 AND (age > ? AND name LIKE ? OR email IS NULL)
      ==> Parameters: 被修改了(String), 23(Integer), %a%(String)
      <==    Updates: 1
      
    • 条件优先级

          @Test
          public void test05() {
              QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
              //(age>23且用户名包含a)  或 (邮箱为null)
              userQueryWrapper
                      .like("name", "a")
                      //and里面是一个条件构造器
                      .and(
                              userQueryWrapper1 ->
                                      userQueryWrapper1.gt("age", 20)
                                              .or()
                                              .isNull("email")
                      );
      
              User user = new User();
              user.setUserName("被修改了");
              int deleted = userMapper.update(user, userQueryWrapper);
              System.out.println(deleted);
          }
      

      sql日志输出

      ==>  Preparing: UPDATE t_user SET name=? WHERE is_deleted_ly=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
      ==> Parameters: 被修改了(String), %a%(String), 20(Integer)
      <==    Updates: 1
      
    • 注意 or也有优先级的参数 ly-20241212142150787

  • 只查询某些字段

        @Test
        public void test06() {
            QueryWrapper<User> userQueryWrapper
                    =new QueryWrapper<>();
            userQueryWrapper.select("uid","name");
            List<Map<String, Object>> maps = userMapper.selectMaps(userQueryWrapper);
            System.out.println(maps);
        }
    

    sql输出

    ==>  Preparing: SELECT uid,name FROM t_user WHERE is_deleted_ly=0
    ==> Parameters: 
    <==    Columns: uid, name
    <==        Row: 4, 被修改了
    <==        Row: 5, 被修改了
    <==        Row: 6, 张三5
    <==        Row: 7, 张三6
    <==      Total: 4
    
  • 子查询 假设需要完整下面的sql查询 ly-20241212142150835

    • 代码

          @Test
          public void test7(){
              //查询id小于等于100
              QueryWrapper<User>
                      userQueryWrapper=new QueryWrapper<>();
              userQueryWrapper.inSql("uid",
                      "select uid from t_user where uid <= 100");
              List<User> users = userMapper.selectList(userQueryWrapper);
              users.forEach(System.out::println);
          }
      

      sql输出

      ==>  Preparing: SELECT uid AS id,name AS userName,age,email,is_deleted_ly FROM t_user WHERE is_deleted_ly=0 AND (uid IN (select uid from t_user where uid <= 100))
      ==> Parameters: 
      <==    Columns: id, userName, age, email, is_deleted_ly
      <==        Row: 4, 被修改了, 21, test4@baomidou.com, 0
      <==        Row: 5, 被修改了, 24, email被修改了, 0
      <==        Row: 6, 张三5, 18, test5@baomidou.com, 0
      <==        Row: 7, 张三6, 38, test6@baomidou.com, 0
      <==      Total: 4
      
  • UpdateWrapper

        @Test
        public void test8(){
            //(age>23且用户名包含a)  或 (邮箱为null)
            UpdateWrapper<User> updateWrapper=new UpdateWrapper<>();
            updateWrapper.like("name","a")
                    .and(userUpdateWrapper ->
                            userUpdateWrapper.gt("age",23).or().isNotNull("email"));
            updateWrapper.set("name","小黑").set("email","abc@ly.com");
            userMapper.update(null,updateWrapper);
    
        }
    

    sql日志输出

    ==>  Preparing: UPDATE t_user SET name=?,email=? WHERE is_deleted_ly=0 AND (name LIKE ? AND (age > ? OR email IS NOT NULL))
    ==> Parameters: 小黑(String), abc@ly.com(String), %a%(String), 23(Integer)
    <==    Updates: 0
    
  • 模拟用户操作组装条件

        @Test
        public void test9(){
            String username="";
            Integer ageBegin=null;
            Integer ageEnd=30;
            QueryWrapper<User> queryWrapper=new QueryWrapper<>();
            if(StringUtils.isNotBlank(username)){
                queryWrapper.like("user_name",username);
            }
            if( ageBegin!=null){
                queryWrapper.gt("age",ageBegin);
            }
            if( ageEnd!=null){
                queryWrapper.le("age",ageEnd);
            }
            userMapper.selectList(queryWrapper);
        }
    

    sql日志打印

    ==>  Preparing: SELECT uid AS id,name AS userName,age,email,is_deleted_ly FROM t_user WHERE is_deleted_ly=0 AND (age <= ?)
    ==> Parameters: 30(Integer)
    <==    Columns: id, userName, age, email, is_deleted_ly
    <==        Row: 4, 被修改了, 21, test4@baomidou.com, 0
    <==        Row: 5, 被修改了, 24, email被修改了, 0
    <==        Row: 6, 张三5, 18, test5@baomidou.com, 0
    <==      Total: 3
    
  • 使用condition处理条件

    
        @Test
        public void test10(){
    
            String username="abc";
            Integer ageBegin=null;
            Integer ageEnd=30;
            QueryWrapper<User> queryWrapper=new QueryWrapper<>();
            queryWrapper.like(StringUtils.isNotBlank(username),"name",username)
                    .ge(ageBegin!=null,"age",ageBegin);
            userMapper.selectList(queryWrapper);
        }
    

    sql日志输出

    ==>  Preparing: SELECT uid AS id,name AS userName,age,email,is_deleted_ly FROM t_user WHERE is_deleted_ly=0 AND (name LIKE ?)
    ==> Parameters: %abc%(String)
    <==      Total: 0