详解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,Class>editable,String...ignoreProperties)throwsBeansException{
Assert.notNull(source,"Sourcemustnotbenull");
Assert.notNull(target,"Targetmustnotbenull");
Class>actualEditable=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":"张三"
}
性能上肯定是有影响,但是目前整理可行的方案,如果有更好的解决方案欢迎留言。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。