详解Spring Data Jpa当属性为Null也更新的完美解决方案
开场白
我本来是一名android开发者,突然就对java后端产生了浓烈的兴趣。所以,立马就转到了后端。第一个项目使用的使用SpringDataJpa来操作数据库的,可是在更新数据的时候发现一个问题,属性值为Null竟然也更新,这就会导致本来没有更新的属性值,全部就成了Null。
原因
经过一番度娘操作,原来Jpa,不知道你是想把属性设置为Null,还是不想。
解决方法
找到一个方法,就是在数据模型上加上注解@DynamicUpdate,可是发现并不好使。而后经过整理,找到以下解决方案
我们有如下实体
@Entity publicclassUser{ publicUser(){ } @Id @GeneratedValue publicLongid; privateStringname; privateStringmobileNo; privateStringemail; privateStringpassword; privateIntegertype; privateDateregisterTime; privateStringregion; privateIntegervalidity; setter... getter... }
需求:我们只更新用户的名字,其他属性值不变。
controller代码如下
@RestController publicclassUserController{ @Autowired privateUserDaouserDao; @PostMapping(value="/save") publicStringsave(@RequestBodyUseru){ userDao.save(u) return"更新成功"; } }
注意:如果我们只是更新用户的名字,我们会这样操作,如下只是提交需要更新的用户的id和需要更新属性的值,但是这样的结果就是,其他没有提交更改的属性值,会被当成Null,将数据库中对应值全部设为Null,为了解决这个问题提出以下方案。
{ "id":"1", "name":"张三" }
方案如下:
说明:
- 目标源:请求更新的实体数据。
- 数据源:通过目标源传上来的id,去数据库中查出的实体数据
我们可以将目标源中需要改变的属性值过滤掉以后,将数据源中的数据复制到目标源中,这样就达到了,只是更新需要改变的属性值,不需要更新的保持不变。
工具类如下
importorg.springframework.beans.BeanUtils; importorg.springframework.beans.BeanWrapper; importorg.springframework.beans.BeanWrapperImpl; importjava.beans.PropertyDescriptor; importjava.util.HashSet; importjava.util.Set; /** *Thereisnoroyalroadtolearning. *Description:提交实体对象中的null赋值 *Createdby贤领·周on2018年04月10日15:26 */ publicclassUpdateTool{ /** *将目标源中不为空的字段过滤,将数据库中查出的数据源复制到提交的目标源中 * *@paramsource用id从数据库中查出来的数据源 *@paramtarget提交的实体,目标源 */ publicstaticvoidcopyNullProperties(Objectsource,Objecttarget){ BeanUtils.copyProperties(source,target,getNoNullProperties(target)); } /** *@paramtarget目标源数据 *@return将目标源中不为空的字段取出 */ privatestaticString[]getNoNullProperties(Objecttarget){ BeanWrappersrcBean=newBeanWrapperImpl(target); PropertyDescriptor[]pds=srcBean.getPropertyDescriptors(); SetnoEmptyName=newHashSet<>(); for(PropertyDescriptorp:pds){ Objectvalue=srcBean.getPropertyValue(p.getName()); if(value!=null)noEmptyName.add(p.getName()); } String[]result=newString[noEmptyName.size()]; returnnoEmptyName.toArray(result); } }
这里重点说明一下,BeanUtils.copyProperties这个方法,网上很多教程都是存在误区的,源码如下:
/** *通过源码不难看出,该方法就是将source的属性值复制到target中 * *@paramsource数据源(也就是我们通过id去数据库查询出来的数据) *@paramtarget目标源(也就是我们请求更新的数据) *@paramignoreProperties(需要过滤的字段) */ publicstaticvoidcopyProperties(Objectsource,Objecttarget,String...ignoreProperties)throwsBeansException{ copyProperties(source,target,(Class)null,ignoreProperties); } privatestaticvoidcopyProperties(Objectsource,Objecttarget,Classeditable,String...ignoreProperties)throwsBeansException{ Assert.notNull(source,"Sourcemustnotbenull"); Assert.notNull(target,"Targetmustnotbenull"); ClassactualEditable=target.getClass(); if(editable!=null){ if(!editable.isInstance(target)){ thrownewIllegalArgumentException("Targetclass["+target.getClass().getName()+"]notassignabletoEditableclass["+editable.getName()+"]"); } actualEditable=editable; } PropertyDescriptor[]targetPds=getPropertyDescriptors(actualEditable); ListignoreList=ignoreProperties!=null?Arrays.asList(ignoreProperties):null; PropertyDescriptor[]var7=targetPds; intvar8=targetPds.length; for(intvar9=0;var9 有了上面的工具类以后,我们的controller如下写:
@RestController publicclassUserController{ @Autowired privateUserDaouserDao; @PostMapping(value="/save") publicStringsave(@RequestBodyUseru){ if(u.getId!=0){ Usersource=userDao.findOne(u.getId); UpdateTool.copyNullProperties(source,u); } userDao.save(u) return"更新成功"; } }结果
这样我们更新部分属性值得时候,其他不更新的属性值就不会设置为Null
{ "id":"1", "name":"张三" }性能上肯定是有影响,但是目前整理可行的方案,如果有更好的解决方案欢迎留言。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。