MybatisPlus
学习笔记
一、实体类中对应主键生成策略
public enum IdType {
AUTO(0),//数据库id自增,数据库需设置主键自增
NONE(1),//未设置主键
INPUT(2),//手动输入
ID_WORKER(3),//默认的全局唯一ID
UUID(4),//全局唯一ID uuid
ID_WORKER_STR(5);//ID_WORK字符串表示
}
二、自动填充
创建时间,修改时间都是自动化完成,我们不希望手动去更新。两种实现方式:
- 数据库级别:
在表中新建create_time和update_time,其中创建时间和更新时间的默认值均设置为CURRENT_TIMESTAMP,更新时间设置为自动更新。
进行测试:
@Test
@Test
void contextLoads() {
User user = new User();
user.setUsername("crisp");
user.setPassword("2345");
userMapper.insert(user);
}
我没有手动对时间进行任何操作,查看日志和数据库如图
可以看到更新时间设置自动更新,因此实现自动数据填充,同时创建时间设置和更新时间设置为相同的默认值,故也得到更新。
- 代码级别:
首先清除数据库时间自动更新以及默认值;
其次在实体类相应字段添加自动填充注解;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
最后自定义一个元数据处理器实现MetaObjectHandler中的插入字段以及更新字段处理方法,在代码如下:
@Slf4j
@Component//注入IOC容器中
public class MyMeteObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("going to insert fill");
this.setFieldValByName("createTime", LocalDateTime.now(),metaObject);
this.setFieldValByName("updateTime", LocalDateTime.now(),metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("going to update fill");
this.setFieldValByName("updateTime", LocalDateTime.now(),metaObject);
}
}
进行测试,实现自动填充:
@Test
void contextLoads() {
User user = new User();
user.setUsername("xcs");
user.setPassword("2345");
userMapper.insert(user);
}
三、乐观锁
乐观锁:顾名思义,它总是认为不会出现问题,无论做什么都不去上锁,出现问题时,再次更新值测试
悲观锁:顾名思义十分悲观,它认为总是会出现问题,无论做什么都去上锁,再去操作。
乐观锁实现方式:
取出记录时,获取当前version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion
如果version不对,就更新失败
测试:
- user表新增version字段,默认值为1
- 实体类添加对应的字段
@Version
private Integer version;
- 注册组件
@MapperScan("com.zpl.demo.mapper")//扫描mapper
@EnableTransactionManagement//事物管理
@Configuration//配置类
public class MybatisPlusConfig {
//注册乐观锁插件
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
- 测试乐观锁
单线程下:
@Test
public void testOptimisticLocker()
{
User user = userMapper.selectById(2);
user.setUsername("小小酥");
user.setPassword("saswqeqw");
userMapper.updateById(user);
}
ID为2的数据更新,同时版本号得到更新
多线程下:
@Test
public void testOptimisticLockerFail()
{
User user = userMapper.selectById(2);
user.setUsername("嘿嘿");
user.setPassword("saswqeqw");
//插队线程
User user2 = userMapper.selectById(2);
user2.setUsername("星星");
user2.setPassword("saswqeqw");
userMapper.updateById(user2);
userMapper.updateById(user);//如果没有乐观锁就会覆盖插队线程的值
}
插队线程得以更新数据,版本号自动更新
四、查询操作
//测试批量查询
@Test
public void testQuery()
{
List<User> list = userMapper.selectBatchIds(Arrays.asList(1,2,3));
list.forEach(System.out::println);
}
//条件查询之一 使用map
@Test
public void testMapQuery()
{
Map map = new HashMap();
map.put("username","hhh");
map.put("password",2345);
List<User> list = userMapper.selectByMap(map);
list.forEach(System.out::println);
}
//测试分页查询
@Test
public void testPagination()
{
//参数一:当前页
//参数二:页面大小
Page<User> page = new Page<>(1,5);
userMapper.selectPage(page,null);
page.getRecords().forEach(System.out::println);
}
五、删除操作
//测试删除
@Test
public void testDeleteById()
{
userMapper.deleteById(3);
}
//批量删除
@Test
public void testDeleteBatchById()
{
userMapper.deleteBatchIds(Arrays.asList(2,4));
}
//测试map删除
@Test
public void testMapDelete()
{
Map map = new HashMap();
map.put("username","hhh");
userMapper.deleteByMap(map);
}
六、逻辑删除
物理删除:将数据从数据库清除
逻辑删除:数据库中数据并未真正删除,使用标签字段控制。如cdeleted0->deleted1
- 数据库增加字段deleted。实体类添加deleted属性
@TableLogic
private Integer deleted;
- 配置
//逻辑删除组件
@Bean
public ISqlInjector sqlInjector(){
return new LogicSqlInjector();
}
#配置逻辑删除
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(4);
}
-
结果,逻辑删除实际执行的是更新操作
-
再次查询ID为4的数据
@Test
public void query() {
userMapper.selectById(4);
}
6.结果,由于开启逻辑删除,SQL语句自动拼接deleted=0,即不能查询到数据
七、性能分析插件
开发时可能遇到一些慢SQL,MybatisPlus提供了性能分析插件帮助我们提高效率。
在MybatisPlus配置类中引入插件:
@Bean
@Profile({"dev","test"})//设置dev、test环境开启,保证效率
public PerformanceInterceptor performanceInterceptor(){
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(1);//设置SQL执行的最大时间,超过则不执行,单位毫秒
performanceInterceptor.setFormat(true);//执行的SQL语句会格式化输出
return performanceInterceptor;
}
项目配置文件中设置开发环境:
#设置开发环境
spring.profiles.active=dev
再次执行一个简单的插入操作:
@Test
void contextLoads() {
User user = new User();
user.setUsername("wsd");
user.setPassword("2345");
userMapper.insert(user);
}
控制台输出错误,由于我上面设置的SQL最大执行时间是1毫秒(根据实际情况进行设置),故大于该时间的操作均不能执行。可以看出性能分析插件已经生效,这有助于帮助我们找到慢SQL,进而优化。
八、条件构造器
复杂的SQL可以使用条件构造器实现,根据需要使用相应条件,使用链式编程的方式。**需要注意的是,各个条件之默认是and连接,需要or连接,直接使用or(),在其后添加条件。此外还包含了or嵌套、and嵌套、nested嵌套(正常嵌套 不带 AND 或者 OR)**一些常用操作如下:
@Test
void test1()
{
//查询用户名不为空,邮件不为空,年龄大于等于30的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.isNotNull("username")
.isNotNull("email")
.ge("age",30);
userMapper.selectList(wrapper).forEach(System.out::println);
}
@Test
void test2()
{
//查询年龄在20与30岁之间的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age",20,30);
Integer num = userMapper.selectCount(wrapper);
System.out.println(num);
}
@Test
void test3()
{
//模糊查询
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.notLike("username","s")
.likeRight("email","3");
List<Map<String,Object>> maps = userMapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
@Test
void test4()
{
//多条件模糊查询(使用and嵌套,类似的还有or嵌套以及nested正常嵌套)
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.notLike("username","s")
.and(i -> i.like("age","1").or().like("password","5"));
List<Map<String,Object>> maps = userMapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
@Test
void test5()
{
//in查询
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.inSql("id","select id from user where id >= 5");
List<Object> objects = userMapper.selectObjs(wrapper);
objects.forEach(System.out::println);
}