問題描述
在更新數據時,有時候我們只需要更新一部分字段,其他字段保持不變。Spring Data JPA並未提供現成的接口,直接使用save()更新會導致其他字段被Null覆蓋掉。
解決辦法
通常有兩種方法解決此問題:
1、通過傳入對象的id,從數據庫中查詢得到原始對象,然后將要修改的字段封裝到原始對象中。再以封裝后的對象為參數進行save()。
代碼如下:
public FrontResult update(Evaluation evaluation) {
// 從數據庫中獲取對象
Evaluation original = evaluationRepo().findById(evaluation.getId());
// 復制想要更改的字段值
BeanUtils.copyProperties(evaluation, original, getNullPropertyNames(evaluation));
// 更新操作
evaluationRepo().save(original);
return FrontResult.init(FrontResult.SUCCEED, "更新成功");
}
使用的工具類如下(用於獲取未被修改的字段名):
public class UpdateUtil {
public static String[] getNullPropertyNames(Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> emptyNames = new HashSet<>();
for (java.beans.PropertyDescriptor pd : pds) {
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue == null) {
emptyNames.add(pd.getName());
}
}
String[] result = new String[emptyNames.size()];
return emptyNames.toArray(result);
}
}
2、通過注解@Query自己實現sql語句。
注意:
在執行update或者delete方法時,必須加上注解@Modifying 和 @Transactional。
此處的Test要使用實體的類名,不是數據庫中的表名。
代碼如下:
@Modifying
@Transactional
@Query("update Test a set " +
"a.name = CASE WHEN :#{#testAre.name} IS NULL THEN a.name ELSE :#{#testAre.name} END ," +
"a.age = CASE WHEN :#{#testAre.age} IS NULL THEN a.age ELSE :#{#testAre.age} END ," +
"a.insertTime = CASE WHEN :#{#testAre.insertTime} IS NULL THEN a.insertTime ELSE :#{#testAre.insertTime} END ," +
"a.spare = CASE WHEN :#{#testAre.spare} IS NULL THEN a.spare ELSE :#{#testAre.spare} END " +
"where a.id = :#{#testAre.id}")
int update(@Param("testAre") TestAre testAre);
一般字段比較多時我會選第一種方式,雖然多查了一次數據庫,但是省鍵盤。