JPA null值不更新(null值覆盖问题)

JPA null值不更新

网上很多说@DynamicUpdate可以实现null值不更新,那都是谬论

作用:这个注解的作用是更新前比对提交的数据和数据库保存的数据是否一致,若一致则不做更新,否则会更新数据库的值,为null就会覆盖原先的数据

  • 新增工具类,作用:只拷贝不为null的属性
package cn.com.util;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapperImpl;

import java.beans.PropertyDescriptor;
import java.util.stream.Stream;

/**
 * JPA避免一些空值对于动态部分更新的影响工具类
 * (null值不更新, 配合实体类@DynamicUpdate使用)
 */
public class JpaUtil {

    /**
     * 查询出entity值 -> JpaUtil.copyNotNullProperties(input, entity); -> save(entity)
     *
     * @param input  输入实体类
     * @param entity 数据库查询出的实体类
     */
    public static void copyNotNullProperties(Object input, Object entity) {
        BeanUtils.copyProperties(input, entity, getNullPropertyNames(input));
    }

    /**
     * 忽略的字段:值为null、浮点数为0.0、值为空字符串""
     * <p>
     * ignoreProperties {@link BeanUtils#copyProperties(java.lang.Object, java.lang.Object, java.lang.String...)}
     *
     * @param object 传入全部字段
     * @return 忽略的字段
     */
    private static String[] getNullPropertyNames(Object object) {
        final BeanWrapperImpl wrapper = new BeanWrapperImpl(object);
        return Stream.of(wrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)
                .filter(propertyName -> wrapper.getPropertyValue(propertyName) == null
                        || "0.0".equals(String.valueOf(wrapper.getPropertyValue(propertyName)))
                        || "".equals(String.valueOf(wrapper.getPropertyValue(propertyName))))
                .toArray(String[]::new);
    }

}
  • 实体类
package cn.com.dao.jpa.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

@Data
@Entity
@DynamicUpdate
@ApiModel("角色表")
@Table(name = "sys_role")
@EntityListeners(AuditingEntityListener.class)
@org.hibernate.annotations.Table(appliesTo = "sys_role", comment = "角色表")
public class SysRole implements Serializable {

	private static final long serialVersionUID =  9022888214467860659L;

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
   	@Column(name = "id")
	@ApiModelProperty(value = "主键")
	private Long id;

	@Column(name = "bid")
	@ApiModelProperty(value = "业务id")
	private String bid;

   	@Column(name = "parent_bid")
	@ApiModelProperty(value = "父业务id")
	private String parentBid;

   	@Column(name = "name")
	@ApiModelProperty(value = "角色名称")
	private String name;

   	@Column(name = "enname")
	@ApiModelProperty(value = "角色英文名称")
	private String enname;

   	@Column(name = "description")
	@ApiModelProperty(value = "备注")
	private String description;

	@CreatedDate
   	@Column(name = "create_time", updatable = false)
	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
	@ApiModelProperty(value = "创建时间")
	private Date createTime;

	@LastModifiedDate
   	@Column(name = "update_time")
	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
	@ApiModelProperty(value = "更新时间")
	private Date updateTime;

	@CreatedBy
   	@Column(name = "create_user", updatable = false)
	@ApiModelProperty(value = "创建人")
	private String createUser;

	@LastModifiedBy
   	@Column(name = "update_user")
	@ApiModelProperty(value = "更新人")
	private String updateUser;

}
  • 更新
// 先查询出来
SysRole exists = this.findByEnNameAndLogicDelete(form.getEnname(), Boolean.FALSE);
if (exists != null) {
    // 提交的不为null的值覆盖数据库的值
    JpaUtil.copyNotNullProperties(role, exists);
    baseRepository.save(exists);
}
  • 部分修改时,sql日志
update
        public.sys_role 
    set
        name=?,
        update_time=? 
    where
        id=?
1 个赞

springboot jpa 关于save保存空值的问题整理_perfect_red的博客-CSDN博客 我曾经的博客,有兴趣可以看看,交流一下。

可以写一个工具类,忽略null值

不用工具类,用上面大佬提供的方案

  • 监听类
package cn.com.jpa;

import java.util.Map;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.event.internal.DefaultMergeEventListener;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl;
import org.hibernate.type.Type;

/**
* @author: Jonny
* @description: 对象拷贝时忽略null或空字符
* @date:created in 2021/4/27 12:15
* @modificed by:
*/
public class IgnoreNullEventListener extends DefaultMergeEventListener {

 public static final IgnoreNullEventListener INSTANCE = new IgnoreNullEventListener();

 @Override
 protected void copyValues(EntityPersister persistent, Object entity, Object target,
     SessionImplementor source, Map copyCache) {
   //源目标
   Object[] original = persistent.getPropertyValues(entity);
   //存储目标
   Object[] targets = persistent.getPropertyValues(target);

   Type[] types = persistent.getPropertyTypes();

   Object[] copied = new Object[original.length];
   int len = types.length;
   for (int i = 0; i < len; i++) {
     if (original[i] == null ||
         original[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY ||
         original[i] == PropertyAccessStrategyBackRefImpl.UNKNOWN
     ) {
       copied[i] = targets[i];
     } else {
       copied[i] = types[i].replace(original[i], targets[i], source, target, copyCache);
     }
   }

   persistent.setPropertyValues(target, copied);
 }
}


  • 配置类
package cn.com.jpa;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.internal.SessionFactoryImpl;
import org.springframework.context.annotation.Configuration;

/**
 * @author: Jonny
 * @description:
 * @date:created in 2021/4/27 12:19
 * @modificed by:
 */
@Configuration
public class HibernateListenerConfigurer {

  @PersistenceUnit
  private EntityManagerFactory entityManagerFactory;

  @PostConstruct
  protected void init() {
    SessionFactoryImpl sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl.class);
    EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
    registry.getEventListenerGroup(EventType.MERGE).clear();
    registry.getEventListenerGroup(EventType.MERGE).prependListener(IgnoreNullEventListener.INSTANCE);
  }

}

那要指定的null值需要更新怎么办