CRUD和myBatis-plus插件
1.Insert方法和雪花算法
当一个数据表中的id为主键时,且插入的数据的时候不插入主键id,那么会发生什么呢?接下来就进行一次简单测试,还是那个User表,插入其它属性,不插入主键id。
测试方法:
@Testpublic void testInsert(){User user = new User();user.setAge(18);user.setName("maming");user.setEmail("maming@qq.com");int insert = userDao.insert(user);System.out.println(insert);}
通过上面的方式进行插入之后数据库表中的数据会改变为:
我们可以轻易发现id值变成了一串数字,和上面的id格格不入,这就是自动生成的全局唯一id,使用的就是雪花算法
雪花算法(Snowflake Algorithm)是Twitter开源的一种分布式唯一ID生成算法,用于在分布式系统中生成全局唯一的ID。其主要特点包括:
-
全局唯一性:通过特定的算法设计,确保生成的ID在分布式系统中的全局唯一性。
-
时间有序性:生成的ID中包含时间戳信息,使得ID按照时间顺序递增,便于排序和查询。
-
符号位(1位):最高位固定为0,表示ID为正数。
-
时间戳(41位):记录ID生成的时间,精确到毫秒级。41位时间戳可以表示大约69年的时间范围(从某个自定义的起始时间开始计算),确保ID在较长时间内不会重复。
-
数据中心ID(5位):用于标识不同的数据中心,通常在5到10位之间,根据实际需求配置。这有助于在多数据中心环境中区分不同数据中心生成的ID。
-
机器ID(5位):用于标识同一数据中心内的不同机器或进程,通常在5到10位之间。与数据中心ID结合,确保每台机器生成的ID唯一。
-
序列号(12位):在同一毫秒内,通过序列号区分不同的ID。12位序列号可以支持每毫秒生成4096个唯一的ID,满足高并发场景下的需求。
除此之外,insert方法插入id还有其它的生成方式,都可以使用@ValueId注解来指定: @TableId(type = IdType.ASSIGN_ID) //默认的雪花算法生成
2.update方法
使用update方法有很多,常用的就是使用ID进行更改和使用条件选择器
使用ID更改:
@Testpublic void testUpdate(){User msf = new User().setAge(16).setName("msf").setId(1L);int i = userDao.updateById(msf);System.out.printf("---", i);}
使用一个对象进行更改,只要传入id属性值,那么就会将其作为条件,并且mybaits-plus的方法都是默认 动态sql,没多一个属性就会多拼接更改列,根本不需要自己来设置,非常高效
使用条件选择器进行更改:
@Testpublic void testUpdate(){User msf = new User().setAge(100);QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("name", "msf"); //where name =‘msf’int i = userDao.update(msf, wrapper);System.out.println(i);}
3.自动填充
MyBatis-Plus 提供了一个便捷的自动填充功能,用于在插入或更新数据时自动填充某些字段,如创建时间、更新时间等
为了测试,我们需要在数据库更新两个字段,用户后期自动填充:
ALTER TABLE userADD COLUMN create_time DATE AFTER email;
ALTER TABLE userADD COLUMN update_time DATE AFTER create_time;
实时更新后面的实体类:
@TableField(fill = FieldFill.INSERT)private Date createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private Date updateTime;
@TableField注解,可以配置在进行数据库操作的时候自动填入那些属性,INSERT即插入的时候需要使用,而INSERT_UPDATE是插入和删除都需要自动填充
实现 MetaObjectHandler:创建一个类来实现 MetaObjectHandler
接口,并重写 insertFill
和 updateFill
方法
// java example @Slf4j @Component public class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {log.info("开始插入填充...");this.setFieldValByName("createTime", new Date(), metaObject);this.setFieldValByName("updateTime", new Date(), metaObject);}@Overridepublic void updateFill(MetaObject metaObject) {log.info("开始更新填充...");this.setFieldValByName("updateTime",new Date(), metaObject);} }
测试插入新数据后是否自动填入时间:
@Testpublic void testAutoFill(){User user = new User().setAge(100).setEmail("maming@qq.com").setName("ok");int i = userDao.insert(user);}
数据库插入截图:
测试更新:
@Testpublic void testAutoFill2(){User user = new User().setAge(100).setEmail("maming@qq.com").setName("ok").setId(1898304906765160449L);userDao.updateById(user);}
4.乐观锁插件
乐观锁是一种并发控制机制,用于确保在更新记录时,该记录未被其他事务修改。MyBatis-Plus 提供了 OptimisticLockerInnerInterceptor
插件,使得在应用中实现乐观锁变得简单。
悲观锁(Pessimistic Locking)
定义:悲观锁假设冲突会发生,并因此在整个事务期间对数据进行锁定。这意味着当一个事务正在操作某个数据项时,其他事务不能对该数据项进行任何操作,直到当前事务完成并释放锁
乐观锁(Optimistic Locking)
定义:乐观锁假设冲突不会发生,在没有锁的情况下允许所有的事务读取和尝试更新数据。只有在提交更改时才会检查是否有冲突(即是否有其他事务在此期间修改了相同的数据)。如果检测到冲突,则当前事务需要重试或回滚。
使用乐观锁相当于不给数据库上锁,而是当多个线程进行操作时,会出现更新失败的情况,这不是锁的作用,而是操作表有个version字段,当version匹配时才可以操作成功
使用前先需要配置Mybatis config,当然这也是官方写好的配置类,适用于配置mybatis-plus:
@Configuration @MapperScan("org.cqust.testmybatisplus.dao") public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;} }
由于这个配置文件专门用于配置mybatis-plus,故而需要扫描mapper文件的操作可以配置在这里
更新数据库表,新增一个version字段:
ALTER TABLE userADD COLUMN version int default 1 AFTER email;
同步实体类中的属性:
@Versionprivate Integer version;
新增此字段时,更新操作就会默认对比version,如果匹配跟新成功,反之更新失败
测试方法:
@Testpublic void testVersion(){//先查询,获得version号,然后进行更改User user = userDao.selectById(2L);//填写修改信息user.setName("testVersion");user.setAge(18);//修改操作执行 userDao.updateById(user);}
测试结果:
如上图:更新语句会自动检测version是不是和查询出来的version版本对应,我们手写的更新条件并没有写上version字段,而是@version注解自动配置的
5.分页查询插件
分页插件和乐观锁插件都是mybatis-plus的插件,故而它们都需要配置在config配置类之下:
注册分页插件的代码直接从官网上复制就好了
@Configuration @MapperScan("org.cqust.testmybatisplus.dao") public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;} }
使用mybatis-plus插件的时候需要结合一个selectPage方法才可以使用,这就是专门用于分页的方法,查询出来的结果就是分页结果
由于mybatis-plus3.5.9就把分页插件分割出来了,故而使用的话需要单独导入其插件依赖:
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-jsqlparser --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-jsqlparser</artifactId><version>3.5.10.1</version></dependency>
测试方法:
@Testpublic void testLimit(){//拿取第1页,此页两列数据List<User> users = userDao.selectList(new Page<>(1, 2), null);users.forEach(System.out::println);}
查询结果:
6.逻辑删除
逻辑删除是一种优雅的数据管理策略,它通过在数据库中标记记录为“已删除”而非物理删除,来保留数据的历史痕迹,同时确保查询结果的整洁性。MyBatis-Plus 提供了便捷的逻辑删除支持,使得这一策略的实施变得简单高效。
它通过在数据库配置一个字段专门用于记录可见与不可见,在后面的操作增删改查都会监视这个字段,当为逻辑删除后的字段时,就不会执行操作,目的是为了保存数据
物理删除:已经从数据库中删除;逻辑删除:对增删改查进行隐藏,数据库依旧存在此数据
依旧需要先在数据库中创建一个字段:
ALTER TABLE userADD COLUMN deleted int default 1 AFTER create_time;
在实体类中更新属性:
@TableLogic //逻辑删除注解private Integer deleted;
配置springBoot的配置属性,标志全局统一逻辑删除符号:
#逻辑删除 mybatis-plus.global-config.db-config.logic-delete-value=0 mybatis-plus.global-config.db-config.logic-not-delete-value=1
测试方法:
@Testpublic void testDelete(){//删除1号userDao.deleteById(1L);//删除后查询User user = userDao.selectById(1L);System.out.println(user);}
结果:
如上deleted字段被改为1,查询结果:
同时我们可以发现配置逻辑删除之后,其实走的是update方法,同时在查询的时候会需要携带上deleted = 1,即有效数据的标识符,才可以查出,故而逻辑删除后,在查询记录其实是查询不出来的,但是数据库表中又是还存在的