mybatisPlus
快速入门
准备工作
- 准备数据
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`user_name` varchar(20) NOT NULL COMMENT '用户名',
`password` varchar(20) NOT NULL COMMENT '密码',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`address` varchar(100) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
insert into `user`(`id`,`user_name`,`password`,`name`,`age`,`address`) values (1,'ruiwen','123','瑞文',12,'山东'),(2,'gailun','1332','盖伦',13,'平顶山'),(3,'timu','123','提姆',22,'蘑菇石'),(4,'daji','1222','妲己',221,'狐山');
- 创建springboot项目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- 创建启动类
@SpringBootApplication
public class SGApplication {
public static void main(String[] args) {
SpringApplication.run(SGApplication.class);
}
}
准备实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String userName;
private String password;
private String name;
private Integer age;
private String address;
}
- 添加mp依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
- 配置数据库信息
spring:
datasource:
url: jdbc:mysql://localhost:3306/mp_db?characterEncoding=utf-8&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
- 创建mapper接口
public interface UserMapper extends BaseMapper<User> {}
- 配置mapper扫描
@SpringBootApplication
@MapperScan("com.sangeng.mapper") //在启动类上添加包
public class SGApplication {
public static void main(String[] args) {
SpringApplication.run(SGApplication.class,args);
}
}
//或者,在每个mapper上添加@Mapper
@Mapper
public interface UserMapper extends BaseMapper<User> {}
- 测试
@SpringBootTest
public class MPTest {
@Autowired
private UserMapper userMapper;
@Test
public void testQueryList(){
System.out.println(userMapper.selectList(null));
}
}
- 结果打印
[User(id=1, userName=ruiwen, password=123, name=瑞文, age=12, address=山东), User(id=2, userName=gailun, password=1332, name=盖伦, age=13, address=平顶山), User(id=3, userName=timu, password=123, name=提姆, age=22, address=蘑菇石), User(id=4, userName=daji, password=1222, name=妲己, age=221, address=狐山), User(id=1564511451302768641, userName=三更草堂222, password=7777, name=null, age=null, address=null)]
常用设置
映射规则
@TableName("tb_user") //表映射规则
public class User {
@TableId(type = IdType.AUTO) //主键生成策略
private Long id;
@TableField("address") //字段映射规则
private String addressStr;
}
表映射规则
1.MP默认表名和实体类莫名一致
2.单独设置@TableName(“数据库表名”)
3.全局设置表名前缀,所有表名都比实体类多”tb_”前缀
mybatis-plus:
global-config:
db-config:
#表名前缀
table-prefix: tb_
主键生成策略
1.MP插入数据,没有设置主键生成策略时,默认基于雪花算法的自增id
2.使用其他策略定义实体类时,需要加@TableId
注解,用type属性指定主键生成策略,单独设置
3.IdType
取值
- AUTO:数据库ID自增,依赖于数据库。(确保数据库设置ID自增)
- NONE:未设置主键类型,若在代码中没有手动设置主键,根据主键的全局策略自动生成(默认主键全局策略是基于雪花算法自增ID)
- INPUT:需要手动设置主键,若不设置,插入操作生成SQL时,会是null
- ASSIGN_ID:当没有手动设置主键,即实体类中的主键属性为空时,才会自动填充,使用雪花算法
- ASSIGN_UUID:当实体类的主键属性为空时,才会自动填充,使用UUID
4.全局设置:
mybatis-plus:
global-config:
db-config:
# id生成策略 auto为数据库自增
id-type: auto
字段映射关系
1.默认实体类的属性名映射表的列名
2.属性名和列名不一致时,用@TabelField设置映射关系
字段和列名的驼峰映射
1.默认开启字段名到列名的驼峰命名(即从数据库a_id到属性aId的映射)
2.使用配置关闭
mybatis-plus:
configuration:
map-underscore-to-camel-case: false
日志
配置日志输出打印MP操作对应的SQL语句
mybatis-plus:
configuration:
# 日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
基本使用
insert插入数据
@Test
public void testInsert(){
User user = new User();
user.setUserName("三更草堂333");
user.setPassword("7777888");
int r = userMapper.insert(user);
System.out.println(r);
}
deleteXXX删除数据
deleteBatchIds:批量删除
deleteById:根据id删除
deleteByMap:
@Test
public void testDelete(){
List<Integer> ids = new ArrayList<>();
ids.add(2);
ids.add(3);
ids.add(4);
int i = userMapper.deleteBatchIds(ids);
System.out.println(i);
}
@Test
public void testDeleteById(){
int i = userMapper.deleteById(8);
System.out.println(i);
}
@Test
public void testDeleteByMap(){
Map<String, Object> map = new HashMap<>();
map.put("name","提姆");
map.put("age",22);
int i = userMapper.deleteByMap(map);
System.out.println(i);
}
updateXXX更新数据
@Test
public void testUpdate(){
//把id为2的用户的年龄改为14
User user = new User();
user.setId(2L);
user.setAge(14);
int i = userMapper.updateById(user);
System.out.println(i);
}
(查询)条件构造器
在其子类AbstractWrapper
中提供了很多用于构造Where条件的方法。
AbstractWrapper
的子类QueryWrapper
则额外提供了用于针对Select语法的select
方法。可以用来设置查询哪些列。
AbstractWrapper
的子类UpdateWrapper
则额外提供了用于针对SET语法的set
方法。可以用来设置对哪些列进行更新。
常用AbstractWrapper方法:
eq:equals,等于
gt:greater than ,大于 >
ge:greater than or equals,大于等于≥
lt:less than,小于<
le:less than or equals,小于等于≤
between:相当于SQL中的BETWEEN
like:模糊匹配。like("name","黄"),相当于SQL的name like '%黄%'
likeRight:模糊匹配右半边。likeRight("name","黄"),相当于SQL的name like '黄%'
likeLeft:模糊匹配左半边。likeLeft("name","黄"),相当于SQL的name like '%黄'
notLike:notLike("name","黄"),相当于SQL的name not like '%黄%'
isNull
isNotNull
and:SQL连接符AND
or:SQL连接符OR
in: in(“age",{1,2,3})相当于 age in(1,2,3)
groupBy: groupBy("id","name")相当于 group by id,name
orderByAsc :orderByAsc("id","name")相当于 order by id ASC,name ASC
orderByDesc :orderByDesc ("id","name")相当于 order by id DESC,name DESC
用AbstractWrapper
的子类QueryWrapper
来实现
数据库:
示例一:大于等于
SELECT
id,user_name,PASSWORD,NAME,age,address
FROM
USER
WHERE
age > 18 AND address = '狐山'
Wrapper写法:
@Test
public void testWrapper01(){
//实例构造器
QueryWrapper wrapper = new QueryWrapper();
//构造器添加条件(属性,条件)
wrapper.gt("age",18);
wrapper.eq("address","狐山");
//使用构造器查询
List<User> users = userMapper.selectList(wrapper);
System.out.println(users);
}
示例二:范围,排序
SELECT
id,user_name,PASSWORD,NAME,age,address
FROM
USER
WHERE
id IN(1,2,3) AND
age > 10
ORDER BY
age DESC
Wrapper写法:
@Test
public void testWrapper03(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.in("id",1,2,3);
queryWrapper.gt("age",18);
queryWrapper.orderByDesc("age");
List<User> users = userMapper.selectList(queryWrapper);
System.out.println(users);
}
常用QueryWrapper方法
QueryWrapper的 select 可以设置要查询的列。
示例一:要查询的列名
select(String… sqlSelect) 方法的测试为要查询的列名
SELECT
id,user_name
FROM
USER
Wrapper写法:
@Test
public void testSelect01(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//select之后,要哪些字段,添加对应数据库列名
queryWrapper.select("id","user_name");
List<User> users = userMapper.selectList(queryWrapper);
System.out.println(users);
}
示例二:
select(Class entityClass, Predicate predicate),需要实体类对象
方法的第一个参数为实体类的字节码对象,第二个参数为Predicate类型,可以使用lambda的写法,过滤要查询的字段 (主键除外) 。
SELECT
id,user_name
FROM
USER
Wrapper写法:(对主键之外的数据)
@Test
public void testSelect02(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//实体类对象的字节码对象
queryWrapper.select(User.class, new Predicate<TableFieldInfo>() {
@Override
public boolean test(TableFieldInfo tableFieldInfo) {
return "user_name".equals(tableFieldInfo.getColumn());
}
});
List<User> users = userMapper.selectList(queryWrapper);
System.out.println(users);
}
@Test
public void testSelect02(){
//获取除主键之外,数据库字段为user_name的属性值
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select(User.class, tableFieldInfo -> "user_name".equals(tableFieldInfo.getColumn()));
List<User> users = userMapper.selectList(queryWrapper);
System.out.println(users);
}
示例三:
select(Predicate predicate),需要实体类对象
方法第一个参数为Predicate类型,可以使用lambda的写法,过滤要查询的字段 (主键除外) 。
SELECT
id,user_name,PASSWORD,NAME,age
FROM
USER
Wrapper写法:
@Test
public void testSelect03(){
//传入实体类对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>(new User());
queryWrapper.select(new Predicate<TableFieldInfo>() {
@Override
public boolean test(TableFieldInfo tableFieldInfo) {
return !"address".equals(tableFieldInfo.getColumn());
}
});
List<User> users = userMapper.selectList(queryWrapper);
System.out.println(users);
}
@Test
public void testSelect03(){
//获取除主键之外,字段名不等于address的属性值
QueryWrapper<User> queryWrapper = new QueryWrapper<>(new User());
queryWrapper.select(tableFieldInfo -> !"address".equals(tableFieldInfo.getColumn()));
List<User> users = userMapper.selectList(queryWrapper);
System.out.println(users);
}
常用UpdateWrapper方法
使用update方法时需要创建一个实体类对象传入,用来指定要更新的列及对应的值。但是如果需要更新的列比较少时,创建这么一个对象显的有点麻烦和复杂。
我们可以使用UpdateWrapper的set方法来设置要更新的列及其值。同时这种方式也可以使用Wrapper去指定更复杂的更新条件。
示例一:修改
UPDATE
USER
SET
age = 99
where
id > 1
Wrapper写法:
@Test
public void testUpdateWrapper(){
//针对更新条件较复杂的
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
//匹配条件
updateWrapper.gt("id",1);
//设置值
updateWrapper.set("age",99);
updateWrapper.set("name","张三");
userMapper.update(null,updateWrapper);
}
Lambda条件构造器
前面在使用条件构造器时列名都是用字符串的形式去指定,这种方式无法在编译期确定列名的合法性。
Lambda条件构造器可以直接以实体类的方法引用的形式来指定列名,弥补上述缺陷。
SELECT
id,user_name,PASSWORD,NAME,age,address
FROM
USER
WHERE
age > 18 AND address = '狐山'
之前构造器方式:
@Test
public void testLambdaWrapper(){
QueryWrapper<User> queryWrapper = new QueryWrapper();
queryWrapper.gt("age",18);
queryWrapper.eq("address","狐山");
List<User> users = userMapper.selectList(queryWrapper);
}
lambda构造器:
//编译期间列名合法性,万一age写错,编译期间不报错,运行期间报错
@Test
public void testLambdaWrapper2(){
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.gt(User::getAge,18);
queryWrapper.eq(User::getAddress,"狐山");
List<User> users = userMapper.selectList(queryWrapper);
}
自定义SQL
支持Mybatis的方式去自定义方法,同时也支持在使用Mybatis的自定义方法时使用MP的条件构造器帮助我们进行条件构造。
1.准备sql
CREATE TABLE `orders` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`price` int(11) DEFAULT NULL COMMENT '价格',
`remark` varchar(100) DEFAULT NULL COMMENT '备注',
`user_id` int(11) DEFAULT NULL COMMENT '用户id',
`update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
`create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`version` int(11) DEFAULT '1' COMMENT '版本',
`del_flag` int(1) DEFAULT '0' COMMENT '逻辑删除标识,0-未删除,1-已删除',
`create_by` varchar(100) DEFAULT NULL COMMENT '创建人',
`update_by` varchar(100) DEFAULT NULL COMMENT '更新人',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
/*Data for the table `orders` */
insert into `orders`(`id`,`price`,`remark`,`user_id`,`update_time`,`create_time`,`version`,`del_flag`,`create_by`,`update_by`) values (1,2000,'无',2,'2021-08-24 21:02:43','2021-08-24 21:02:46',1,0,NULL,NULL),(2,3000,'无',3,'2021-08-24 21:03:32','2021-08-24 21:03:35',1,0,NULL,NULL),(3,4000,'无',2,'2021-08-24 21:03:39','2021-08-24 21:03:41',1,0,NULL,NULL);
2.实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Orders {
private Long id;
/**
* 价格
*/
private Integer price;
/**
* 备注
*/
private String remark;
/**
* 用户id
*/
private Integer userId;
/**
* 更新时间
*/
private LocalDateTime updateTime;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 版本
*/
private Integer version;
/**
* 逻辑删除标识,0-未删除,1-已删除
*/
private Integer delFlag;
}
3.Mapper接口中定义方法
public interface UserMapper extends BaseMapper<User> {
User findMyUser(Long id);
}
4.配置xml文件的存放目录:
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
5.在xml中编写sql
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sangeng.mapper.UserMapper">
<select id="findMyUser" resultType="com.sangeng.domian.User">
select * from user where id = #{id}
</select>
</mapper>
6.测试
@Test
public void testSelect04(){
User myUser = userMapper.findMyUser(1L);
System.out.println(myUser);
}
Mybatis方式结合条件构造器
1.方法中添加Warpper类型的参数,并指定参数名(默认ew)
public interface UserMapper extends BaseMapper<User> {
User findMyUserByWrapper(@Param(Constants.WRAPPER) Wrapper<User> wrapper);
}
2.在xml中拼接sql
<select id="findMyUserByWrapper" resultType="com.suisui.entity.User">
select * from user ${ew.customSqlSegment}
</select>
分页查询
基本分页查询
1.配置分页查询拦截器
@Configuration
public class MybatisPulsConfig {
/**
* 3.4.0之前的版本
* @return
*/
/* @Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}*/
/**
* 3.4.0之后版本
* @return
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
2.进行分页查询
@Test
public void testPage(){
IPage<User> page = new Page<>();
//设置每页条数
page.setSize(2);
//设置查询第几页
page.setCurrent(1);
//mp定义好的,只能用于单表
userMapper.selectPage(page, null);
System.out.println(page.getRecords());//获取当前页的数据
System.out.println(page.getTotal());//获取总记录数
System.out.println(page.getCurrent());//当前页码
}
多表分页查询
分页配置同上,数据准备三更草堂视频
示例:查询orders表,还要求查询每个订单的下单用户的用户名。
1.在mapper接口中自定义方法,让方法接收Page对象
public interface OrdersMapper extends BaseMapper<Orders> {
IPage<Orders> findAllOrders(Page<Orders> page);
}
2.在xml中不需要关心分页操作,MP会帮我们完成
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sangeng.mapper.OrdersMapper">
<select id="findAllOrders" resultType="com.sangeng.domian.Orders">
SELECT
o.*,u.`user_name`
FROM
USER u,orders o
WHERE
o.`user_id` = u.`id`
</select>
</mapper>
3.调用方法测试
@Autowired
private OrdersMapper ordersMapper;
@Test
public void testOrdersPage(){
Page<Orders> page = new Page<>();
//设置每页大小
page.setSize(2);
//设置当前页码
page.setCurrent(2);
ordersMapper.findAllOrders(page);
System.out.println(page.getRecords());
System.out.println(page.getTotal());
}
Service接口
Service层更多支持批量操作
1.接口extends Iservice<实体类>
public interface UserService extends IService<User> {
User test();
}
2.实现类extends ServiceImpl<Mapper接口,实体类> implements 接口
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {
//获取其他类的mapper接口
@Autowired
private OrdersMapper ordersMapper;
@Override
public User test() {
//获取自己的mapper接口
UserMapper userMapper = getBaseMapper();
List<Orders> orders = ordersMapper.selectList(null);
User user = userMapper.selectById(3);
//查询用户对于的订单
QueryWrapper<Orders> wrapper = new QueryWrapper<>();
wrapper.eq("user_id",3);
List<Orders> ordersList = ordersMapper.selectList(wrapper);
return user;
}
}
代码生成器
一键生成实体类,Mapper接口,Service,Controller等全套代码
1.添加依赖
<!--mybatisplus代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!--模板引擎-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
2.编写类,修改相应配置
public class GeneratorTest {
@Test
public void generate() {
AutoGenerator generator = new AutoGenerator();
// 全局配置
GlobalConfig config = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
// 设置输出到的目录
config.setOutputDir(projectPath + "/src/main/java");
config.setAuthor("sangeng");
// 生成结束后是否打开文件夹
config.setOpen(false);
// 全局配置添加到 generator 上
generator.setGlobalConfig(config);
// 数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/mp?characterEncoding=utf-8&serverTimezone=UTC");
dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
dataSourceConfig.setUsername("root");
dataSourceConfig.setPassword("12345678");
// 数据源配置添加到 generator
generator.setDataSource(dataSourceConfig);
// 包配置, 生成的代码放在哪个包下
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.sangeng.mp.generator");
// 包配置添加到 generator
generator.setPackageInfo(packageConfig);
// 策略配置
StrategyConfig strategyConfig = new StrategyConfig();
// 下划线驼峰命名转换
strategyConfig.setNaming(NamingStrategy.underline_to_camel);
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
// 开启lombok
strategyConfig.setEntityLombokModel(true);
// 开启RestController
strategyConfig.setRestControllerStyle(true);
generator.setStrategy(strategyConfig);
generator.setTemplateEngine(new FreemarkerTemplateEngine());
// 开始生成
generator.execute();
}
}
文件位置及日志打印:
自动填充(创建时间创建用户)
1.在实体类的对应字段上增加注解
/**
* 更新时间,在修改的时候自动填充
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 创建时间,在新增的时候自动填充
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
2.自定义填充处理器,implements MetaObjectHandler重写方法
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
String format = "YYYY-MM-dd HH:mm:ss";//24小时制,hh12小时制
@Override
public void insertFill(MetaObject metaObject) {
Long userId = null;
//注册时,还没有token
try {
userId = SecurityUtils.getUserId();
} catch (Exception e) {
e.printStackTrace();
userId = -1L;//表示是自己创建
}
this.setFieldValByName("createTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern(format)), metaObject);
this.setFieldValByName("createBy",userId , metaObject);
this.setFieldValByName("updateTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern(format)), metaObject);
this.setFieldValByName("updateBy", userId, metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern(format)), metaObject);
this.setFieldValByName(" ", SecurityUtils.getUserId(), metaObject);
}
}
注意数据库与实体类,数据类型保持一致
逻辑删除
3.3.0版本之前需要在对应字段上加@TableLogic
注解
mybatis-plus:
global-config:
db-config:
logic-delete-field: delFlag
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
代码测试:
实体类属性:
/**
* 逻辑删除标识,0-未删除,1-已删除
*/
private Integer delFlag;
测试删除:
@Test
public void testDeleteById(){
int i = ordersMapper.deleteById(3);
System.out.println(i);
}
测试查询:
@Test
public void testDelete02(){
ordersMapper.selectList(null);
}
乐观锁
乐观锁就是先加上不存在并发冲突问题,在进行实际数据操作的时候再检查是否冲突。
在表中增加一个version列,用来记录每条记录操作的版本。每次对每条记录进行操作时,对应的版本也需要+1。
如果在查询老版本号到更新操作的中间时刻有其他人更新了这条数据,这次更新语句就会更新失败。
1.配置对应插件
@Configuration
public class MybatisPlusConfig {
/**
* 旧版
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
/**
* 新版
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
2.在实体类的字段上加@Version注解
@Version
private Integer version;
3.更新:在更新前一定要先查询到version设置到实体类上再进行更新才能生效
@Test
public void testVersion(){
//查询id为3的数据
QueryWrapper<Orders> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id",3);
Orders orders = ordersMapper.selectOne(queryWrapper);
//对id为3的数据进行更新 把price修改为88
orders.setPrice(88);
ordersMapper.updateById(orders);
}
多插件配置问题
需要用到多个插件的话要注意,在配置的时候只需要注入一个MybatisPlusInterceptor 对象,把插件对象添加到MybatisPlusInterceptor 对象中即可。
顺序:
- 多租户,动态表明
- 分页,乐观锁
- sql性能规范,防止全表更新与删除
- 对sql进行单次改造的优先放入,不对sql进行改造的最后放入
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}