(1)JPA自帶的接口更新和保存調用的都是同一個save(S)方法,是更新還是新增取決於傳入的對象主鍵相對於數據庫是否存在,如果存在則是更新,如果不存在,則是新增。
(2)但是JPA沒有類似Mybatis example的insertSelective()方法,因此手動寫了一個util用來對數據庫字段部分更新
(3)實現原理:剛開始想着更新的時候先從數據庫讀取原來的數據,然後通過BeanUtils.copyProperties(object1,object2)
(第一個參數爲傳遞來的新的對象,第二個參數爲從數據庫通過主鍵查詢到的對象。)
將前面的值賦給後面的值然後將後面的值進行數據庫提交,來實現更新,但是問題在於使用時,後者的對象會被前者爲null的字段覆蓋
比如傳遞過來的對象object1 的 name沒有設置值,那麼jpa會默認爲其設置爲null,所以使用上述方法的時候嗎,依舊會將我們從數據庫獲取的object2的name設置爲null,這顯然不是我們想要的,因此需要對該方法進行修改,後來發現BeanUtils.copyProperties()還有一個三個參數的方法BeanUtils.copyProperties(object1,object2,ignoreString) 第三個參數是忽略的屬性,也就是copy的時候不copy得屬性,這正好是我們需要用到的,第三個字段是擴展類型(String ... str) ,因此第三個參數除了可以使用(object1,object2,ignore1,ignore2)這種外,可以直接傳入數組,這顯然是非常美觀的,因此我們想辦法將忽略的屬性獲取到
具體實現原理:通過java反射object.getClass().getDeclaredFileds()獲取該對象的所有屬性,並與傳入的對象進行屬性字段匹配,判斷出該屬性是否爲空
我這裏新建了一個util可以複用,大家可以直接拷走
public class JudgeNullUtil {
public static String[] getNullPropertiesArray(Object o1) throws IllegalAccessException {
Field[] declaredFields = o1.getClass().getDeclaredFields();
List<String> nullProperties = new ArrayList<>();
for (Field declaredField : declaredFields) {
//設置可以對private屬性的對象進行訪問
declaredField.setAccessible(true);
if(declaredField.get(o1)==null){
// 將爲空的屬性添加到list中
nullProperties.add(declaredField.getName());
}
}
//nullProperties.stream().toArray(String[]::new);
return nullProperties.toArray(new String[0]);
}
}
具體調用的時候,在dao層或者service層,看具體需求,我的需求是下面的(我是直接在service調用的)
public Role updateRole(Role role) throws IllegalAccessException {
// 從數據庫獲取原有的值
Optional<Role> byId = roleDao.findById(role.getId());
if(byId.isPresent()){
//如果optional其不爲空
Role old = byId.get();
System.out.println("前:"+JSONObject.toJSONString(old));
// 獲取接收的對象爲空的屬性數組
String[] ignore = JudgeNullUtil.getNullPropertiesArray(role);
// 將前面的賦值給後面的
BeanUtils.copyProperties(role,old,ignore);
System.out.println("後"+JSONObject.toJSONString(old));
return roleDao.save(old);
}
// 如果沒有修改成功就將原來的返回
LOGGER.error("RoleService-updateRole異常,沒有更新成功");
return byId.get();
}
!就是如此