在Mybatis-Plus官网当中并没有对于update进行针对性的详细讲解以及其使用,很多初级小白都用不明白,包括我有时候都迷迷糊糊的,基于这个问题我也是下定决心好好整理一篇。本篇文章重点是通过多个案例来进行讲解,每一个案例执行出来的sql我都会放到文章当中,方便大家快速掌握并使用!
在实际开发当中我们一般都是将service的接口继承IService
,然后serviceImpl继承ServiceImpl
,mapper接口继承BaseMapper
BaseMapper相对来说方法比较少,一般都是通过service接口或者serviceImpl来使用,其update的相关方法有如下:
update方法主要有以下:
// 返回UpdateChainWrapper,主要用来链式调用
UpdateChainWrapper update();
// 返回LambdaUpdateChainWrapper ,同样是主要用来链式调用,只不过是基于Lambda的
LambdaUpdateChainWrapper lambdaUpdate();
// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
boolean update(Wrapper<T> updateWrapper);
// 根据 whereWrapper 条件,更新记录
boolean update(T updateEntity, Wrapper<T> whereWrapper);
// 根据 ID 选择修改
boolean updateById(T entity);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList, int batchSize);
参数说明
Mybatis-Plus给我们提供了4个关于Java修改的条件构造器,这四个构造器的顶级父类都是Wrapper
UpdateWrapper<User> objectUpdateWrapper = new UpdateWrapper<>();
UpdateWrapper<User> update1 = Wrappers.update();
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper();
LambdaUpdateWrapper<User> updateWrapper = new UpdateWrapper().lambda();
LambdaUpdateWrapper<User> objectLambdaUpdateWrapper = Wrappers.lambdaUpdate();
@Resource
private UserService userService;
@Resource
private UserMapper userMapper;
@Test
void contextLoads() {
UpdateChainWrapper<User> update = userService.update();
UpdateChainWrapper<User> userUpdateChainWrapper = new UpdateChainWrapper<>(User.class);
UpdateChainWrapper<User> userUpdateChainWrapper1 = new UpdateChainWrapper<>(userMapper);
}
LambdaUpdateChainWrapper userLambdaUpdateChainWrapper = userService.lambdaUpdate();
UpdateChainWrapper和LambdaUpdateChainWrapper都继承了AbstractChainWrapper,而UpdateWrapper和LambdaUpdateWrapper都继承了AbstractWrapper。AbstractChainWrapper和AbstractWrapper都继承了Wrapper。
在上面我们也了解到了Mybatis-Plus给我们提供了好几个update方法,并且传参都离不开Wrapper,下面我们将通过不同的Wrapper和不同的方法进行结合起来,彻底了解清楚什么时候该用什么方法!
UpdateWrapper当中主要这四个方法用的比较多一点
set(String column, Object val)
set(boolean condition, String column, Object val)
setSql(String sql)
setSql(boolean condition, String sql)
参数解释:
类型 | 参数名 | 描述 |
---|---|---|
String | column | 数据库当中的字段名称 |
Object | val | 需要修改的数据 |
boolean | condition | 判断条件,控制该sql是否用到条件当中,假如为true代表生效 |
String | sql | 拼接sql |
(1)使用场景一:
UpdateWrapper<User> updateWrapper = Wrappers.update();
// 修改表中name字段为指定的数据
updateWrapper.set("name", "123");
// 假如age不等于空,那么我就将age修改为我指定的参数
Integer age = 66;
updateWrapper.set(age != null, "age", age);
updateWrapper.set("email", "[email protected]");
// 修改条件为id=5的数据
updateWrapper.eq("id", 5);
// 假如修改失败返回值为false
boolean update = userService.update(updateWrapper);
日志打印出来的sql:
UPDATE user SET name='123',age=66,email='[email protected]' WHERE (id = 5)
使用 boolean update(Wrapper
修改,他本质上就是调用的update(T updateEntity, Wrapper
,只不过Entity传的为null!
(2)使用场景二:把所有set值全去掉,只使用UpdateWrapper作为修改的条件,然后通过Entity赋值
UpdateWrapper<User> updateWrapper = Wrappers.update();
// 修改条件为id=5的数据
updateWrapper.eq("id", 5);
User user = new User();
user.setName("wdawdwa");
user.setAge(888);
user.setEmail("[email protected]");
// 假如修改失败返回值为false
boolean update = userService.update(user, updateWrapper);
日志打印出来的sql:
UPDATE user SET name='wdawdwa', age=888, email='[email protected]' WHERE (id = 5)
(3)Entity之和UpdateWrapper两种赋值方式同时使用:
接下来我们进行测试看看boolean update(T updateEntity, Wrapper
假如同时使用两种赋值方式会是什么样的。
UpdateWrapper<User> updateWrapper = Wrappers.update();
// 修改表中name字段为指定的数据
updateWrapper.set("name", "123");
// 假如age不等于空,那么我就将age修改为我指定的参数
Integer age = 66;
updateWrapper.set(age != null, "age", age);
updateWrapper.set("email", "[email protected]");
// 修改条件为id=5的数据
updateWrapper.eq("id", 5);
User user = new User();
user.setName("wdawdwa");
user.setEmail("[email protected]");
// 假如修改失败返回值为false
boolean update = userService.update(user, updateWrapper);
日志打印出来的sql:
UPDATE user SET name='wdawdwa', age=0, email='[email protected]', name='123222',age=66 WHERE (id = 5)
得出结论:
(4)现在有一个疑问需要验证一下,new出来的user并没有对email字段赋值,那么他会不会将数据库当中的email赋值为null。
下面我们进行验证,我user表有好几个字段,我只修改了两个!
UpdateWrapper<User> updateWrapper = Wrappers.update();
updateWrapper.eq("id", 5);
User user = new User();
user.setName("wdawdwa");
user.setAge(888);
boolean update = userService.update(user, updateWrapper);
日志打印出来的sql:
UPDATE user SET name='wdawdwa', age=888 WHERE (id = 5)
得出结论:
(5)本质上假如我们不使用UpdateWrapper的set相关方法的话,其实也可以直接使用QueryWrapper的,如下:
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
updateWrapper.eq("id", 5);
User user = new User();
user.setName("wdawdwa");
user.setAge(888);
user.setEmail("[email protected]");
boolean update = userService.update(user, objectQueryWrapper);
日志打印出来的sql同时用UpdateWrapper是一样的:
UPDATE user SET name='wdawdwa', age=0 WHERE (id = 5)
UpdateWrapper和QueryWrapper都继承了AbstractWrapper,AbstractWrapper主要用于生成 sql 的 where 条件
,AbstractWrapper又继承了Wrapper。他两个唯一的区别是:
(6)假如我们不使用Entity也不使用UpdateWrapper的set,那sql打印出来是什么呢?
UpdateWrapper<User> updateWrapper = Wrappers.update();
// 修改条件为id=5的数据
updateWrapper.eq("id", 5);
// 假如修改失败返回值为false
boolean update = userService.update(null, updateWrapper);
运行结果:
得出结论:
UpdateWrapper和LambdaUpdateWrapper其实是一样的,都能够实现彼此的功能,只是语法不同,LambdaUpdateWrapper所有的指定column参数不再是字符串,而是SFunction函数。
(1)使用场景:
LambdaUpdateWrapper<User> objectLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
// 修改条件为id=5的数据
objectLambdaUpdateWrapper.eq(User::getId, 5);
objectLambdaUpdateWrapper.set(User::getAge, 22);
objectLambdaUpdateWrapper.set(User::getName, "zhangsan");
objectLambdaUpdateWrapper.setSql("email='[email protected]'");
User user = new User();
user.setAge(666);
boolean update = userService.update(user, objectLambdaUpdateWrapper);
日志打印出来的sql:
UPDATE user SET age=666, age=22,name='zhangsan',email='[email protected]' WHERE (id = 5)
(2)原理讲解
SFunction其实就是继承了Function函数。而Function是一个函数式(Functional)接口
Function三种使用方法:
public static void main(String[] args) {
// 匿名内部类
Function function = new Function<User, Object>() {
@Override
public Object apply(User user) {
return user.getAge();
}
};
// lambda
Function<User, Object> function1 = (a) -> a.getAge();
// 方法引用
Function<User, Object> function2 = User::getAge;
}
核心源码: 感兴趣的可以断点观察一下源码
(3)假如我们的get方法和表中的字段不对应,是否会出问题?
LambdaUpdateWrapper<User> objectLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
// 修改条件为id=5的数据
objectLambdaUpdateWrapper.eq(User::getId, 5);
objectLambdaUpdateWrapper.set(User::getSexaa, "男");
boolean update = userService.update(objectLambdaUpdateWrapper);
日志打印出来的sql:
UPDATE user SET sex='男' WHERE (id = 5)
得出结论:
(1)使用场景一:
// UpdateChainWrapper有三种创建方式
// UpdateChainWrapper userUpdateChainWrapper = new UpdateChainWrapper<>(User.class);
// UpdateChainWrapper update = new UpdateChainWrapper<>(userMapper);
User user = new User();
user.setAge(222);
UpdateChainWrapper<User> updateChainWrapper = userService.update();
updateChainWrapper.eq("id", 5)
.set("name", "张三")
.setSql("email='[email protected]'")
.setEntity(user)
.update();
日志打印出来的sql:
UPDATE user SET name='张三',email='[email protected]' WHERE age=222 AND (id = 5)
(2)使用场景二:
User user = new User();
user.setAge(222);
UpdateChainWrapper<User> updateChainWrapper = userService.update();
updateChainWrapper.eq("id", 5)
.update(user);
日志打印出来的sql:
UPDATE user SET age=222 WHERE (id = 5)
LambdaUpdateChainWrapper同UpdateChainWrapper一模一样,只不过参数是函数式表达式,这里就不再过多叙述了。
// LambdaUpdateChainWrapper有三种创建方式
// LambdaUpdateChainWrapper userUpdateChainWrapper = new LambdaUpdateChainWrapper<>(User.class);
// LambdaUpdateChainWrapper update = new LambdaUpdateChainWrapper<>(userMapper);
User user = new User();
user.setAge(222);
LambdaUpdateChainWrapper<User> userLambdaUpdateChainWrapper = userService.lambdaUpdate();
userLambdaUpdateChainWrapper.eq(User::getId,5)
.update(user);
日志打印出来的sql:
UPDATE user SET age=222 WHERE (id = 5)
(1)updateById 使用场景:
在调用updateById方法前,需要在T entity(对应的实体类)中的主键属性上加上@TableId注解。
User user = new User();
user.setId(new Long(5));
user.setAge(555);
user.setEmail(null);
userService.updateById(user);
日志打印出来的sql:
UPDATE user SET age=555 WHERE id=5
(2)updateBatchById 使用场景:
User user = new User();
user.setId(new Long(5));
user.setAge(555);
user.setEmail(null);
User user1 = new User();
user1.setId(new Long(4));
user1.setAge(666);
user1.setEmail(null);
List<User> objects = new ArrayList<>();
objects.add(user);
objects.add(user1);
boolean b = userService.updateBatchById(objects);
日志打印出来的sql:
UPDATE user SET age=555 WHERE id=5
UPDATE user SET age=666 WHERE id=4
Mybatis-Plus给我们提供的update根本没有全量修改的,都是只对不为空的进行修改,例如updateById和update使用Entity赋值的方法都是只对不为null的进行赋值修改。假如遇到需要将某个值赋为null这时候该怎么办?
方式一:在mapper.xml中写对应的sql语句
方式二:实体类对应字段添加注解
@TableField注解介绍:
可以聚合三种进行同时使用:
@TableField(insertStrategy = FieldStrategy.IGNORED,updateStrategy = FieldStrategy.IGNORED,whereStrategy = FieldStrategy.IGNORED)
说明 FieldStrategy 的可选值:
在email字段中设置注解,加上该注解之后,Mybatis-plus会在对应的情况下生成sql时忽略掉该字段值的判断;即该字段值不论是什么都会生成在sql中,就可以设置字段值为null了;
@TableField(value = "email", updateStrategy = FieldStrategy.IGNORED)
测试添加注解后的效果:
User user = new User();
user.setId(new Long(5));
user.setAge(555);
user.setEmail(null);
userService.updateById(user);
日志打印出来的sql:
UPDATE user SET age=555, email=null WHERE id=5
方式三:通过条件构造器的set进行赋值
UpdateWrapper<User> updateWrapper = Wrappers.update();
updateWrapper.set("name", null);
updateWrapper.eq("id", 5);
boolean update = userService.update(updateWrapper);
方式四:Mybatis-plus 全局参数配置(yaml方式配置如下)
全局配置的值可选项和第二种方式 FieldStrategy 的可选项一致,全局配置默认值为 not_null
mybatis-plus:
global-config:
db-config:
insert-strategy: ignored
update-strategy: ignored
select-strategy: ignored
这种方式和第二种一样可能会使用不当导致字段值为null,数据丢失;并且该方式是全局配置,对所有表的实体类所有字段都生效,危害会更严重;如果同时配置了第二种,那么优先第二种生效。
saveOrUpdate方法主要有以下:
// TableId 注解存在更新记录,否插入一条记录
boolean saveOrUpdate(T entity);
// 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
参数说明:
下面进行一个一个讲解
方法一:
boolean saveOrUpdate(T entity);
这个方法我就不演示了哈,通过源码已经可以看的一清二楚了,如下:
方法二:
default boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper) {
return this.update(entity, updateWrapper) || this.saveOrUpdate(entity);
}
User user = new User();
user.setId(new Long(5));
user.setAge(555);
user.setEmail(null);
UpdateWrapper<User> updateWrapper = Wrappers.update();
updateWrapper.set("name", "123");
boolean b = userService.saveOrUpdate(user, updateWrapper);
日志打印出来的sql:
我把user.setId(new Long(5));这一行去掉之后也是打印出来的下面的sql
UPDATE user SET age=555, name='123'
通过上面示例好像说明不了其作用,我们再来看一个示例:
User user = new User();
user.setAge(555);
user.setEmail(null);
UpdateWrapper<User> updateWrapper = Wrappers.update();
// 修改表中name字段为指定的数据
updateWrapper.set("name", "123");
// 假如不存在id为5的数据,这时候会进行新增
updateWrapper.eq("id",5);
boolean b = userService.saveOrUpdate(user, updateWrapper);
日志打印出来的sql:
UPDATE user SET age=555, name='123' WHERE (id = 5)
假如不存在id为5的数据,这时候打印出来的sql:
UPDATE user SET age=555, name='123' WHERE (id = 6)
INSERT INTO user ( id, age ) VALUES ( 1668194662817099777, 555 )
得出结论:
方法三:
default boolean saveOrUpdateBatch(Collection<T> entityList) {
return this.saveOrUpdateBatch(entityList, 1000);
}
测试代码:数据库当中只有id为5的数据,没有id为6的数据
User user = new User();
user.setId(new Long(5));
user.setAge(555);
user.setEmail(null);
User user1 = new User();
user1.setId(new Long(6));
user1.setAge(666);
user1.setEmail(null);
List<User> objects = new ArrayList<>();
objects.add(user);
objects.add(user1);
boolean b = userService.saveOrUpdateBatch(objects);
System.out.println("11111111111111111111");
System.out.println(b);
日志打印出来的sql:
SELECT id,name,age,email FROM user WHERE id=5
UPDATE user SET age=555 WHERE id=5
SELECT id,name,age,email FROM user WHERE id=6
INSERT INTO user ( id, age ) VALUES ( 6, 666 )
得出结论:
方法四:
其实方法三就是掉用的方法四,设置了batchSize 为1000。 batchSize代表的是插入批次数量。
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
如果大数据量(例如五千、一万)插入建议修改MySQL的JDBC连接的url为如下,开启rewriteBatchedStatements,可以优化插入速度。
url: jdbc:mysql://127.0.0.1:3306/text1?characterEncoding=UTF-8&useSSL=false&rewriteBatchedStatements=true
在使用Mybatis-Plus的时候如果对API不是特别了解很容易导致重大事故,就拿下面的代码来说:
User user = new User();
user.setId(new Long(5));
user.setAge(555);
user.setEmail(null);
UpdateWrapper<User> updateWrapper = Wrappers.update();
updateWrapper.set("name", "123");
boolean b = userService.saveOrUpdate(user, updateWrapper);
日志打印出来的sql:
UPDATE user SET age=555, name='123'
本来想着只修改id为5的数据,却不料最终执行的sql连where条件都没有,导致把整张表全给修改了。针对于这个问题我们可以添加如下配置进行防范:
这是官网给我们提供的:https://baomidou.com/pages/c571bc/#blockattackinnerinterceptor
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
}
然后这时候再执行sql会发现直接就异常了,原因是没有where条件!