JpaRepository.save()居然调用了2次insert?

先描述一下场景:

我需要对用户实体进行数据鉴权,所以期望的是监听用户的save()方法执行后,同步执行新增数据权限,并且给对应的用户分配该数据权限。

问题来了,在没有做数据权限前,save方法执行正常,加入了监听器之后,save方法就insert了2次,然后就是报主键冲突(因为是同一个对象)。

看一下实现:

第一步,创建用户实例

第二步,创建监听器

 

第三步,User对象注册监听器

 

第四步,开始测试

 

可以看到,一模一样的数据被insert了2次。然后跟到源码查了一下原因,我大概总结一下:

由于JPA的一级缓存是根据session来进行缓存的,而且因为我所有的保存方法都是调用的save()方法,这个方法在你调用的时候并不会直接执行SQL,而是先缓存在一个执行队列。我的创建用户方法调用完成后,执行队列开始执行,然后执行第一条SQL,创建用户,执行完成之后,触发了用户创建后的监听器,监听器就开始保存数据权限和权限分配,这个时候也要操作数据库,JPA的一级缓存机制的从session中拿到了之前缓存的执行队列,然后把你监听器里面要执行的SQL给加了进去!!!然后重新执行队列!!!  问题就在这,我第一条创建用户的SQL已经执行了呀??你从缓存再拿一次队列,然后加入进来,我用户又被insert了一次。

发一下证明图:

 第一步,方法执行完成(还未开始insert)

 

 第二步,查看执行队列

 第三步,监听器被触发

第四步,session缓存被刷新

 

 好!干的漂亮!

刚执行完的插入用户语句又特么回来了,让我再执行一次!

报错了,主键冲突....

怎么解决?

我的解决方案仅限于我当前的场景:

第一种、不通过监听器实现,手动在创建的地方执行创建数据权限

        缺点很明显,如果你将来还有别的域对象要做数据权限,全都得这么搞,每个创建方法都要做这个事情 

第二种、不用同步,用异步

        我这个场景不适用,我需要将创建用户和创建数据权限放在一个事务,一荣俱荣,一损俱损!异步就没法保证事务,当然有的小伙伴可以说用最终一致性解决方案什么的。。。你要想这么玩也行,但是我这得要求实时同步数据。

第三种、把这个数据权限的创建放在save()方法执行前

        但是这么玩有个坑! 那就是如果你的实例对象的主键是通过策略实现的,那你拿不到主键ID。当然,这个是说你得先要用这个主键,如果不用就不用管了,如果你要用,你就必须把主键的策略去除,手动设置主键,就是让这个Object传过来的时候,主键已经放进去了。

       

        问题已经描述完了,如果有其他更好解决方案的小伙伴,欢迎留言或私信,期待你的点赞! 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
回答: 在使用JPA编写Repository接口方法时,可以按照以下几种方式进行编写。首先,可以直接继承JpaRepository接口,然后调用对应的save方法来实现数据的保存。其,可以在dao层中实现Repository接口的方法,方法名需要符合规范。\[1\]JpaRepository继承了PagingAndSortingRepository接口,并添加了一组JPA规范相关的方法。在JpaRepository中,直接返回了List,省去了我们进行强制类型转换的步骤。在开发中,最常用的是JpaRepositoryJpaSpecificationExecutor接口。\[2\]如果需要了解更多关于JpaRepository的使用方法,可以参考相关的文章,如《JpaRepository数据层 Spring Data JPA 提供的各种Repository接口》和《一步一步学SpringDataJpa——JpaRepository查询功能》。\[3\] #### 引用[.reference_title] - *1* [spring jpa基础:使用repository接口](https://blog.csdn.net/Dumb_Brother/article/details/127449008)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [JPARepository详解](https://blog.csdn.net/xfx_1994/article/details/104921234)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Run_the_ant

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值