同一张表有多个业务实体绑定时,每个实体页面点击删除按钮时,抛出乐观锁异常

HI,如题,我们有一个“事故”的主表,分不同的事故类型,创建了多个业务实体绑定这张表。对单条数据进行删除时,会抛出乐观锁异常,报错内容如下:image
其中报错的业务实体为PersonAccident,但是删除操作是在另一个实体PowerAccident的界面执行的。
调试发现:image 执行过程中有一步getInstances(),执行后会把同一条数据映射到多个业务实体中。
请问原因是什么?如何解决?
感谢

Hi?

你是说 A,B 实体都关联数据库表 T?这样的话,删除了 A 实体之后,B 实体肯定是游离的了。如果一定要这样设计,可以在 A 实体删除的监听器里面更新一下 B,反之亦然。

您好,是指创建监听器,重写beforeCommit方法,在里面更新B实体的状态吗?
删除方法是这么写的:image
监听器中如何取到B并更新状态呢?

有两种方式:
第一种是在每次使用 B 实体时,用 EntityStatus 接口检查其状态是否为 detached, 不过看你的日志,似乎此时 B 实体还处于 managed 状态,也许不一定好用,另外也比较繁琐。
第二种是使用 Events,当 A 实体删除时发送 event,同时在 B 实体的展示界面处监听该事件做处理。

抱歉,我还是有点困惑。
实体A删除时发送event,为什么要到B实体的展示界面监听并处理呢?
实体A和实体B关联同一张表T,删除实体A后,因为这条数据也映射到实体B,所以状态上可能出问题,这点姑且是明白了,但是去B实体的展示界面监听是为何?是因为别处监听不到吗?
删除时发送事件,但是错误是删除的过程中抛出的,执行顺序上是否来得及做处理?
感谢

B 实体处监听到 A 实体删除了,才可以做处理呀,比如,如果是展示的 B 实体列表,那么在监听到 A 删除时,列表的数据容器需要重新加载,以反映 A 实体对 T 表的删除:原来能加载出 5 个 B 实体,现在应当只能加载出 4 个。
别处也能监听到,但是别处不处理 B 实体,我是觉得监听到也没有意义吧。

可以在删除前在 Event 里面发送要删除实体的 UUID,在 B 实体监听到了之后,直接重新加载除掉该 UUID 的数据。

乐观锁发生的根源是 甲、乙同时读了一条数据version=1,甲删除了,数据version=2; 乙之前拿到version1也要做操作,commit的时候声明也让version=2, 这个时候发现数据库里已经是version=2了,此时系统错误阻止乙的操作。
楼主这个问题,他要删PowerAccident,使它version=2, 这个时候PersonAccident也报错,说明有另外的程序也尝试通过PersonAccident写入version=2数据。

这个问题感觉奇怪,根本上是不应该让PersonAccident触发写操作; 也许是业务代码的问题,也许是cuba的某个机制的问题。

我感觉根源应该查为什么删除PowerAccident会导致PersonAccident有动作。

感谢二位,我困惑的原因就在于此:为什么删除A类会导致B类有读或写的动作。

A类和B类除了关联同一张表外,并无其他交集,也不在同一个界面。查询的数据也是通过Type字段进行区分的,按理说读写的数据也并无交集,也就无需在删除A类之后在B的展示界面监听。

之所以这样设计,是因为A和B类同属于一个业务,只是关联的子业务不同,考虑到如果用同一个实体类的话,就需要将每个子业务都关联同一个类,也不太合适。

我在调试过程中发现,走到cuba.core包中的PersistenceImplSupport.java类时,同一条数据映射成了多个实体,这一步是在做什么操作二位了解吗?

我注意到截图中两个实体的ID是一样的?是这样吗?

是的,这两个实体应该是对应着一条数据。不确定是不是截图这里的问题,但是可能是个线索。

操作的话,就是在Power类的界面删除了一条数据,与Person类那边没有联系

这两个实体在不在一个表?
另外像你描述的场景,一般用实体继承,不清楚你是不是这样设计的?、
关于实体继承可参考这个指南:

在一个表

实体继承我理解的是:Cat类和Dog类分别在不同的表,都继承Pet基类,查询的时候可以一并查询出来

但是我们的业务是:Cat类和Dog类在同一张表,查询的时候分开查询。Cat类关联着Cat相关的业务,比如猫选美表,Dog类关联着Dog的业务,比如导盲犬导盲记录表

  • 在一个表的话先消除重复ID试试。
  • 完全适用你所述场景,可以试试。

您好,没有重复ID,数据库UNIQUE。

奇怪的地方就是,这是同一条数据,就是PET表中的一条记录,同时映射成了Cat类和Dog类

因为我们这块业务已经开发完了,只剩一个删除会报错的问题,用实体继承的话还要返工,所以还是想找找看解决方案

感谢!

你用的什么机制去区分一条数据应该映射成哪个实体?我感觉是相同DB记录创建了不同的实体。所以检查一下业务逻辑,是不是有数据范围重叠的地方。

我们在Dog-Browser.xml中使用JPQL查询当前的Dog实体,但因为都存在同一张表所以添加了type字段。JPQL为:(虽然看起来比较奇怪,但是确实是这么做的)

select e from Dog e where e.type = ‘dog’

删除时获取到当前表格选中的Dog实体,使用CommitContext和DataManager进行删除。Cat同理

Hi?
:partying_face:

先确定为什么一条记录创建了两个不同类型的实例,问题应该就在这。

Hi,Ray,
我明白问题在这,我想我们又绕回了起点,我仍然在调试,在1L截图中,408行前一切正常,408行执行后,container.getAllInstances()即能映射错误的实体类