MybatisPlus
一、介绍
MybatisPlus 是一个Mybatis的增强工具,为简化开发、提升效率而生。
功能:
-
自动生成单表的CRUD 功能
-
提供丰富的 条件拼接方式
-
全自动ORM类型持久层框架
二、简单使用
-
导入 mybatis-plus 的 启动器
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- 测试环境 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <!-- mybatis-plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <!-- 数据库相关配置启动器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- druid启动器的依赖 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-3-starter</artifactId> <version>1.2.23</version> </dependency> <!-- 驱动类--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.28</version> </dependency>
导入 mybatis-plus 就不用 导入 mybatis了有依赖传递
-
创建配置文件、启动类、实体类等
-
创建Mapper 接口,该接口继承 BaseMapper,泛型就是对应的实体类
继承该接口,自带 crud 方法
package com.ztone.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ztone.pojo.User; public interface UserMapper extends BaseMapper<User> { }
@Test public void test(){ List<User> users = userMapper.selectList(null); System.out.println(users); }
使用时直接调用 里面的方法即可。
三、Service层的 crud
在mapper 接口中继承了 BaseMapper后,就可以直接调用上面的增删查改。
在service 层也可以直接进行 crud 的操作然后返回给controller,不用经过 mapper。
具体方式如下:
-
service 层接口 继承 IService 接口,该接口提供了一些crud的操作方法,但是实现了一部分,另一部分没有实现
-
需要service 层接口的实现类,在实现接口的同时,继承ServiceImpl 类。这个类是 IService的实现类,实现了IService中没有实现的方法。
package com.ztone.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ztone.pojo.User;
public interface UserService extends IService<User> {
}
package com.ztone.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ztone.mapper.UserMapper;
import com.ztone.pojo.User;
import com.ztone.service.UserService;
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
继承 ServiceImpl 传入的泛型是:继承了 BaseMapper的mapper 和 实体类
service层提供的方法:
Save:
//插入一条记录
boolean save(T entity);
//批量插入
boolean saveBatch(Collection<T> entityList);
//批量插入,指定插入的最大条数
boolean saveBatch(Collection<T> entityList,int batchSize);
SaveOrUpdate:
//主键如果没有值就是插入,有值就是更新
boolean saveOrUpdate(T entity);
...
Remove:
//根据 queryWrapper 设置的条件,删除记录
boolean remove(Wrapper<T> queryWrapper);
//根据id删除
boolean removeById(Serializable id);
//根据map条件删除
boolean removeByMap(Map<String,Object> columnMap);
//批量删除
boolean removeByIds(Collection idList);
Update:
//根据updateWrapper 更新
boolean update(Wrapper<T> updateWrapper);
//根据id更新
boolean updateById(T entity);
//根据id批量更新
boolean updateBatchById(Collection entityList);
Get:
//根据id查询
T getById(Serializable id);
//根据wrapper 查询一条记录
T getOne(Wrapper queryMapper);
四、分页
实现分页的步骤:
-
创建分页插件
在配置类中,将mybatis-plus 的插件集合加入到 ioc容器
将需要的 分页插件加到 插件集合中
@Bean public MybatisPlusInterceptor plusInterceptor(){ MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor (new PaginationInnerInterceptor(DbType.MYSQL)); return mybatisPlusInterceptor; }
创建分页插件时,需要传入 数据库的类型,如DBType.MYSQL
-
使用
创建Page对象,传入的参数是页码和页容量,调用 selectPage,传入 page对象
查询的结果也会保存在 page对象中
@Test public void test_page(){ Page<User> page = new Page<>(1, 2); userMapper.selectPage(page,null); //当前页数据 List<User> records = page.getRecords(); //总条数 long total = page.getTotal(); System.out.println(records); }
在自定义的方法中使用 Page 分页,mapper 接口中的查询方法的 返回值和第一个参数都是 Page 类型
Page<User> queryByAge(Page page,Integer age);
这样在调用该方法时传入 page 对象,得到的就是分页的结果
五、条件构造器
条件构造器是把查询条件封装成对象,不需要手动编写复杂的sql语句。
直接调用对象的方法,传入列名或值,就能拼接成响应的条件语句
方法:
-
eq("列名","值") ---相等
-
ne("列名","值") ---不相等
-
gt("列名","值") ---大于
-
ge("列名","值") ---大于等于
-
lt("列名","值") ---小于
-
le("列名","值") ---小于等于
-
between("列名","值","值") ---设置 between
-
between("列名","值","值") ---设置 not between ,不在这两个值之间
-
like("列名","值") ---设置 like 模糊查询
-
isNull("列名") ---设置 is null
...........
1.QueryWrapper
@Test
public void test_01(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name","a");
queryWrapper.between("age",21,30);
userMapper.selectList(queryWrapper);
}
默认是查询全部列,如果想要指定查询的列,可以调用 select 方法
queryWrapper.select("name","age");
通过方法连续调用是用 and 关键字来连接每个条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name","a").between("age",21,30);
上面的代码相当于 SELECT id,name,age,email FROM user WHERE (name LIKE a AND age BETWEEN 21 AND 30)
如果想要使用 or 来 连接条件,可以调用 or方法
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name","a").between("age",21,30).or().isNull("email");
上面的代码相当于
SELECT id,name,age,email FROM user WHERE (name LIKE a AND age BETWEEN 21 AND 30 OR email IS NULL)
每个条件方法,都会有一个boolean 类型的参数,这个参数可以是一个表达式,当返回true时,该条件生效,返回false不生效
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like(StringUtils.isNotBlank(name),"name","a")
.between(age != null && age >18,"age",21,30).or().isNull("email");
这样可以动态判断 参数的条件,就像 mybatis 中的 动态sql
< if test="" 标签
2.UpdateWrapper
主要是用来做 更新的条件对象,用queryWrapper 也可以进行更新操作,但是有两个问题:
-
需要自己创建实体类,传到更新方法中
-
不能将值修改为null,因为在queryWrapper中,值是 null 的话就不去修改该列
所以进行更新操作可以用 updateWrapper
调用对象的set 方法,传入相应的列名和值,就可以指定列了
@Test
public void test_02(){
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.ge("age",20)
.set("name","xxxxxx")
.set("email","123345@");
userMapper.update(null,updateWrapper);
}
3.LambdaQueryWrapper 和 LambdaUpdateWrapper
这两个类是 对 QueryWrapper 和 UpdateWrapper 的升级
当传入列名时,有可能会将列名写错。
那么用 LambdaQueryWrapper ,第一个参数传入一个 lambda表达式,来表示列名
该表达式是用 实体类的方法引用 User::getName() 来表示该属性,从而代表了数据库的列
@Test
public void test_02(){
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.ge(User::getAge,20)
.set(User::getName,"xxxxxx")
.set(User::getEmail,"123345@");
userMapper.update(null,updateWrapper);
}
六、注解
1. @TableName
当数据库的表名和 实体类不同时,用BaseMapper 的方法就会找不到数据库表。
这时可以在实体类上使用 @TableName 注解,标注数据库表名可以解决这一问题。
如果实体类和数据库表名的差距只是一个前缀的问题,可以在配置文件中设置给所有实体类加上前缀
mybatis-plus:
global-config:
db-config:
table-prefix: t_
这样就不用在每个实体类前面使用 @TableName,前提是你的数据库表名前缀相同并且后面和实体类名相同
2. @TableId
@TableId 是用在 实体类的主键属性上
-
当主键的列名和属性名不一致时,可以使用该注解指定属性对应的列名
@TableId(value="t_id") private Long id;
该注解还有一个属性是 type 用来指定插入数据时如何生成主键值,type有两个值
-
AUTO 数据库id自增长,要求数据库主键自增长开启
-
ASSING_ID 通过雪花算法生成不重复的long类型数字,也是默认值,要求数据库主键是bigint或 varchar(64)
当一个表数据非常多,需要横切分成几个表,那么主键的生成建议用雪花算法,因为如果自增长可能会和下一个表中的主键重复
全局设置主键策略:
mybatis-plus:
global-config:
db-config:
id-type: auto
3. @TableField
当属性名和数据库表的列名不一致时,可以在实体类的属性上使用该注解,指定列名
他还有一个属性是 exist,设置为false表示无论属性名和列名相不相同都忽略这个属性
七、高级扩展
1. 逻辑删除
-
物理删除:真实删除,将数据在数据库中删除,之后完全查询不到
-
逻辑删除:假删除,给数据库添加一个字段,逻辑删除后,将该字段的状态修改为被删除状态,在数据库中仍然可以看到该数据。
逻辑删除的实现:
-
给数据库添加 删除状态字段
-
给实体类添加逻辑删除属性
-
在属性上使用 @TableLogic
当你删除时,他会自动把删除语句改成 更新语句,并且查询时条件加上不查询已删除状态的字段
除了使用@TableLogic 指定删除状态字段,还可以全局指定,并且指定字段的已删除和未删除对应的值(默认是0和1)
mybatis-plus:
global-config:
db-config:
logic-delete-field: delete
logic-delete-value: 1
logic-not-delete-value: 0
2.乐观锁
乐观锁和悲观锁 是防止在并发数据更新时出现错误数据
乐观锁的实现方式这里用的是版本号的方式
版本号:为数据添加一个版本号字段,每次数据更新时,比较当前版本号是否与期望值一致,如果一致则更新成功,不一致表示数据已经被更改,那么就不会去更新数据了
实现步骤:
-
给数据库添加 版本号的字段
-
在配置类中,添加乐观锁的插件
@Bean public MybatisPlusInterceptor plusInterceptor(){ MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return mybatisPlusInterceptor; }
OptimisticLockerInnerInterceptor() 就是乐观锁插件
-
给实体类添加版本号属性,并且在属性上使用 @Version 注解
@Data public class User { private Integer id; private String name; private Integer age; private String email; @Version private int version; }
正常进行更新操作即可。
在执行更新语句时,会判断版本号是否是最新的,并且更新时也会更新版本号
3.防止全表更新或删除
通过在配置类中添加 拦截器实现
@Bean
public MybatisPlusInterceptor plusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return mybatisPlusInterceptor;
}
BlockAttackInnerInterceptor
八、逆向工程
通过数据库表生成 实体类、mapper接口、mapper的xml文件、service接口和实现类
需要idea 的插件 MyBatisX
将数据库导入到idea后
MyBatisX-Generator 选项
生成文件的配置
选择根据mybatis-plus3 生成文件
自动生成sql代码:
在mapper接口中,选择想要生成的sql
选择好想要生成的 sql alt+ enter 然后选择第一个就能直接生成sql语句