【问题标题】:Merging the same entity twice causes INSERT-Primary Key violation两次合并同一实体会导致 INSERT-Primary Key 违规
【发布时间】:2015-11-29 00:51:23
【问题描述】:

我有一个非常简单的 JAP-Entity,没有任何指向其他类的链接:

@Entity
@IdClass(TimestampNameAndGroupIdentity.class)
public class TechFitMainClassHistory extends JPABase {

    @Id
    @Basic
    protected Date timestamp;

    @Id
    @Basic
    protected String type_name;

    @Id
    @Basic
    protected String group_name;

    @Basic
    protected int count;

    ...

使用 IdClass:

public class TimestampNameAndGroupIdentity implements Serializable {
    private static final long serialVersionUID = 1L;

    public Date timestamp;
    public String type_name;
    public String group_name;

    ...

在测试中我尝试合并两次:

        em.merge(new TechFitMainClassHistory(date, "com.test.Main", "java", 2));
        em.merge(new TechFitMainClassHistory(date, "com.test.Main", "java", 2));

我收到以下错误

Caused by: org.apache.openjpa.lib.jdbc.ReportingSQLException: Violation of PRIMARY KEY constraint 'PK__TechFitM__D91CE761E4A3725C'. Cannot insert duplicate key in object 'dbo.TechFitMainClassHistory'. The duplicate key value is (PROCESS_TYPE_MAIN, 2015-09-03 00:00:00.0000000, com.ruxit.Main). {prepstmnt 261650860 INSERT INTO TechFitMainClassHistory (group_name, timestamp, type_name, count) VALUES (?, ?, ?, ?)} [code=2627, state=23000]
    at org.apache.openjpa.lib.jdbc.LoggingConnectionDecorator.wrap(LoggingConnectionDecorator.java:219)
    at org.apache.openjpa.lib.jdbc.LoggingConnectionDecorator.wrap(LoggingConnectionDecorator.java:207)
    at org.apache.openjpa.lib.jdbc.LoggingConnectionDecorator.access$1200(LoggingConnectionDecorator.java:59)
    at org.apache.openjpa.lib.jdbc.LoggingConnectionDecorator$LoggingConnection$LoggingPreparedStatement.executeBatch(LoggingConnectionDecorator.java:1215)
    at org.apache.openjpa.lib.jdbc.DelegatingPreparedStatement.executeBatch(DelegatingPreparedStatement.java:250)
    at org.apache.openjpa.jdbc.kernel.JDBCStoreManager$CancelPreparedStatement.executeBatch(JDBCStoreManager.java:1810)
    at org.apache.openjpa.jdbc.kernel.BatchingPreparedStatementManagerImpl.executeBatch(BatchingPreparedStatementManagerImpl.java:364)
    at org.apache.openjpa.jdbc.kernel.BatchingPreparedStatementManagerImpl.flushBatch(BatchingPreparedStatementManagerImpl.java:189)
    ... 39 more

不出现错误的情况有两种: * 如果我在通话之间提交 * 如果对象在调用之前已经存在

这只是简化的测试用例,实际的应用程序做类似的事情,我希望事情能根据 merge() 方法的描述工作。

那么为什么我会收到这个错误?我希望合并可以多次工作,或者这是 OpenJPA 中的错误/限制?

【问题讨论】:

  • 如前所述,这只是测试用例,在真正的应用程序中,对象的创建分布在不同的地方。据我阅读合并()的文档,它应该允许这样做。如果我在两者之间做出承诺,它就会起作用!
  • 我会说这是一个错误。您可以尝试使用 Hibernate 或 EclipseLink 等其他实现来运行测试吗?
  • 这是一个错误。您应该为此问题打开一个 JIRA。

标签: java jpa openjpa


【解决方案1】:

合并的预期行为是:

  • 在持久化上下文中查找具有相同 ID 的附加对象。
  • 如果存在,则更新并返回已附加的对象(并让给定的对象不受管理)。
  • 如果没有,请制作一个副本,将其持久化(附加)并返回(并让给定的原始实例不受管理)...

因此,您在测试用例中期望的行为应该起作用,除非两个合并调用都不是在相同的持久性上下文中进行的(事实上,两个调用之间的提交修复了您的错误导致认为是这种情况)

但是在并发上下文中,您将重现此问题,因为这两个实体将属于两个不同且因此是孤立的持久性上下文,第二个要刷新的将触发 pk 违规

【讨论】:

  • 调用是在同一个 EntityManager 实例上完成的,所以我希望它在内部使用相同的持久性上下文,所以你说这听起来像是 JPA 实现中的一个错误?
  • 也许……但在他们的网站上,他们说他们成功通过了合规性测试 (openjpa.apache.org)。 - 另见stackoverflow.com/a/12746806/2087640 - 如果您保留第一次合并调用结果的参考怎么办?
  • 另请参阅:techblog.bozho.net/how-does-merge-work-in-jpa-and-hibernate - 也许 openJPA 不会检查临时实例的持久性上下文,而只检查数据库本身?它会解释你的情况......无论如何我认为你必须查看来源的奇怪行为
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-17
  • 1970-01-01
  • 2019-09-28
  • 1970-01-01
相关资源
最近更新 更多