這篇文章主要介紹了詳解Spring Data Jpa當屬性爲Null也更新的完美解決方案,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
開場白
我本來是一名android開發者,突然就對java後端產生了濃烈的興趣。所以,立馬就轉到了後端。第一個項目使用的使用Spring Data Jpa來操作數據庫的,可是在更新數據的時候發現一個問題,屬性值爲Null竟然也更新,這就會導致本來沒有更新的屬性值,全部就成了Null。
原因
經過一番度娘操作,原來Jpa,不知道你是想把屬性設置爲Null,還是不想。
解決方法
找到一個方法,就是在數據模型上加上註解@DynamicUpdate,可是發現並不好使。而後經過整理,找到以下解決方案
我們有如下實體
@Entity public class User{ public User(){ } @Id @GeneratedValue public Long id; private String name; private String mobileNo; private String email; private String password; private Integer type; private Date registerTime; private String region; private Integer validity; setter... getter... }
需求:我們只更新用戶的名字,其他屬性值不變。
controller代碼如下
@RestController public class UserController { @Autowired private UserDao userDao; @PostMapping(value = "/save") public String save(@RequestBody User u) { userDao.save(u) return "更新成功"; } }
注意:如果我們只是更新用戶的名字,我們會這樣操作,如下只是提交需要更新的用戶的id和需要更新屬性的值,但是這樣的結果就是,其他沒有提交更改的屬性值,會被當成Null,將數據庫中對應值全部設爲Null,爲了解決這個問題提出以下方案。
{ "id" : "1", "name" : "張三" }
方案如下:
說明:
- 目標源:請求更新的實體數據。
- 數據源:通過目標源傳上來的id,去數據庫中查出的實體數據
我們可以將目標源中需要改變的屬性值過濾掉以後,將數據源中的數據複製到目標源中,這樣就達到了,只是更新需要改變的屬性值,不需要更新的保持不變。
工具類如下
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import java.beans.PropertyDescriptor; import java.util.HashSet; import java.util.Set; /** * There is no royal road to learning. * Description:提交實體對象中的null賦值 * Created by 賢領·周 on 2018年04月10日 15:26 */ public class UpdateTool { /** * 將目標源中不爲空的字段過濾,將數據庫中查出的數據源複製到提交的目標源中 * * @param source 用id從數據庫中查出來的數據源 * @param target 提交的實體,目標源 */ public static void copyNullProperties(Object source, Object target) { BeanUtils.copyProperties(source, target, getNoNullProperties(target)); } /** * @param target 目標源數據 * @return 將目標源中不爲空的字段取出 */ private static String[] getNoNullProperties(Object target) { BeanWrapper srcBean = new BeanWrapperImpl(target); PropertyDescriptor[] pds = srcBean.getPropertyDescriptors(); Set<String> noEmptyName = new HashSet<>(); for (PropertyDescriptor p : pds) { Object value = srcBean.getPropertyValue(p.getName()); if (value != null) noEmptyName.add(p.getName()); } String[] result = new String[noEmptyName.size()]; return noEmptyName.toArray(result); } }
這裏重點說明一下, BeanUtils.copyProperties這個方法,網上很多教程都是存在誤區的,源碼如下:
/** * 通過源碼不難看出,該方法就是將source的屬性值複製到target中 * * @param source 數據源(也就是我們通過id去數據庫查詢出來的數據) * @param target 目標源(也就是我們請求更新的數據) * @param ignoreProperties (需要過濾的字段) */ public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException { copyProperties(source, target, (Class)null, ignoreProperties); } private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties) throws BeansException { Assert.notNull(source, "Source must not be null"); Assert.notNull(target, "Target must not be null"); Class<?> actualEditable = target.getClass(); if (editable != null) { if (!editable.isInstance(target)) { throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]"); } actualEditable = editable; } PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable); List<String> ignoreList = ignoreProperties != null ? Arrays.asList(ignoreProperties) : null; PropertyDescriptor[] var7 = targetPds; int var8 = targetPds.length; for(int var9 = 0; var9 < var8; ++var9) { PropertyDescriptor targetPd = var7[var9]; Method writeMethod = targetPd.getWriteMethod(); if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) { PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName()); if (sourcePd != null) { Method readMethod = sourcePd.getReadMethod(); if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) { try { if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { readMethod.setAccessible(true); } Object value = readMethod.invoke(source); if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { writeMethod.setAccessible(true); } writeMethod.invoke(target, value); } catch (Throwable var15) { throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var15); } } } } } }
有了上面的工具類以後,我們的controller如下寫:
@RestController public class UserController { @Autowired private UserDao userDao; @PostMapping(value = "/save") public String save(@RequestBody User u) { if(u.getId != 0){ User source= userDao.findOne(u.getId); UpdateTool.copyNullProperties(source, u); } userDao.save(u) return "更新成功"; } }
結果
這樣我們更新部分屬性值得時候,其他不更新的屬性值就不會設置爲Null
{ "id" : "1", "name" : "張三" }
性能上肯定是有影響,但是目前整理可行的方案,如果有更好的解決方案歡迎留言。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持神馬文庫。