今天正好学习了Mybatis-Plus Mapper的Insert、Update 及自动填充功能的使用,特来和大家分享一下,需要注意的是我用的Mybatis-Plus版本是3.5.2,基础配置可见01。
目录
①数据表增加字段:create_time,update_time
源码地址:尹煜 / mybatis_plus_study · GitCode
1 Insert
1.1 测试插入
路径:src/test/java/com/yinyu/MybatisPlusApplicationTests.java
//测试插入
@Test
public void testInsert(){
User user = new User();
user.setName("尹煜");
user.setAge(3);
user.setEmail("yinyu@163.com");
int result = userMapper.insert(user);//帮助用户自动生成id
System.out.println(result);//受影响的行数
System.out.println(user);//通过日志发现id会自动回填
}
输出结果:
需要注意的是,数据库插入的id的默认值为全局的唯—id,而且是自动生成的。
1.2 主键生成策略
分布式系统唯一Id生成方案链接:分布式系统唯一ID生成方案汇总
源码解释:
public enum IdType {
AUTO, //数据库id自增
NONE;//未设置主键
INPUT, //手动输入
ID_WORKER, //默认的全局唯一id
UUID, //全局唯一id uuid
ID_WORKER_STR, //字符串表示法
**
}
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心(北京、香港···),5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。
1.2.1 主键自增:
AUTO 我们需要配置主键自增
①在实体类字段上配置@TableId(type = IdType.AUTO)
②数据库字段设置自增
我用的是DBeaver可视化软件
③执行testInsert,输出结果:
1.2.2 手动输入:
②执行testInsert(id:null)
可以看到插入成功,但是id为null,不过mysql会匹配成自增输入id形式,所以也没有报错。
③执行testInsert(id:6)
2 Update
路径:src/test/java/com/yinyu/MybatisPlusApplicationTests.java
//测试更新
public void testUpdate(){
User user = new User();
user.setId(6L);
user.setName("无关风月");
//updateById,但是参数是个user!!
int result = userMapper.updateById(user);
System.out.println(result);
System.out.println(user);
}
输出结果:
3 自动填充
创建时间、更改时间! 这些操作一般都是自动化完成,我们不希望手动更新
阿里巴巴开发手册︰几乎所有的表都要配置 gmt_create、gmt_modified !而且需要自动化
3.1 方式一:数据库级别
①数据表增加字段:create_time,update_time
②实体类中同步!
路径:src/main/java/com/yinyu/pojo/User.java
private Date createTime;//驼峰命名
private Date updateTime;
③查看结果
3.2 方式二:通过代码
①取消数据库的默认值,更新操作!
注意不是删掉字段,而是取消默认值等👇
②实体类字段属性上需要增加注解
路径:src/main/java/com/yinyu/pojo/User.java
//字段 字段添加填充内容
@TableField(fill = FieldFill.INSERT)//value = ("create_time"),
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
③编写处理器来处理注解!
自动填充功能官方文档:自动填充功能 | MyBatis-Plus
官方已更新setFieldValByName方法为strictInsertFill或fillStrategy等,不过需要将mybatis-plus版本升级到3.3.0及以上,由于本文mybatis-plus版本在3.5.2,采用官网推荐的方法👇
路径:src/main/java/com/yinyu/handler/MyMetaObjectHandler.java
@Slf4j//日志
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override//插入时的填充策略
public void insertFill(MetaObject metaObject) {
log.info("==start insert ······==");
//setFieldValByName(java.lang.String fieldName, java.lang.Object fieldVal, org.apache.ibatis.reflection.MetaObject metaObject)
this.strictInsertFill(metaObject,"createTime",Date.class, new Date());
this.strictInsertFill(metaObject,"updateTime",Date.class, new Date());
}
@Override//更新时的填充策略
public void updateFill(MetaObject metaObject) {
log.info("==start update ······==");
this.strictUpdateFill(metaObject,"updateTime",Date.class, new Date());
}
}
④测试插入/更新,观察时间:
//测试插入
@Test
public void testInsert(){
User user = new User();
user.setName("testMetaObjectInsert");
user.setAge(3);
user.setEmail("yinyu@163.com");
int result = userMapper.insert(user);//帮助用户自动生成id
System.out.println(result);//受影响的行数
System.out.println(user);//通过日志发现id会自动回填
}
//测试更新
@Test
public void testUpdate(){
User user = new User();
user.setId(6L);
user.setName("testMetaObjectUpdate");
int result = userMapper.updateById(user);
System.out.println(result);
System.out.println(user);
}
⑤输出结果
4 乐观锁
乐观锁:顾名思义十分乐观,他总是认为不会出现问题,无论干什么都不上锁!如果出现了问题,再次更新值测试。
悲观锁:顾名思义十分悲观,他总是认为出现问题,无论干什么都会上锁!再去操作!
乐观锁实现方式:
-
取出记录时,获取当前version
-
更新时,带上这个version
-
执行更新时,set version = newVersion where version = oldVersion
-
如果version不对,就更新失败
乐观锁:先查询,获得版本号
-- A
update user set name = "yinyu",version = version+1
where id = 1 and version = 1
-- B (B线程抢先完成,此时version=2,会导致A线程修改失败!)
update user set name = "yinyu",version = version+1
where id = 1 and version = 1
接下里实践一下~
4.1 数据库中增加version字段
4.2 实体类加对应的字段
路径:src/main/java/com/yinyu/pojo/User.java
@Version//乐观锁version注解
private Integer version;
说明:
- 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
- 整数类型下 newVersion = oldVersion + 1
- newVersion 会回写到 entity 中
- 仅支持 updateById(id) 与 update(entity, wrapper) 方法
- 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
4.3 注册组件
路径:src/main/java/com/yinyu/config/MyBatisPlusConfig.java
@MapperScan("com.yinyu.mapper")//交给mybatis做的,可以让这个配置类做扫描
@EnableTransactionManagement//自动管理事务
@Configuration//配置类
public class MyBatisPlusConfig {
/**
* 新版
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
4.4 测试
① 测试乐观锁成功
@Test//测试乐观锁成功
public void testOptimisticLocker1(){
//1、查询用户信息
User user = userMapper.selectById(1L);
//2、修改用户信息
user.setAge(18);
user.setEmail("yinyu@qq.com");
//3、执行更新操作
userMapper.updateById(user);
}
② 测试乐观锁失败
@Test//测试乐观锁失败 多线程下
public void testOptimisticLocker2(){
//线程1
User user1 = userMapper.selectById(2L);
user1.setAge(18);
user1.setEmail("yinyu@qq.com");
//线程2,模拟另外一个线程执行了插队操作
User user2 = userMapper.selectById(2L);
user2.setAge(17);
user2.setEmail("yinyu17@qq.com");
userMapper.updateById(user2);
//自旋锁来多次尝试提交!
userMapper.updateById(user1);//如果没有乐观锁就会覆盖插队线程的值
}
5 Select
5.1 普通查询
① 通过id查询单个用户
@Test//通过id查询单个用户
public void testSelectById(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
② 通过id查询多个用户
@Test//通过id查询多个用户
public void testSelectBatchIds(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
users.forEach(System.out::println);
}
③ 条件查询 通过map封装
@Test//通过条件查询之一 map
public void testMap(){
HashMap<String, Object> map = new HashMap<>();
//自定义要查询的
map.put("name","尹煜");
map.put("age",3);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
5.2 分页查询
分页在网站的使用非常之多!
1、原始的limit分页
2、pageHelper第三方插件
3、MybatisPlus其实也内置了分页插件!!!
① 配置拦截器组件
路径:src/main/java/com/yinyu/config/MyBatisPlusConfig.java
//新的分页插件,,一缓和二缓遵循mybatis的规则
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
② 直接使用page对象
@Test//测试分页查询
public void testPage(){
//参数一current:当前页 参数二size:页面大小
//使用了分页插件之后,所有的分页操作都变得简单了
Page<User> page = new Page<>(2,3);
userMapper.selectPage(page,null);
page.getRecords().forEach(System.out::println);
System.out.println("总页数==>"+page.getPages());
}
③ 查看结果
6 Delete
6.1 普通删除
类似select~
@Test
public void testDeleteById(){
userMapper.deleteById(1L);
}
@Test
public void testDeleteBatchIds(){
userMapper.deleteBatchIds(Arrays.asList(2L,3L));
}
@Test
public void testD(){
HashMap<String, Object> map = new HashMap<>();
map.put("age","18");
map.put("name","john");
userMapper.deleteByMap(map);
}
6.2 逻辑删除
物理删除:从数据库中直接删除
逻辑删除:在数据库中没有被删除,而是通过一个变量来使他失效! deleted=0 ==> deleted=1
场景:管理员可以查看被删除的记录!防止数据的丢失,类似于回收站!
① 数据表增加deleted字段
② 实体类中添加对应属性
@TableLogic//逻辑删除注解
private Integer deleted;
③ 配置!
3.3版本后只需要在下边或yaml中配置即可
路径:src/main/resources/application.properties
#配置逻辑删除 没删除的为0 删除的为1
mybatis-plus.global-config.db-config.logic-delete-field: flag
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
④ 测试逻辑删除
@Test
public void testDeleteById(){
userMapper.deleteById(4L);
}
再次测试查询被删除的用户,发现查询为空
@Test
public void testselect(){
User user = userMapper.selectById(4);
System.out.println(user);
}
👇
说明逻辑删除已成功
以上所有的CRUD及其扩展操作,我们都必须精通掌握!会大大提高工作写项目的效率!
总结
大家如果有疑问都可以评论提出,有不足之处请大家批评指正,希望能多结识这方面的朋友,共同学习、共同进步。