关键字: JPA更新为null,JPA save null,JPA保存为null
今天使用jpa的save方法时发现null字段也会被更新到数据库,这个直接把数据库数据覆盖的行为很可怕,果断研究了一下怎样能不保存null值。
解决方法:
一.扩展SimpleJpaRepository覆盖save方法
ZeusJpaRepository.java
package com.lmt.zeus.jpa;
import com.lmt.zeus.parent.utils.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import javax.persistence.EntityManager;
import java.util.Optional;
/**
* @description 扩展SimpleJpaRepository,save()时不更新null字段
*
* @author 隐
* @date 2020/3/4 18:01
* @since JDK1.8
*/
@Slf4j
public class ZeusJpaRepository extends SimpleJpaRepository {
private JpaEntityInformation jpaEntityInformation;
/**
* Creates a new {@link ZeusJpaRepository} to manage objects of the given {@link JpaEntityInformation}.
*
* @param entityInformation must not be {@literal null}.
* @param entityManager must not be {@literal null}.
*/
@Autowired
public ZeusJpaRepository(JpaEntityInformation entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
this.jpaEntityInformation = entityInformation;
}
/**
* 覆盖原来实现,不更新null字段
* @author 隐
* @date 2020-03-16
* @param entity
* @param
* @return
*/
@Override
public S save(S entity) {
ID id = (ID) jpaEntityInformation.getId(entity);
if (id != null) {
Optional op = findById(id);
if (op.isPresent()) {
T t = op.get();
BeanUtils.copyPropertiesWithoutNull(entity, t);
entity = (S) t;
}
}
return super.save(entity);
}
}
BeanUtils.java
package com.lmt.zeus.parent.utils;
import org.springframework.beans.FatalBeanException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* @description Bean拷备、map转换工具类
* @author 隐
* @date 2018/11/8 17:55
* @since JDK1.8
*/
public class BeanUtils extends org.springframework.beans.BeanUtils {
/**
* bean copy不复制null值
* @author 隐
* @date 2018-11-10
* @param source
* @param target
*/
public static void copyPropertiesWithoutNull(Object source, Object target) {
if(source == null || target == null){
return;
}
Class> actualEditable = target.getClass();
Class> sourceClass = source.getClass();
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
for (PropertyDescriptor targetPd : targetPds) {
if(targetPd.getWriteMethod() == null) {
continue;
}
PropertyDescriptor sourcePd = getPropertyDescriptor(sourceClass, targetPd.getName());
if(sourcePd == null || sourcePd.getReadMethod() == null) {
continue;
}
try {
Method readMethod = sourcePd.getReadMethod();
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
setValue(target, targetPd, value);
} catch (Exception ex) {
throw new FatalBeanException("Could not copy properties from source to target", ex);
}
}
}
/**
* 设置值到目标bean
* @param target
* @param targetPd
* @param value
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
private static void setValue(Object target, PropertyDescriptor targetPd, Object value) throws IllegalAccessException, InvocationTargetException {
// 这里判断以下value是否为空
if (value != null) {
Method writeMethod = targetPd.getWriteMethod();
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, value);
}
}
}
二、启动类
import com.lmt.zeus.jpa.ZeusJpaRepository;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* @description Spring Boot启动类
*
* @author 隐
* @date 2020/3/16 10:10
* @since JDK1.8
*/
@SpringBootApplication
@EnableTransactionManagement
@ServletComponentScan
@EnableScheduling
@EnableJpaRepositories(repositoryBaseClass = ZeusJpaRepository.class)
public class CoinTradeApp {
public static void main(String [] args) {
SpringApplication.run(CoinTradeApp.class, args);
}
}
注:实体类上不要忘了加@DynamicInsert(true)/@DynamicUpdate(true)注解;
ps:这里还有个坑如果根据id查出来直接在查出的实体上设置某字段为null,再保存时仍可以把null字段保存到数据库(因为session缓存原因),所以save方法执行时最好不要直接拿查出的实体修改,还是new一个比较安全。
欢迎关注「Java牧码人」
追求技术的路上永无止境