mybatis_plus -- CRUD 、扩展、插件

文章目录

1. Mapper CRUD 接口

  • 通用 CRUD 封装BaseMapper (opens new window)接口,为 Mybatis-Plus 启动时自动解析实体表关系映射转换为 Mybatis 内部对象注入容器
  • 泛型 T 为任意实体对象
  • Wrapper updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
  • 参数 Serializable 为任意类型主键 Mybatis-Plus 不推荐使用复合主键约定每一张表都有自己的唯一 id 主键
  • 对象 Wrapper 为 条件构造器

1.1 Insert – 添加User – 默认 ID_WORKER 全局唯一id

语法:

// 插入一条记录
int insert(T entity); 

insert插入操作会自动生成id,返回受影响的行数,id会自动回填值User对象

数据库插入的id的默认值为:全局的唯一id

// 测试插入
@Test
public void testInsert(){
  User user = new User();
  user.setName("mybatis_plus");
  user.setAge(3);
  user.setEmail("12345678@qq.com");
  int result = userMapper.insert(user); // 帮我们自动生成id
  System.out.println(result); // 受影响的行数
  System.out.println(user); // 发现,id会自动回填
}

扩展 :主键(id)生成策略 – 插入时自动生成id

字段 描述
AUTO 数据库ID自增
NONE 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
INPUT insert前自行set主键值
ASSIGN_ID 分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口- IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)
ASSIGN_UUID 分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator的方法- nextUUID(默认default方法)
ID_WORKER 分布式全局唯一ID 长整型类型(please use ASSIGN_ID)
UUID 32位UUID字符串(please use ASSIGN_UUID)
ID_WORKER_STR 分布式全局唯一ID 字符串类型(please use ASSIGN_ID)

1) ID_WORKER 全局唯一id – 雪花算法

分布式系统唯一id生成:https://www.cnblogs.com/haoxinyue/p/5208136.html

雪花算法:
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为
毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味
着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。可以保证几乎全球唯
一!

2) 设置主键生成策略

实体类字段上 @TableId(type = IdType.AUTO)

public class User {
    @TableId(type = IdType.UUID)
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

1.2 Update – 更新id为 3 的User数据

语法:

// 根据 whereWrapper 条件,更新记录
int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper);
// 根据 ID 修改
int updateById(@Param(Constants.ENTITY) T entity);

updateById 但是参数是一个对象!Update 通过条件自动拼接动态sql

// 测试更新
@Test
public void testUpdate(){
  User user = new User();
  // 通过条件自动拼接动态sql
  user.setId(3L);//long类型
  user.setName("mybayis_plus");
  user.setAge(18);
  // 注意:updateById 但是参数是一个对象!
  int i = userMapper.updateById(user);
  System.out.println(i);
}

扩展 :自动填充 – 插入、更新时自动填充数据

自动填充创建时间、修改时间!这些个操作一遍都是自动化完成的,我们不希望手动更新!

阿里巴巴开发手册:所有的数据库表:gmt_create、gmt_modified几乎所有的表都要配置上!而且需
要自动化!

1) 实体类字段属性上需要增加注解

  • DEFAULT 默认不处理
  • INSERT 插入时填充字段
  • UPDATE 更新时填充字段
  • INSERT_UPDATE 插入和更新时填充字段
// 字段添加填充内容
@TableField(fill = FieldFill.INSERT)
private Date createTime;

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

2) 编写处理器来处理这个注解

@Slf4j
@Component // 一定不要忘记把处理器加到IOC容器中!
public class MyMetaObjectHandler implements MetaObjectHandler {

// 插入时的填充策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill.....");
// setFieldValByName(String fieldName, Object fieldVal, MetaObject
metaObject
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}

// 更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill.....");
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}

插件:乐观锁 – 当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁 : 故名思意十分乐观,它总是认为不会出现问题,无论干什么不去上锁!如果出现了问题,
再次更新值测试
悲观锁:故名思意十分悲观,它总是认为总是出现问题,无论干什么都会上锁!再去操作!
乐观锁实现方式:

  • 取出记录时,获取当前version(版本)
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
    newVersion = oldVersion + 1 自动装配
  • 如果version不对,就更新失败

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

1) 在配置类注册组件

// 扫描我们的 mapper 文件夹
@MapperScan("com.yanyu.mapper")
@EnableTransactionManagement
@Configuration // 配置类
public class MyBatisPlusConfig {
// 注册乐观锁插件
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}

2)实体类加对应的字段添加注解

@Version //乐观锁Version注解
private Integer version;

1.3 Select

语法:

// 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 查询(根据ID 批量查询)
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据 columnMap 条件)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

查询测试:

// 测试查询
@Test
public void testSelectById(){
User user = userMapper.selectById(1L);
System.out.println(user);
}

// 测试批量查询!
@Test
public void testSelectByBatchId(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println);
}

// 按条件查询之一使用map操作
@Test
public void testSelectByBatchIds(){
HashMap<String, Object> map = new HashMap<>();
// 自定义查询
map.put("name","mybatis_plus");
map.put("age",3);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}

插件:分页 – 对查询数据进行分页处理

1) 在配置类注册组件

@Configuration
@MapperScan("scan.yanyu.mapper")
public class MybatisPlusConfig {

@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}

2) 测试

语法:

// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

测试:

  • 入参的 IPage 不能为null,因为 返回的IPage == 入参的IPage
  • IPage.setRecords 返回的 List;
// 测试分页查询
@Test
public void testPage(){
// 参数一:当前页
// 参数二:页面大小
// 使用了分页插件之后,所有的分页操作也变得简单的!
 Page<User> page = new Page<>(2,5);
 userMapper.selectPage(page,null);
 page.getRecords().forEach(System.out::println);
 System.out.println(page.getTotal());
}

3) 自定义的 mapper 使用分页

如果返回类型是 IPage 则入参的 IPage 不能为null,因为 返回的IPage == 入参的IPage
如果返回类型是 List 则入参的 IPage 可以为 null(为 null 则不分页),但需要你手动 入参的
IPage.setRecords(返回的 List);

IPage<UserVo> selectPageVo(IPage<?> page, Integer state);
// or (class MyPage extends Ipage<UserVo>{ private Integer state; })
MyPage selectPageVo(MyPage page);
// or
List<UserVo> selectPageVo(IPage<UserVo> page, Integer state);
---------------------------------------------------------
<select id="selectPageVo" resultType="xxx.xxx.xxx.UserVo">
    SELECT id,name FROM user WHERE state=#{state}
</select>

1.4 Delete

语法:

// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 删除(根据ID 批量删除)
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 ID 删除
int deleteById(Serializable id);
// 根据 columnMap 条件,删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

测试删除

@Test
public void testDeleteById(){
userMapper.deleteById(1L);
}
// 通过id批量删除
@Test
public void testDeleteBatchId(){
userMapper.deleteBatchIds(Arrays.asList(1L,2L));
}
// 通过map删除
@Test
public void testDeleteMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","狂神说Java");
userMapper.deleteByMap(map);
}

扩展:逻辑删除

物理删除 :从数据库中直接移除
逻辑删除 :再数据库中没有被移除,而是通过一个变量来让他失效! deleted = 0 => deleted = 1

说明:

只对自动注入的sql起效:

  • 插入: 不作限制
  • 查找: 追加where条件过滤掉已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
  • 更新: 追加where条件防止更新到已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
  • 删除: 转变为 更新

例如:
删除: update user set deleted=1 where id = 1 and deleted=0
查找: select id,name,deleted from user where deleted=0

字段类型支持说明:
支持所有数据类型(推荐使用 Integer,Boolean,LocalDateTime)
如果数据库字段使用datetime,逻辑未删除值和已删除值支持配置为字符串null,另一个值支持配置为函数来获取值如now()

1) 配置类注册组件

// 逻辑删除组件!
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}

2) 配置文件设置逻辑未删除值和已删除值

mybatis-plus:
  global-config:
    db-config:
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

3)实体类字段添加注解

@TableLogic //逻辑删除
private Integer deleted;

插件:性能分析插件

作用:性能分析拦截器,用于输出每条 SQL 语句及其执行时间

1) 导入插件

/**
* SQL执行效率插件
*/
@Bean
@Profile({"dev","test"})// 设置 dev test 环境开启,保证我们的效率
 public PerformanceInterceptor performanceInterceptor() {
 PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
 performanceInterceptor.setMaxTime(100); // ms设置sql执行的最大时间,如果超过了则不执行
 performanceInterceptor.setFormat(true); // 是否规格化代码
 return performanceInterceptor;
}

记住,要在SpringBoot中配置环境为dev或者 test 环境!

上一篇:吐槽 | 做增删改查有前途么?


下一篇:什么是CRUD?CRUD程序员是什么意思?