MyBatisPlus常用功能总结!(附项目示例)

这篇主要是总结一下MybatisPlus一些常用的场景,目前主要有以下几点:

  • 完整的CURD操作示例
  • 逻辑删除功能示例
  • 自动填充功能示例
  • 分页插件功能示例

有关一些其它重要的功能比如 条件生成器主键策略通用枚举多数据源乐观锁多租户等功能可以看官方文档,官方文档已经写的很清楚了,而且每个功能点都有对应的项目示例。

示例准备

1、用户表

CREATE TABLE `user` (
  `id` int unsigned  AUTO_INCREMENT COMMENT '主键',
  `username` varchar(128)  COMMENT '用户名',
  `phone` varchar(32)  COMMENT '手机号',
  `sex` char(1)  COMMENT '性别',
  `create_time` datetime  COMMENT '创建时间',
  `update_time` datetime  COMMENT '更新时间',
  `deleted` tinyint DEFAULT '0' COMMENT '1、删除 0、未删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 

2、创建对应实体

@Data
@Accessors(chain = true)
@TableName("user")
public class UserDO implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 用户名
     */
    @TableField("username")
    private String username;

    /**
     * 手机号
     */
    @TableField("phone")
    private String phone;

    /**
     * 性别
     */
    @TableField("sex")
    private String sex;

    /**
     * 创建时间
     */
    @TableField("create_time")
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    @TableField("update_time")
    private LocalDateTime updateTime;

    /**
     * 1、删除 0、未删除
     */
    @TableField("deleted")
    private Integer deleted;
}

3、创建Mapper

@Mapper
public interface UserMapper extends BaseMapper<UserDO> {

}

其它有关代码这里就不粘贴了,具体看项目源码就可以了。


一、完整的CURD操作

public class UserServiceTest extends Base {

   @Autowired
   private UserMapper mapper;
   
    @Test
    public void insert() {
        UserDO user = new UserDO();
        user.setUsername("小小");
        user.setPhone("18812345678");
        user.setSex("女");
        Assertions.assertThat(mapper.insert(user)).isGreaterThan(0);
        // 成功直接拿回写的 ID
        Assertions.assertThat(user.getId()).isNotNull();
    }

    @Test
    public void delete() {
        mapper.deleteById(6);
        mapper.delete(new LambdaQueryWrapper<UserDO>().eq(UserDO::getUsername, "张三"));
    }
    
    @Test
    public void update() {
        //方式一: 根据id更新
        mapper.updateById(new UserDO().setId(1).setPhone("13312345678"));
        //方式二: 左边是需要更新的值 右边是where条件
        mapper.update(
                new UserDO().setDeleted(1), new LambdaQueryWrapper<UserDO>().eq(UserDO::getPhone, "18812345678")
        );
        //方式三:不创建User对象
        mapper.update(null,new LambdaUpdateWrapper<UserDO>()
                .set(UserDO::getPhone,"13111111111").set(UserDO::getCreateTime, LocalDateTime.now()).eq(UserDO::getUsername,"小小"));
    }

    @Test
    public void select() {
         //1、根据主键获取
         UserDO userDO = mapper.selectById(1);
         //2、根据手机号获取 单个
         UserDO userDO1 = mapper.selectOne(new LambdaQueryWrapper<UserDO>().eq(UserDO::getPhone, "13312345678"));
         //3、获取集合
         List<UserDO> userDOS = mapper.selectList(new LambdaQueryWrapper<UserDO>().eq(UserDO::getPhone, "13312345678"));
         List<UserDO> userDOS1 = mapper.selectList(null);
    }
    
    @Test
    public void orderBy() {
        //1、单个排序
        List<UserDO> users = mapper.selectList(Wrappers.<UserDO>query().orderByAsc("create_time"));
        //2、多字段排序
        List<UserDO> users2 = mapper.selectList(Wrappers.<UserDO>query().orderByAsc(Lists.newArrayList("create_time","phone")));
        //3、先按手机号升序排列,phone相同再按create_time降序排列
        List<UserDO> users3 = mapper.selectList(Wrappers.<UserDO>query().orderByAsc("phone").orderByDesc("create_time"));
        //4、Lambda实现方式,和3实现的效果是一样的。
        List<UserDO> users4 = mapper.selectList(new LambdaQueryWrapper<UserDO>().orderByAsc(UserDO::getPhone).orderByDesc(UserDO::getCreateTime));
    }

    @Test
    public void groupBy() {
        QueryWrapper<UserDO> wrapper = new QueryWrapper<>();
        wrapper.select("phone, count(*) as total")
                .groupBy("phone");
        //注意要用 listMaps ,返回的是 Map<String,Object>
        List<Map<String, Object>> maplist = mapper.selectMaps(wrapper);
    }
    
    @Test
    public void testSelectMaxId() {
        QueryWrapper<UserDO> wrapper = new QueryWrapper<>();
        wrapper.select("max(id) as id");
        UserDO user = mapper.selectOne(wrapper);
    }
}

二、逻辑删除功能

1、使用场景

这个场景是这样的,因为我们在设计表结构的时候都会有一个逻辑删除字段,比如上表中就有一个deleted字段,1=删除 0=未删除。

那我们在查询或者更新操作时,sql都会带上这个字段。

例如:

-- 更新
update user set sex='女' where id = 1 and deleted=0
-- 查询
select id,sex,username from user where deleted=0

既然都要带上,那是不是可以做成全局的,不用我们每个sql都手动添加deleted=0,这个条件。

2、使用方法

1、配置

例如: application.yml

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted # 全局逻辑删除的实体字段名()
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

2、实体类字段上加上@TableLogic注解

    @TableLogic
    private Integer deleted;

注意:since 3.3.0,配置后可以忽略不配置这步骤

3、示例

public class UserServiceDeleteTest extends Base {

   @Autowired
   private UserMapper mapper;
   
    @Test
    public void update() {
        //方式一: 根据id更新
        mapper.updateById(new UserDO().setId(1).setPhone("13312345678"));
        //实际执行sql: UPDATE user SET phone=? WHERE id=? AND deleted=0
    }

    @Test
    public void select() {
         //1、根据主键获取
         UserDO userDO = mapper.selectById(1);
         //实际执行sql: SELECT id,username,phone,sex,create_time,update_time,deleted FROM user WHERE id=? AND deleted=0
    }
}

上面两条sql虽然没有加入deleted=0这个条件,但因为加了全局配置,所以会自动加上deleted=0条件。


三、自动填充功能

1、使用背景

我们在设计表的时候,会有创建人ID,创建人名称,创建时间,更新人ID,更新人名称,更新时间

我们在新增或者更新数据的时候,都会修改这些数据。所以我们也可以做成全局的。

2、使用方式

1、实体添加注解

   /**
     * 创建时间
     */
    @TableField(value = "create_time",fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    @TableField(value = "update_time",fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

FieldFill一共有4种属性,默认是DEFAULT

public enum FieldFill {
    DEFAULT,
    INSERT,
    UPDATE,
    INSERT_UPDATE;
}

2、自定义实现类 MyMetaObjectHandler

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    @Override
    public void insertFill(MetaObject metaObject) {
        //设置属性值
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); 
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
}

3、测试

  @Test
    public void insert() {
        UserDO user = new UserDO();
        user.setUsername("小小");
        user.setPhone("18812345678");
        user.setSex("女");
        Assertions.assertThat(mapper.insert(user)).isGreaterThan(0);
        // 成功直接拿回写的 ID
        Assertions.assertThat(user.getId()).isNotNull();
    }

用上面这个测试用例,发现虽然这里没有插入创建时间和更新时间,但打印sql发现插入该属性。

==>  Preparing: INSERT INTO user ( username, phone, sex, create_time, update_time ) VALUES ( ?, ?, ?, ?, ? )
==> Parameters: 小小(String), 18812345678(String), 女(String), 2022-09-27T16:40:00.682(LocalDateTime), 2022-09-27T16:40:00.687(LocalDateTime)
<==    Updates: 1

4、注意事项

  • 填充原理是直接给entity的属性设置值!!!
  • 注解则是指定该属性在对应情况下必有值,如果无值则入库会是null
  • MetaObjectHandler提供的默认方法的策略均为:如果属性有值则不覆盖,如果填充值为null则不填充
  • 字段必须声明TableField注解,属性fill选择对应策略,该声明告知Mybatis-Plus需要预留注入SQL字段
  • 填充处理器MyMetaObjectHandler在 Spring Boot 中需要声明@Component@Bean注入
  • update(T t,Wrapper updateWrapper)时t不能为空,否则自动填充失效

四、分页功能示例

1、配置类

@Configuration
public class MybatisPlusPageConfig {
    /**
     * 新的分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

2、示例

public class UserServicePageTest extends Base {

   @Autowired
   private UserMapper mapper;

   
    @Test
    public void page() {
        Page<UserDO> page = new Page<>(1, 3);
        Page<UserDO> result = mapper.selectPage(page, new LambdaQueryWrapper<UserDO>().eq(UserDO::getDeleted, 0));
    }
}

这里有点需要注意: MybatisPlus 与 pagehelper 存在依赖 jsqlparser 冲突,不建议混用

项目地址: https://github.com/yudiandemingzi/spring-boot-study



posted on 2022-09-29 20:15  雨点的名字  阅读(643)  评论(0编辑  收藏  举报