首页 > 其他分享 >用tk.mybaits实现指定字段更新

用tk.mybaits实现指定字段更新

时间:2023-05-11 23:44:58浏览次数:40  
标签:更新 指定 param record mybaits tk sql example append

​ 去年年底的因为业务需要需要在使用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);
}
```

 

标签:更新,指定,param,record,mybaits,tk,sql,example,append
From: https://www.cnblogs.com/fhey/p/17392583.html

相关文章

  • 二级域名做http代理(跳到指定机器的指定端口)
    1添加域名  2添加域名后会自动生成一个加速域名,将这个加速域名做CNAME解析。  3开启自定义端口(首先打开静态回源配置和动态内容回源配置)  6然后配置自定义端口7配置后将以上两项配置的http关掉才能生效(回源配置,动态加速配置) ......
  • 【经验分享】在百度搜索引擎中的结果中,过滤指定的网站
    环境:系统版本:Windows10需求描述:描述:在搜索引擎中想在结果中过滤一些指定网站,快速找到有价值的内容。实现方法-视频与文字教程:视频教程:文字教程:1.进入百度搜索引擎,bing搜索引擎貌似不可以。www.baidu.com2.搜索内容如:锐捷无线AC基础命令-csdn,“-”这个符号是减号,0......
  • tkinter 鼠标滚轮控制滚动条滚动
    defprocessWheel(event):globalvbar,canvas,stepifevent.delta>0:#滚轮往上滚动,放大canvas.yview_scroll(step,'units')else:canvas.yview_scroll(step,'units')#滚轮往下滚动,缩小......
  • STATA 按STKCD YEAR 合并所有的kamd
    uselinshi0510,clearbysstkcdyear:genbz=_nlocalk=_Nlocalaa""localbb""localdd""genbzz=""formatbzz%200sforvaluesi=1/`k'{localaa=stkcd[`i']localbb=year[`i']ifbz[`i&......
  • rocky linux: 禁止使用root登录ssh,只允许指定用户(Rocky Linux 9.1)
    一,配置可以sudo的用户参考这个:https://www.cnblogs.com/architectforest/p/17386259.html二,禁止使用root登录ssh1,配置ssh[root@img~]#vi/etc/ssh/sshd_config配置内容:设置PermitRootLogin值为no#PermitRootLoginyesPermitRootLoginno新增一行:AllowUsers......
  • c#判断指定路径的目录/文件是否存在
    Directory.Exists(stringpath)是C#中用来判断指定目录是否存在的方法。它的返回值是bool类型,true表示目录存在,否则表示目录不存在。使用该方法的完整攻略如下:1.确定需要判断的目录路径,可以是绝对路径或者相对路径。2.使用Directory.Exists(stringpath)方法对目录进行判断。3......
  • 俄大神 lopatkin Windows 精简优化系统 - 工具软件
          昨天有个网友邮件我,说是想找个Tiny7Rev2的ISO操作系统文件,但是我找了下,以前的那些文件有些已经删除了,所以就在网上搜到了俄大神lopatkinWindows精简优化系统,特此放到网盘上让大家能够下载。      链接:https://pan.baidu.com/s/1w6rsaLhNGI35tT6WX46ijA......
  • 请问截图并保存到指定目录用哪个库要好一点?
    大家好,我是皮皮。一、前言前几天在Python最强王者群【定时】问了一个Python处理的问题,这里拿出来给大家分享下。二、实现过程这里【东哥】给了一个答案,如下所示:在Python中,可以使用Pillow库(PythonImagingLibrary)、opencv-python库、PyAutoGUI库等来实现截图并保存到指定......
  • 如何下载指定版本的node
    只需要在https://nodejs.org/dist/v16.14.2/加上指定的版本即可v16.14.2版本v16.20.0版本时间:2023-05-09午休......
  • MTK8766安卓开发板定制_智能4g联发科MTK8766安卓开发板套件
     MTK8766是一个性价比极高的四核芯片,主频可达2.0GHz,支持国内4G全网通和谷歌Android9.0系统。它采用先进的12nm工艺,GPU使用超强的IMGGE8300,主频高达600MHz,支持LPDDR4/X内存和EMMC5.1存储。CPUMTKMT8766(12纳米),ARMCortex-A53四核64位处理器,主频2.0GHzGPUIMGGE8300@600MHz内存......