您好,登錄后才能下訂單哦!
開場(chǎng)白
我本來(lái)是一名android開發(fā)者,突然就對(duì)java后端產(chǎn)生了濃烈的興趣。所以,立馬就轉(zhuǎn)到了后端。第一個(gè)項(xiàng)目使用的使用Spring Data Jpa來(lái)操作數(shù)據(jù)庫(kù)的,可是在更新數(shù)據(jù)的時(shí)候發(fā)現(xiàn)一個(gè)問(wèn)題,屬性值為Null竟然也更新,這就會(huì)導(dǎo)致本來(lái)沒有更新的屬性值,全部就成了Null。
原因
經(jīng)過(guò)一番度娘操作,原來(lái)Jpa,不知道你是想把屬性設(shè)置為Null,還是不想。
解決方法
找到一個(gè)方法,就是在數(shù)據(jù)模型上加上注解@DynamicUpdate,可是發(fā)現(xiàn)并不好使。而后經(jīng)過(guò)整理,找到以下解決方案
我們有如下實(shí)體
@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 "更新成功"; } }
注意:如果我們只是更新用戶的名字,我們會(huì)這樣操作,如下只是提交需要更新的用戶的id和需要更新屬性的值,但是這樣的結(jié)果就是,其他沒有提交更改的屬性值,會(huì)被當(dāng)成Null,將數(shù)據(jù)庫(kù)中對(duì)應(yīng)值全部設(shè)為Null,為了解決這個(gè)問(wèn)題提出以下方案。
{ "id" : "1", "name" : "張三" }
方案如下:
說(shuō)明:
我們可以將目標(biāo)源中需要改變的屬性值過(guò)濾掉以后,將數(shù)據(jù)源中的數(shù)據(jù)復(fù)制到目標(biāo)源中,這樣就達(dá)到了,只是更新需要改變的屬性值,不需要更新的保持不變。
工具類如下
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:提交實(shí)體對(duì)象中的null賦值 * Created by 賢領(lǐng)·周 on 2018年04月10日 15:26 */ public class UpdateTool { /** * 將目標(biāo)源中不為空的字段過(guò)濾,將數(shù)據(jù)庫(kù)中查出的數(shù)據(jù)源復(fù)制到提交的目標(biāo)源中 * * @param source 用id從數(shù)據(jù)庫(kù)中查出來(lái)的數(shù)據(jù)源 * @param target 提交的實(shí)體,目標(biāo)源 */ public static void copyNullProperties(Object source, Object target) { BeanUtils.copyProperties(source, target, getNoNullProperties(target)); } /** * @param target 目標(biāo)源數(shù)據(jù) * @return 將目標(biāo)源中不為空的字段取出 */ 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); } }
這里重點(diǎn)說(shuō)明一下, BeanUtils.copyProperties這個(gè)方法,網(wǎng)上很多教程都是存在誤區(qū)的,源碼如下:
/** * 通過(guò)源碼不難看出,該方法就是將source的屬性值復(fù)制到target中 * * @param source 數(shù)據(jù)源(也就是我們通過(guò)id去數(shù)據(jù)庫(kù)查詢出來(lái)的數(shù)據(jù)) * @param target 目標(biāo)源(也就是我們請(qǐng)求更新的數(shù)據(jù)) * @param ignoreProperties (需要過(guò)濾的字段) */ 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 "更新成功"; } }
結(jié)果
這樣我們更新部分屬性值得時(shí)候,其他不更新的屬性值就不會(huì)設(shè)置為Null
{ "id" : "1", "name" : "張三" }
性能上肯定是有影響,但是目前整理可行的方案,如果有更好的解決方案歡迎留言。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。