使用@TableGenerator的JPA EclipseLink。事务回滚后,为什么分配给id的值不为null?

时间:2013-04-02 14:51:28

标签: java java-ee jpa eclipselink

我有以下实体:

@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

我做错了什么?

2 个答案:

答案 0 :(得分:2)

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

您需要创建一个新的持久化上下文,并且需要重新创建对象,或者如果您想要使用现有的那些,请仔细合并然后进入新的持久性上下文并将其ID归零。此外,对于任何更新的对象,您需要还原其版本字段。

JPA不提供重新提交交易的简便方法。 EclipseLink的本机API提供commitAndResumeOnFailure(),但这不会暴露给JPA。也许记录增强请求以获得某种选项以允许在JPA中重新提交失败的事务。

答案 1 :(得分:0)

好的,我设法弄明白了,我会在这里发布我的结果,以帮助任何可能遇到同样问题的人。

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

EclipseLink似乎是通过设计或bug来为ID字段分配值并从ID序列中删除该值。因此,事务回滚将导致序列号中出现漏洞(实际上并不是那么重要)。

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

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

不幸的是,我无法重现这个错误。 :(我不知道EclipseLink是如何进入畸形状态的。