springboot jpa 关于save保存空值的问题整理

这两天需要用springboot jpa做更新处理,但是惊讶的发现 jpa种并没有忽略空值的配置,而且网上搜了一堆解决方案,大致做下整理

1.

在实体类上面添加这两个注解,

@DynamicInsert :设置为true,表示insert对象的时候,生成动态的insert语句,如果这个字段的值是null就不会加入到insert语句当中.默认true。 这个我没测试过,插入的时候,空值是否生成sql那点效率关系不大

@DynamicUpdate:设置为true,在开始的时候,我也以为这是忽略空值,但我测试的时候,发现我把实体类设置为null,去替换数据库里的数据,结果依然update成功了,最后才看见有人说,这是动态更新,只观察值是否有变动,和null无关,不知道网上哪些小伙伴是怎么成功的。

 

2.

在service 或者 Repository层,增加一个切面,每次更新的时候,先通过ID查询一次数据库,然后用beanUtil复制非空字段到目标字段,最后用Repository.save(entity),

弊端:

  1. 线程安全,并发情况下,会出现意外情况,需要在整个方法上加锁,
  2. 每次多一次查询,效率降低

3.

自定义simpleRepository,重写部分save中的代码,取消原来的repository注册,在启动类上重新使用自定义的simpleRepository

优缺点和上面差不多,只是更内层

 

每次必须多做一次查询,让我这个稍微有点强迫证的人很难受,于是自己就去翻源码,google找解决办法,在反复查看源码后,终于找到了一种解决办法,没人实践过,我自己测试没有问题,在这里做个记录:

jpa的更新,属于合并更新,而每次在合并中,都是由一个 DefaultMergeEventListener 的事件监听器来执行copy的,所以,我自定义一个 DefaultMergeEventListener

/**
 * @Auther: by yaoqiang
 * @Date: 2019-10-22 09:59
 * @Description:
 */
public class IgnoreNullEventListener extends DefaultMergeEventListener {

    public static final IgnoreNullEventListener INSTANCE = new IgnoreNullEventListener();

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

        Type[] types = persister.getPropertyTypes();


        Object[] copied = new Object[original.length];
        for ( int i = 0; i < types.length; 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 );
            }
        }

        persister.setPropertyValues( target, copied );
    }
}

在这里面,我将条件做了一些修改,使空值使用了targets中的值,也就是原值,

下一步使将 这个监听器,注入进去,源码翻了半天,找到了注册中心:网上也查了一些资料,注意,这里要将原来的DefaultMergeEventListener清理掉,要不然会循环调用,调用两次listeners,不管自定义的在前面还是在后面都会失败,前面,空值要么使在第二次直接覆盖,后面,空值第一次覆盖,第二次全是空值,这里,我也躺了坑,跟着源码跑,才看出来。

/**
 * @Auther: by yaoqiang
 * @Date: 2019-10-22 09:57
 * @Description:
 */
@Configuration
public class HibernateListenerConfigurer {


    @PersistenceUnit
    private EntityManagerFactory emf;

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

通过上面的配置,我测试了一个,发现不会在更新空值了,而非空会得到更新,并且调用次数和线程问题也由框架自己解决

  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值