EclipseLink generates duplicate primary keys on an Oracle DB
我使用的是EclipseLink 2.4.2(与Spring结合使用,但是我认为这与我们的问题无关),并且有几次出现以下异常:
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.4.2.v20130514-5956486): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: ORA-00001 : unique constraint (****.PK_*****) violated
我们的主键是由EclipseLink生成的,该链接使用Oracle序列进行分配。我仔细检查了一下,
以下是在我们的Java类中如何定义此序列的示例:
1 2 3 4 5 | @Id @SequenceGenerator(name ="SOME_GENERATOR", sequenceName ="SOME_SEQ", allocationSize = 10000, initialValue = 1) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator ="SOME_GENERATOR") @Column(name ="ID") private Long id; |
这是序列创建的SQL语句:
1 | CREATE SEQUENCE SOME_SEQ INCREMENT BY 10000 START WITH 10000; |
当然,该问题很难重现,只能随机发生。服务器负载很重时,这种情况似乎更经常发生。
我不知道这是否相关,但是这里有一些额外的信息:
- 我们有两个服务器访问同一个数据库
- 主键定义被许多类继承
我首先想到的是这与并发有关,但是对于这样一个成熟的框架而言,这似乎是一个相当严峻的问题。我应该验证或可能忽略的任何其他内容?
我看过相关的文章,例如这些文章:
- 使用EntityManagerFactory导致重复的主键异常
- 使用@GeneratedValue作为主键的持久性错误
- JPA不会引发EntityExistsException,而是会生成重复的行(自动生成的PK)
但是对于我来说,它们似乎无济于事。
我们最终找到了对此问题的解释。
实际的问题出在以下事实:数据库已重置(因此删除表和序列,然后重新初始化所有内容),但应用程序服务器不是。
- 因此,一开始,EclipseLink仍然从先前的预分配队列中分配ID(例如[100016,100017,100018,100019,100020])。
- 然后,当它到达结尾时,它从初始值([1,2,3,4,5,...])重新开始。
- 一段时间之后,EclipseLink到达了已经分配的ID,因此我们遇到了违反主键约束的情况。
由于id由多个实体使用,因此行为是相当随机的,并且实际上不太可能发生冲突。
现在,开发人员和测试人员将始终在重置数据库后重新启动应用程序服务器。