【问题标题】:JPA EclipseLink with @TableGenerator. Why is the value assigned to id not null after transaction rollback?JPA EclipseLink 与 @TableGenerator。为什么事务回滚后分配给 id 的值不为空?
【发布时间】:2013-04-02 14:51:28
【问题描述】:

我有以下实体:

@Entity
@Table(name = "sales", schema = "cust_tables")
@Multitenant(MultitenantType.TABLE_PER_TENANT)
@TenantTableDiscriminator(contextProperty = "customer_schema", type = TenantTableDiscriminatorType.SCHEMA)
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class Sale implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "Sale")
    @TableGenerator(name = "Sale", schema = "thehub", allocationSize = 5)
    @Column(name = "id", unique = true, nullable = false, updatable = false)
    @XmlElement
    private Long id;

在持久化操作期间,我可能故意允许事务在乐观锁定失败期间回滚。

然而,我对 JPA/EclipseLink 在这种情况下的行为感到惊讶。正如预期的那样,JPA 将序列中的一个值分配给id。但是,在回滚期间,该值不会被“删除”。然后下次我尝试坚持时,id 的值已经存在。 JPA 跳过从序列中提取新数字并尝试持久化实体。

我因 SQL 完整性约束异常而失败:

Caused by: javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130226-e0971b1): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '51' for key 'PRIMARY'
Error Code: 1062

我做错了什么?

【问题讨论】:

    标签: java jakarta-ee jpa eclipselink


    【解决方案1】:

    当 JPA 事务失败时,所有对象都将分离。您无法重新提交交易。

    您需要创建一个新的持久性上下文,并且需要重新创建对象,或者如果您想使用现有的对象,请仔细合并到新的持久性上下文中并清空它们的 id。此外,对于任何更新的对象,您都需要恢复其版本字段。

    JPA 不提供重新提交事务的简单方法。 EclipseLink 的本机 API 提供了 commitAndResumeOnFailure(),但这并没有暴露给 JPA。或许记录一个增强请求,以获得某种选项以允许在 JPA 中重新提交失败的事务。

    【讨论】:

    • 感谢您的评论,尽管重新提交交易不是问题。
    【解决方案2】:

    好的,我设法解决了这个问题,我将在此处发布我的结果以帮助可能遇到相同问题的任何人。

    使用调试器我可以看到 EclipseLink 确实为 entity.id 字段分配了一个值,即使在事务回滚期间也是如此。如果你像我一样,你的 id 字段被注释并且你的 ID 是不可变的,这意味着只有一个 getter 而不是一个 setter。 EclipseLink 能够使用反射为该字段分配一个值,但是您公开的 API 会阻止 id 字段的突变。 (这是一件“好事”)。

    EclipseLink 似乎有意或无意地为 ID 字段分配一个值并从 ID 序列中删除该值。因此,事务回滚会导致您的序列号出现漏洞(这并不是什么大罪)。

    请记住,EclipseLink 读取序列号表,在内存中缓存一些序列号,然后在缓存为空时重新读取它,同时为下一个序列提交起始值。

    不知何故,通过事务回滚和重新启动我的服务器的组合,序列号表落后于正在使用的实际序列号。因此,该错误不是在事务回滚期间分配 ID,而是丢失了对序列号的跟踪。我最终得到的是序列号表的值为 50,但我的数据库中编号最高的实体是 51。解决方案是将序列表更新为 allocationSize(5) 的两倍,将其设置为 60,然后反弹我的服务器。

    很遗憾,我无法重现此错误。 :( 我不知道 EclipseLink 是如何进入格式错误的状态的。

    【讨论】:

    • 这可能是一个不同的错误,但我在使用 EclipseLink 时遇到了类似的问题 DROP_AND_CREATE 不知何故没有丢弃所有内容并抱怨实体已经存在。我不得不直接从 SQL 中删除模式。
    • EclipseLink 通常会在事务上下文之外分配序列号,因此永远不会重新分配分配的 ID。但是,如果您使用 JTA 或触发 flush(),则序列分配将使用当前事务。这很糟糕,解决方法是使用顺序连接池。见,eclipse.org/eclipselink/documentation/2.4/jpa/extensions/…
    • 那里的文档不是很好,您能否在答案中对此进行更多解释?我在代码中有一个 flush() 来强制检查一些唯一键
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-26
    • 2013-11-18
    • 2015-10-25
    • 1970-01-01
    • 1970-01-01
    • 2012-02-04
    • 2021-07-06
    相关资源
    最近更新 更多