? 去年年底的因為業務需要需要在使用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);
}
```
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/552189.html
標籤:Java
下一篇:返回列表
