在使用mybatis-plus的时候,我们需要对某条数据的单一字段进行操作,又不想查出整条数据拿到字段值再加一赋值,此时可以用下面5种方式来实现。
方式一:setSql
官网文档Mybatis-Plus:setSql
官方文档示例:
i.标准setSql
使用setSql
实现字段自增
Wrappers.update(Article).setSql("`read_count` = `read_count` + 1");
缺陷: 如果自增字段名变更时, 需要同步修改
setSql
的字段字符串.
ii.增强setSql(推荐)
为了解决
setSql
硬编码问题,联想到参照MyBatis-Plus的设计从lambda方法引用中获取字段对应的数据库字段名, 参照主要源码如下:
com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper#columnToString(com.baomidou.mybatisplus.core.toolkit.support.SFunction<T,?>)
com.baomidou.mybatisplus.core.toolkit.LambdaUtils#resolve
com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper#getColumn
String column = BlueUtil.columnToUnderline(Article::getReadCount);
Wrappers.lambdaUpdate(Article).setSql(CharSequenceUtil.isNotBlank(column), String.format("`%s` = `%s` + 1", column, column));
- 根据lambda方法引用获取属性名
public static <T> String columnToString(SFunction<T, ?> func) {
// 根据lambda方法引用获取SerializedLambda
SerializedLambda lambda = com.baomidou.mybatisplus.core.toolkit.LambdaUtils.resolve(func);
// 根据SerializedLambda获取方法名,然后截取出属性名
return PropertyNamer.methodToProperty(lambda.getImplMethodName());
}
- 根据lambda方法引用获取字段名
public static <T> String columnToUnderline(SFunction<T, ?> func) {
String fieldName = InternalUtil.tryCatch(func, BlueUtil::columnToString);
return Optional.ofNullable(fieldName).map(CharSequenceUtil::toUnderlineCase).orElse(CharSequenceUtil.EMPTY);
}
- BlueUtil.tryCatch
public static <T, R> R tryCatch(T t, Function<T, R> function) {
TimeInterval timer = DateUtil.timer();
R r = null;
try {
r = function.apply(t);
} catch (Exception e) {
log.error(format("内部方法Function调用异常,错误信息:{}", e.getMessage()), e);
}
log.info("内部方法Function调用,耗时:{}ms", timer.interval());
return r;
}
方式二:MyBatis-Plus update 时 column=column+1(@TableField)
官网问答如何 update 时 column=column+1
i.update 时 column=column+1
使用@TableField
实现字段自增
@TableField(update = "%s+1", updateStrategy = FieldStrategy.IGNORED)
private Integer readCount;
baseMapper.update(
new Article().setUpdateTime(LocalDateTime.now()),
Wrappers.<Article>lambdaUpdate().eq(Article::getId, 123)
)
注意: 该方式绑定在
entity
上,baseMapper提供的update(entity,updateWrapper)
中的entity
不能null,而且所有的update
方法均不能再改变此值为字段实际的指定值
缺陷: 该baseMapper所有使用这种方式进行更新表操作时, 都会使readCount字段加一; 如果有些更新操作不希望更新readCount时, 要考虑使用其他方式.
ii.乐观锁
使用@TableField
实现字段自增(参考上面),在update时给该字段加where限制条件
baseMapper.update(
new Article().setUpdateTime(LocalDateTime.now()),
Wrappers.<Article>lambdaUpdate().eq(Article::getReadCount, 0).eq(Article::getId, 123)
)
方式三:乐观锁(推荐)
官方文档Mybatis-Plus 乐观锁
Mybatis-Plus基于@Version注解的乐观锁实现
使用@Version
实现字段自增
- 实例化
OptimisticLockerInnerInterceptor
,并添加到MyBatis-Plus
的拦截器链中;
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
- 在
readCount
字段加上@Version
注解。
@Version
private Integer readCount;
注意:
- 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
- 整数类型下
newVersion = oldVersion + 1
newVersion
会回写到entity
中- 仅支持
updateById(id)
与update(entity, wrapper)
方法entity
的版本字段必须有值,否则不会生成乐观锁SQL- 在
update(entity, wrapper)
方法下,wrapper
不能复用!!!
MyBatis-Plus开启SQL日志
官网文档启动 mybatis 本身的 log 日志
mybatis-plus.configuration.log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
参考文档
Mybatis-Puls条件构造器setSql
Mybatis-Puls如何实现字段自增1
Mybatis-Puls如何 update 时 column=column+1
Mybatis-Plus 乐观锁
Mybatis-Plus 乐观锁
Mybatis-Plus基于@Version注解的乐观锁实现
启动 mybatis 本身的 log 日志