Detailed Spring Data Jpa Perfect solution when attribute is also updated for Null
- 2021-06-28 12:38:36
- OfStack
Opening remarks
I was an android developer and suddenly became interested in the java backend.So, immediately go to the back end.The first project used Spring Data Jpa to operate the database, but when updating the data, a problem was found that the attribute value Null was also updated, which resulted in the attribute value that was not updated, all became Null.
Reason
After a bride-to-be operation, the original Jpa, I don't know if you want to set the property to Null or not.
Resolvent
One way to do this is to add a comment @DynamicUpdate to the data model, but it's not easy to find out.After that, the following solutions were found
We have the following entities
@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...
}
Requirement: We only update the user's name, other attribute values remain unchanged.
The controller code is as follows
@RestController
public class UserController {
@Autowired
private UserDao userDao;
@PostMapping(value = "/save")
public String save(@RequestBody User u) {
userDao.save(u)
return " Update Successful ";
}
}
Note: If we only update the user's name, we will do this, as follows, simply submit the id of the user who needs to be updated and the value of the attribute that needs to be updated, but as a result, other attribute values that do not submit changes will be treated as Null, setting all corresponding values in the database to Null. To solve this problem, the following solutions are provided.
{
"id" : "1",
"name" : " Zhang 3"
}
The scheme is as follows:
Explain:
Target source: Entity data that requests updates. Data Source: Entity data retrieved from the database by id from the target sourceAfter filtering out the attribute values that need to be changed in the target source, we can copy the data from the data source to the target source. This achieves, but updates the attribute values that need to be changed, and keeps them unchanged without updating.
The tool classes are as follows
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: Submitted in Entity Object null assignment
* Created by Sage President Zhou on 2018 year 04 month 10 day 15:26
*/
public class UpdateTool {
/**
* Filter fields that are not empty in the target source and copy data sources found in the database to the submitted target source
*
* @param source use id Data source found from database
* @param target Submitted entity, target source
*/
public static void copyNullProperties(Object source, Object target) {
BeanUtils.copyProperties(source, target, getNoNullProperties(target));
}
/**
* @param target Target Source Data
* @return Remove non-empty fields from the target source
*/
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);
}
}
Here is the highlight of 1. BeanUtils.copyProperties is a method, many online tutorials are mistaken, the source code is as follows:
/**
* It's easy to see from the source that this method will source Copy attribute values from to target in
*
* @param source Data sources (that is, we use id Data queried from the database)
* @param target Target Source (that is, the data we requested to be updated)
* @param ignoreProperties ( Fields to be filtered )
*/
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);
}
}
}
}
}
}
With the tool classes above, our controller writes as follows:
@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 " Update Successful ";
}
}
Result
This way, when we update some of the attributes that are worth it, other attribute values that are not updated will not be set to Null
{
"id" : "1",
"name" : " Zhang 3"
}
Performance certainly has an impact, but at the moment it's possible to organize and leave a message if there's a better solution.