eclipselink 更新语句中缺少字段

Missing field in update statement in eclipselink

我正在使用 payare 5.201,它使用 eclipselink 2.7。

我正在使用 EJB(无状态 bean)来处理我的事务。

有一个 class A 和方法 X,它从数据库中检索实体 E 并在 class B 上调用方法 Y 并将 E 作为参数传递。

方法 X 是 @ASynchronous,这意味着它将 运行 在单独的线程中。

所以线程 T1 中的方法 A.X 运行s 和 T2 中的 B.Y。

当 A.X 调用 B.Y 时 A.X 不必等待,因为 B.Y returns 是异步的。

就在 A.Z 调用 B.Y 之前,它将 E 中的值 F 设置为 true。这表明 B.Y 是 运行ning。

A.X 正在通过 E,这看起来很奇怪。这样做是为了确保 B.Y 获得正确的对象。

如果我愿意,例如传递 E 的 id 并在 B.Y 中查询它然后我可以获得竞争条件,因为当 E 在 T1 中提交时无法保证所以 B.Y 可以获得 E.F 的旧值。

当B.Y收到E时,它是同一个对象,但它没有注册到B中的(其他)实体管理器。当B.Y准备好时,E被合并到实体管理器中有效更新对 E.

所做的任何更改

当 B.Y 准备就绪时,E.F 设置为 false 以指示其完成。

当 B.Y 退出时,EJB 接管并将 E 中的值更新到数据库中。

我现在看到的问题是 E.F 没有更新。在 sql 日志中,我看到记录已更新,但字段 F 不在更新语句中。这意味着 eclipselink 看不到该字段的任何更改。有趣的是,这种情况并不总是发生。

在调查了 Eclipse 之后,我读到了有关更改跟踪的信息,它可以在启用编织时使用。这可以例如通过在实体上使用@ChangeTracking(ChangeTrackingType.ATTRIBUTE) 来激活。

我正在使用 Payara,默认情况下启用编织,我没有任何@ChangeTracking 注释,这意味着 eclipselink 将根据某些规则在 运行 时间选择这些注释。

当我设置

@ChangeTracking(ChangeTrackingType.ATTRIBUTE)

在 E 实体上,问题消失,E.F 已更新。

实体E与eager fetchtype有一定关系。当我将这些关系的 fetchtype 设置为 LAZY 时,它也再次起作用。

我的结论是,问题的发生是因为eclipse 给实体E 分配了一个ChangeTrackingType 而不是ATTRIBUTE。我假设这是因为我有急切的获取类型。

问题:

感谢@Chris,我能够确认我的实体 class 的 ObjectChangePolicy 确实已延迟。

此值意味着 (ChangeTracking) 仅当值与从数据库加载时的值不同时才会对字段进行 sql 更新。

还有属性设置。

这将在调用 setter 并且原始值不同时更新字段。

所以这实际上是正在发生并导致问题的原因:

延期流程:

当 B.Y 在 A.X 能够提交之前加载 E 记录时,B.Y 中的字段 F 仍然为假。如果 A.X 提交,则 F 在数据库中为真。

当 B.Y 完成后,它会将 F 设置为 false。但是当 E 被加载时,该值也是假的,这意味着不会进行任何更新。

现在介绍解决方案:

首先,B.Y 有可能(尽管不太可能)比 A.X 完成得更快。要覆盖此 B.Y 必须告诉 A.X 不要将 F 设置为 true。这可以通过将作为参数传递的 E 上的 F 设置为 false 轻松实现。

对于第二部分,我知道有两个解决方案:

1)

在 B.Y 结束时使用 EntityManager.flush() 强制更新,然后立即使用 EntityManager.refresh() 刷新数据,然后再次将 F 设置为 false。刷新数据确保 F 现在为真,并在 F 设置为假后更新它。

2)

使用 ATTRIBUTE 并先将值设置为 true,然后再设置为 false。 这听起来很愚蠢,但这是一种强制系统更新字段的简单方法。

显然我的偏好是选项 2,因为第一个选项需要重新加载对象并再次更新它,因此它会导致 2 个额外的 sql 语句。