用tk.mybaits实现指定字段更新
去年年底的因为业务需要需要在使用tk.mybaits框架的系统中实现指定字段的更新,可是tk.mybaits框架本身并不支持这个功能,我翻遍了CSDN和其他相关的技术相关的网站都没有找到相关的解决方法。于是我通过几天的翻阅相关资料和摸索后终于实现了这个功能。最近事情不是很多,想到又想到了去年解决的这个问题,于是有了这篇文章。分享一下当时的解决方法,为有同样需求的小伙伴抛砖引玉。
这个问题如果系统使用的是Mybatis-Plus框架的话,实现只更新指定的字段是一件很简单的事情。只需要写以下代码即可:
```java
UpdateWrapper updateWrapper = new UpdateWrapper();
updateWrapper.eq("id", 1);
updateWrapper.set("status", 1);
updateWrapper.set("nickname", "张三");
baseMapper.update(null, updateWrapper);
```
但是如果系统使用的是tk.mybaits则默认不支持这个功能,有一种迂回的解决的办法是现查询出对应的数据,在set需要更新的字段,再更新整条数据。但是这样终究不是很好,在并发情况下也很容易出现问题。至于tk.mybaits指定字段更新的方案,我在网上找了很久都没有找到相关的解决方法。随后我通过分析tk.mybaits在github的源码和issue回答找到些许线索。最终实现了这个功能。具体结局方案如下:
先定义一个名为UpdateAppointColumnMapper接口
```java
/**
* @author: jie
* @create: 2022/11/15 10:55
* @description: 通用Mapper接口,更新指定字段,实现
*/
@tk.mybatis.mapper.annotation.RegisterMapper
public interface UpdateAppointColumnMapper<T> {
/**
* 根据Example条件更新实体`record`包含的不是null的属性值
*
* @param record
* @param example
* @return
*/
@UpdateProvider(type = UpdateByExampleAppointColumnProvider.class, method = "dynamicSQL")
int updateByExampleAppointColumn(@Param("record") T record, @Param("example") Object example,@Param("updateColumns") List<String> updateColumns);
//
// @UpdateProvider(type = UpdateByExampleAppointColumnExampleProvider2.class, method = "updateByExampleAppointColumn")
// int updateByExampleAppointColumn2(@Param("record") T record, @Param("example") Object example,@Param("updateColumns") List<String> updateColumns);
/**
* 根据Example条件更新实体`record`包含的不是null的属性值
*
* @param record
* @param example
* @return
*/
@UpdateProvider(type = UpdateByExampleAppointColumnProvider.class, method = "dynamicSQL")
int updateByExampleAppointColumnForMap(@Param("record") Map record, @Param("example") Object example, @Param("updateColumns") List<String> updateColumns);
}
```
再定义一个名为UpdateByExampleAppointColumnProvider的Provider(核心代码)
```java
/**
* @author: jie
* @create: 2022/11/16 19:00
* @description:
*/
public class UpdateByExampleAppointColumnProvider extends MapperTemplate {
public static final String UPDATE_PROPERTIES = "updateColumns";
public UpdateByExampleAppointColumnProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
super(mapperClass, mapperHelper);
}
/**
* 根据Example更新非null字段
*
* @param ms
* @return
*/
public String updateByExampleAppointColumn(MappedStatement ms) {
Class<?> entityClass = getEntityClass(ms);
ParameterMap paramterType = ms.getParameterMap();
Configuration configuration = ms.getConfiguration();
MetaObject metaObject = MetaObjectUtil.forObject(ms);
SqlSource sqlSource = ms.getSqlSource();
//BoundSql boundSql = sqlSource.getBoundSql(entityClass);
StringBuilder sql = new StringBuilder();
if (isCheckExampleEntityClass()) {
sql.append(SqlHelper.exampleCheck(entityClass));
}
//安全更新,Example 必须包含条件
if (getConfig().isSafeUpdate()) {
sql.append(SqlHelper.exampleHasAtLeastOneCriteriaCheck("example"));
}
sql.append(SqlHelper.updateTable(entityClass, tableName(entityClass), "record"));
//sql.append(sqlUtil.updateSetAppointColumns(entityClass, Arrays.asList("before_value")));
sql.append(this.updateSetColumnsForce(entityClass, "record", true, isNotEmpty()));
sql.append(SqlHelper.updateByExampleWhereClause());
//sql.append(SqlHelper.wherePKColumns(entityClass, "record", true));
return sql.toString();
}
public String updateByExampleAppointColumnForMap(MappedStatement ms){
return updateByExampleAppointColumn(ms);
}
/**
* update set列
*
* @param entityClass
* @param entityName 实体映射名
* @param notNull 是否判断!=null
* @param notEmpty 是否判断String类型!=''
* @return
*/
public String updateSetColumnsForce(Class<?> entityClass, String entityName, boolean notNull, boolean notEmpty) {
StringBuilder sql = new StringBuilder();
sql.append("<set>");
//获取全部列
Set<EntityColumn> columnSet = EntityHelper.getColumns(entityClass);
//对乐观锁的支持
EntityColumn versionColumn = null;
//当某个列有主键策略时,不需要考虑他的属性是否为空,因为如果为空,一定会根据主键策略给他生成一个值
for (EntityColumn column : columnSet) {
if (column.getEntityField().isAnnotationPresent(Version.class)) {
if (versionColumn != null) {
throw new VersionException(entityClass.getCanonicalName() + " 中包含多个带有 @Version 注解的字段,一个类中只能存在一个带有 @Version 注解的字段!");
}
versionColumn = column;
}
if (!column.isId() && column.isUpdatable()) {
if (column == versionColumn) {
Version version = versionColumn.getEntityField().getAnnotation(Version.class);
String versionClass = version.nextVersion().getCanonicalName();
//version = ${@tk.mybatis.mapper.version@nextVersionClass("versionClass", version)}
sql.append(column.getColumn())
.append(" = ${@tk.mybatis.mapper.version.VersionUtil@nextVersion(")
.append("@").append(versionClass).append("@class, ")
.append(column.getProperty()).append(")},");
} else if (notNull) {
sql.append(this.getIfNotNull(entityName, column, column.getColumnEqualsHolder(entityName) + ",", notEmpty));
} else {
sql.append(column.getColumnEqualsHolder(entityName) + ",");
}
}
}
sql.append("</set>");
return sql.toString();
}
/**
* 判断自动!=null的条件结构
*
* @param entityName
* @param column
* @param contents
* @param empty
* @return
*/
public String getIfNotNull(String entityName, EntityColumn column, String contents, boolean empty) {
StringBuilder sql = new StringBuilder();
sql.append("<choose>");
//指定的字段会被强制更新
sql.append("<when test=\"");
sql.append(UPDATE_PROPERTIES).append(" != null and ").append(UPDATE_PROPERTIES).append(".contains('");
sql.append(column.getProperty());
sql.append("')\">");
sql.append(contents);
sql.append("</when>");
sql.append("<otherwise></otherwise>");
sql.append("</choose>");
return sql.toString();
}
}
```
需要实现指定字段更新的Mapper需要继承UpdateAppointColumnMapper,我是直接定义了一个名为BaseMapper的基类,让基类直接继承
```java
public interface BaseMapper<T> extends Mapper<T>,UpdateAppointColumnMapper<T> {}
```
一个数据仓储的基类
```java
public abstract class AbstractBaseRepository<T> {
@Autowired
protected IBaseMapper<T> baseMapper;
/**
* 更新指定字段
*
* @param record 更新对象
* @param example 更新条件
* @param updateColumns 需要更新的字段
* @return
*/
public void updateByExampleAppointColumn(T record, Example example, List<String> updateColumns, LoginInfo loginInfo){
record.setUpdateUser(loginInfo.getUserId());
this.baseMapper.updateByExampleAppointColumn(record, example, updateColumns);
}
/**
* 根据Map更新指定字段
*
* @param map map 更新Map
* @param example 更新条件
* @return void
*/
public void updateByExampleAppointColumn(Map<String, Object> map, Example example, LoginInfo loginInfo){
if(MapUtils.isEmpty(map)){
throw new BusinessException("更新的字段必填");
}
List<String> updateColumns = map.keySet().stream().collect(Collectors.toList());
map.put("updateUser", loginInfo.getUserId());
this.baseMapper.updateByExampleAppointColumnForMap(map, example, updateColumns);
}
/**
* 根据Map更新指定字段
*
* @param lambdaUpdateWrapper lambdaUpdateWrapper
* @param example 更新条件
* @return void
*/
public void updateByExampleAppointColumn(LambdaUpdateWrapper lambdaUpdateWrapper, Example example, LoginInfo loginInfo){
Map<String, Object> map = lambdaUpdateWrapper.build();
this.updateByExampleAppointColumn(map, example, loginInfo);
}
}
```
LambdaUpdateWrapper:
```java
/**
* @author: jie
* @create: 2022/11/17 14:18
* @description:
*/
public class LambdaUpdateWrapper {
private Map<String, Object> updateMap;
public static LambdaUpdateWrapper create() {
return new LambdaUpdateWrapper();
}
public <T, R> LambdaUpdateWrapper set(ColumnFunction<T, R> column, Object value) {
return set(FieldUtil.name(column), value);
}
public LambdaUpdateWrapper set(String columnName, Object value) {
this.updateMap.put(columnName, value);
return this;
}
public Map<String, Object> build() {
return this.updateMap;
}
}
```
实例代码:
### (1)使用实体对象更新
```java
public void updateByExampleAppointColumnEntityTest(){
//更新的对象
Event event = Event.builder().beforeValue("1").afterValue("2").description(null).build();
//需要更新的字段
List<String> updateColumns = Arrays.asList("beforeValue","afterValue","description");
//指定更新条件
Example example = Example.builder(Event.class)
.andWhere(WeekendSqls.custom()
.andEqualTo("bizId", "123")
.andEqualTo("active", CommonYesNoEnum.YES.getCode())).build();
updateByExampleAppointColumn(event, example, updateColumns);
}
```
### (2)使用 MAP 更新
```java
public void updateByExampleAppointColumnMapTest(){
//更新的MAP
Map<String, Object> map = new HashMap<>();
map.put("beforeValue","1");
map.put("afterValue","2");
map.put("description",null);
//指定更新条件
Example example = Example.builder(Event.class)
.andWhere(WeekendSqls.custom()
.andEqualTo("bizId", "123")
.andEqualTo("active", CommonYesNoEnum.YES.getCode())).build();
updateByExampleAppointColumnForMap(map, example);
}
```
### (3) 使用 LambdaUpdateWrapper 更新
```java
oid updateByExampleAppointColumnLambdaUpdateWrapperTest() {
//更新的MAP
LambdaUpdateWrapper lambdaUpdateWrapper = LambdaUpdateWrapper.create()
.set(Event::getBeforeValue, "1")
.set(Event::getAfterValue, "1")
.set(Event::getDescription, null);
//指定更新条件
Example example = Example.builder(Event.class)
.andWhere(WeekendSqls.custom()
.andEqualTo("bizId", "123")
.andEqualTo("active", CommonYesNoEnum.YES.getCode())).build();
eventRepository.updateByExampleAppointColumn(lambdaUpdateWrapper, example);
}
```
用tk.mybaits实现指定字段更新的更多相关文章
- Django笔记十八之save函数的继承操作和指定字段更新等实例方法
本文首发于微信公众号:Hunter后端 原文链接:Django笔记十八之save函数的继承操作和指定字段更新等实例方法 这篇笔记主要介绍 Django 一些实例方法. 什么是 实例,我们知道通过fil ...
- Entity Framework 同一个上下文中,如何进行对同一个实体进行指定字段更新
转自 http://www.cnblogs.com/flyfish2012/archive/2013/03/13/2957125.html 我在上一篇EF更新指定的字段当中介绍了,如何在EF指定字段进 ...
- Solr局部或指定字段更新之set用法
solr wiki文档也有 http://yonik.com/solr/atomic-updates/ java code public static void up ...
- Entity Framework中实现指定字段更新
foreach (var entity in databasePatents) { var patentTmp = sourcePClist.FirstOrDefault(p => p.Oid ...
- MongoDB学习笔记~为IMongoRepository接口更新指定字段
回到目录 对于MongoDB来说,它的更新建议是对指定字段来说的,即不是把对象里的所有字段都进行update,而是按需去更新,这在性能上是最优的,这当然也是非常容易理解的,我们今天要实现的就是这种按需 ...
- 开发笔记:基于EntityFramework.Extended用EF实现指定字段的更新
今天在将一个项目中使用存储过程的遗留代码迁移至新的架构时,遇到了一个问题——如何用EF实现数据库中指定字段的更新(根据UserId更新Users表中的FaceUrl与AvatarUrl字段)? 原先调 ...
- EF更新指定字段.或个更新整个实体
EF更新指定字段.或个更新整个实体 更新整个实体: public bool Update(Company compay) { if (compay != null) { dbContext.Entry ...
- 关于EF更新数据库,更新指定字段的设置
1.关于EF跟新数据库更新指定字段的设置 在EF提交到数据库的时候或许某些字段不想更新.或者自己更新一个模型到数据库去! 1.更新数据不更新一些字段 /// <summary> /// 数 ...
- OnionArch - 如何实现更新指定字段的通用Handler
博主最近失业在家,找工作之余,自己动手写了个洋葱架构(整洁架构)解决方案,以总结和整理以前的项目经验,起名叫OnionArch,其目的是为了更好的实现采用DDD(领域驱动分析)和命令查询职责分离(CQ ...
- Oracle中使用游标转换数据表中指定字段内容格式(拼音转数字)
应用场景:将数据表TB_USER中字段NNDP的内容中为[sannanyinv]转换为[3男1女] 主要脚本:一个游标脚本+分割字符串函数+拼音转数字脚本 操作步骤如下: 1.创建类型 create ...
随机推荐
- 转帖:巧用Stream优化老代码,太清爽了!
Java8的新特性主要是Lambda表达式和流,当流和Lambda表达式结合起来一起使用时,因为流申明式处理数据集合的特点,可以让代码变得简洁易读 放大招,流如何简化代码 如果有一个需求,需要对数据库 ...
- 记我的第一个UVM项目
没有天天写博客的习惯,后果就是老是忘记自己的排版风格.为了追求统一还要翻一下之前是怎么写的.这也算是意料之外的发现吧. 说实话,没人教的话从零开始学一个新的东西实在是太难了.即使互联网的存在已经大幅降 ...
- 2022中职组网络空间安全 A模块
A-1任务一 登录安全加固 1.密码策略(Windows,Linux) 主要是针对windows和Linux的系统加固,类似于运维的题目 a.设置最短密码长度为15: 这里并没有说明具体是Window ...
- Cisco Packet Tracer(思科模拟器)安装,注册用户
下载 下载地址见湖南科技大学老师:http://mooc1.xueyinonline.com/nodedetailcontroller/visitnodedetail?courseId=2226402 ...
- 详解低延时高音质:丢包、抖动与 last mile 优化那些事儿
本篇是「详解低延时高音质系列」的第三篇技术分享.我们这次要将视角放大,从整个音频引擎链路的角度,来讲讲在时变的网络下,针对不同的应用场景,如何权衡音质和互动的实时性. 当我们在讨论实时互动场景下的低延 ...
- CoordConv:给你的卷积加上坐标
摘要:本文主要对CoordConv的理论进行了介绍,对其进行了复现,并展示了其在网络结构中的用法. 本文分享自华为云社区<CoordConv:给你的卷积加上坐标>,作者: 李长安. 一.理 ...
- SQL Server底层架构技术对比
背景 数据库是信息化的基石,支撑着整个业务系统,发挥着非常重要的作用,被喻为"IT的心脏".因此,让数据库安全.稳定.高效地运行已经成为IT管理者必须要面对的问题.数据库在底层架构 ...
- 关于Java基础中的异常处理知识点
Java中的异常(Exception),史上最全的教程来啦~_smilehappiness的博客-CSDN博客 以及Java:详解Java中的异常(Error与Exception)_王小二(海阔天空) ...
- 随机服务系统模拟—R实现(二)
M/M/1随机服务系统的模拟 M/M/1模型是一种出生-死亡过程,此随机过程中的每一个状态代表模型中人数的数目.因为模型的队列长度无限且参与人数亦无限,故此状态数目亦为无限.例如状态0表示模型闲置.状 ...
- xcodebuild命令行工具使用详解
xcodebuild命令行工具使用 如何通过命令行编译ios项目? xcodebuild是一个命令行工具,允许你从命令行对Xcode项目和工作区执行编译.查询.分析.测试和归档操作.它对项目中包含的一 ...