更新实体只更新非空字段
Posted deleba
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了更新实体只更新非空字段相关的知识,希望对你有一定的参考价值。
更新实体的时候,经常会获取整个实体对数据库进行更新,但是前端传回的实体可能是不完整的,某些空字段是不需要更新,这样会造成数据不完整,需要过滤出非空字段进行更新
1.首先要写好基类
其中基类中要有获取主键的方法。
1 //基类中获取字段的发方法 2 3 private void getField(Class clazz, List<Field> list) { 4 if (!clazz.isInstance(SuperVO.class)) { 5 Field[] fields = clazz.getDeclaredFields(); 6 if (fields != null && fields.length > 0) { 7 Field[] arg3 = fields; 8 int arg4 = fields.length; 9 10 for (int arg5 = 0; arg5 < arg4; ++arg5) { 11 Field field = arg3[arg5]; 12 list.add(field); 13 } 14 } 15 16 this.getField(clazz.getSuperclass(), list); 17 } 18 }
1 //获取实体主键 2 @JsonIgnore 3 public String getPrimaryKey() throws BusinessException { 4 ArrayList fields = new ArrayList(); 5 this.getField(this.getClass(), fields); 6 Iterator arg1 = fields.iterator(); 7 8 Field field; 9 do { 10 if (!arg1.hasNext()) { 11 throw new BusinessException("获取主键失败:未找到主键注解字段"); 12 } 13 14 field = (Field) arg1.next(); 15 } while (field.getAnnotation(Id.class) == null); 16 17 try { 18 return BeanUtils.getProperty(this, field.getName()); 19 } catch (Exception arg4) { 20 LoggerFactory.getLogger(this.getClass()).error(arg4.getMessage(), arg4); 21 throw new BusinessException("获取主键失败", arg4); 22 } 23 }
2.可以建立一个公共接口,实现更新方法先根据主键查询数据库实体
1 public abstract class AbstractBaseService<T extends SuperVO> { 2 3 protected abstract CrudRepository<T, String> getRepository(); 4 5 @Transactional 6 public T update(T vo) throws BusinessException { 7 List vos = (List) this.update((Iterable) Arrays.asList(new SuperVO[]{vo})); 8 return (SuperVO) vos.get(0); 9 } 10 11 @Transactional 12 public Iterable<T> update(Iterable<T> vos) throws BusinessException { 13 vos.forEach((vo) -> { 14 T dbVo = this.getRepository().findOne(vo.getPrimaryKey()); 15 UpdateUtil.copyPropertiesIgnoreNull(dbVo,vo); 16 }); 17 return this.save(vos); 18 } 19 }
这里有一个隐藏性能问题,影响比较大,循环中频繁操作查询数据库,是不合理的,这部分可以修改为批量获取主键集合,查询出所有要更新实体,再进行替换空值,进行更新。
1 @Transactional 2 public Iterable<T> update(Iterable<T> vos) throws BusinessException { 3 List<String> ids = StreamSupport.stream(vos.spliterator(), false).stream().map(T::getPrimaryKey).collect(Collectors.toList()); 4 Iterable<T> dbVos = this.getRepository().findAll(ids); 5 Map<String, T> dbMap = StreamSupport.stream(dbVos.spliterator(), false).stream().collect(Collectors.toMap(T::getPrimaryKey, t->t)); 6 vos.forEach((vo) -> { 7 UpdateUtil.copyPropertiesIgnoreNull(dbMap.get(vo.getPrimaryKey()),vo); 8 }); 9 return this.save(vos); 10 }
3.updateUtil
获取前端实体保存入库前,可以使用,该方法过滤掉空值,再进行更新
需要注意的是,这个方法之前是要建立好基类
import java.util.HashSet; import java.util.Set; import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; /** * update更新非空实体 * * @author Administrator * */ public class UpdateUtil { /** * 获取目标对象中不为空的字段 * * @param source * @return */ public static String[] getNullPropertyNames(Object source) { final BeanWrapper src = new BeanWrapperImpl(source); java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors(); Set<String> emptyNames = new HashSet<String>(); for (java.beans.PropertyDescriptor pd : pds) { Object srcValue = src.getPropertyValue(pd.getName()); if (srcValue != null) emptyNames.add(pd.getName()); } String[] result = new String[emptyNames.size()]; return emptyNames.toArray(result); } /** * 复制源对象到目标对象,忽略目标对象不为空的字段 * * @param src * @param target */ public static void copyPropertiesIgnoreNull(Object src, Object target) { BeanUtils.copyProperties(src, target, getNullPropertyNames(target)); } }
以上是关于更新实体只更新非空字段的主要内容,如果未能解决你的问题,请参考以下文章
指定为非空的参数为空:无法更新 Spring Boot jpa 中的单个实体字段。导致指定为非空的参数为空