您好,登錄后才能下訂單哦!
開場白
我本來是一名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" : "張三" }
方案如下:
說明:
我們可以將目標源中需要改變的屬性值過濾掉以后,將數據源中的數據復制到目標源中,這樣就達到了,只是更新需要改變的屬性值,不需要更新的保持不變。
工具類如下
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" : "張三" }
性能上肯定是有影響,但是目前整理可行的方案,如果有更好的解決方案歡迎留言。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。