【问题标题】:JPA and multithread merge generate exception id already existsJPA和多线程合并生成异常id已经存在
【发布时间】:2015-07-09 11:10:12
【问题描述】:

我有一个实体,其中 id 字段是这样描述的:

@Id
@Column(name = "id", unique = true, nullable = false, insertable = true, updatable = true)
@SequenceGenerator(name = "records_id_seq", sequenceName = "records_id_seq", schema = "recorder", initialValue = 1, allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "records_id_seq")
private Long id;

似乎当多个线程尝试插入新数据时,出现了问题,因为我的应用程序想要插入 2 个具有相同 id 值的实体,而我的数据库拒绝它们,因为 id 是唯一的。

应该怎么做才能证明这一点?

有什么方法可以告诉我的实体不要尝试插入 id 值并让数据库在插入阶段自行生成它?

感谢您的帮助。

编辑:例外:

11:17:47.407 ERROR - Uncatched exception at 201507091117 for thread Thread[RecorderBase-Pool-Thread-1,5,RMI Runtime] : 
javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.0.v20140809-296a69f): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "pkey_records_id"
  Détail : Key (id)=(280) already exists.
Error Code: 0
Call: INSERT INTO recorder.records (id, name, number, DELETED, frid, frname, end, start, hid, REFERENCE, toid, toname) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    bind => [12 parameters bound]
Query: InsertObjectQuery(Record[280/20014364334653801])
    at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:159) ~[ModuleA-2.1.0.jar:na]
    at DBHelper.save(DBHelper.java:215) ~[ModuleA-2.1.0.jar:na]
    at DBHelper.save(DBHelper.java:230) ~[ModuleA-2.1.0.jar:na]
    at RecordEngine$1.run(RecordEngine.java:253) ~[ModuleA-2.1.0.jar:na]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) ~[na:1.8.0_40]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) ~[na:1.8.0_40]
    at java.lang.Thread.run(Unknown Source) ~[na:1.8.0_40]
Caused by: org.eclipse.persistence.exceptions.DatabaseException: 
Internal Exception: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "pkey_records_id"
  Détail : Key (id)=(280) already exists.
Error Code: 0
Call: INSERT INTO recorder.records (id, name, number, DELETED, frid, frname, end, start, hid, REFERENCE, toid, toname) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    bind => [12 parameters bound]
Query: InsertObjectQuery(Record[280/20014364334653801])
    at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:340) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.processExceptionForCommError(DatabaseAccessor.java:1611) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:898) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:962) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:631) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:558) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:2000) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.sessions.server.ClientSession.executeCall(ClientSession.java:298) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:242) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.insertObject(DatasourceCallQueryMechanism.java:377) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:165) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:180) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.insertObjectForWrite(DatabaseQueryMechanism.java:489) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.queries.InsertObjectQuery.executeCommit(InsertObjectQuery.java:80) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.queries.InsertObjectQuery.executeCommitWithChangeSet(InsertObjectQuery.java:90) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:301) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:899) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:798) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1802) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1784) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1735) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.sessions.CommitManager.commitChangedObjectsForClassWithChangeSet(CommitManager.java:273) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsForClassWithChangeSet(CommitManager.java:193) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:139) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:4205) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1441) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1531) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:278) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1169) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:134) ~[ModuleA-2.1.0.jar:na]
    ... 6 common frames omitted
Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "pkey_records_id"
  Détail : Key (id)=(280) already exists.
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2198) ~[ModuleA-2.1.0.jar:na]
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1927) ~[ModuleA-2.1.0.jar:na]
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255) ~[ModuleA-2.1.0.jar:na]
    at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:561) ~[ModuleA-2.1.0.jar:na]
    at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:419) ~[ModuleA-2.1.0.jar:na]
    at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:365) ~[ModuleA-2.1.0.jar:na]
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:890) ~[ModuleA-2.1.0.jar:na]
    ... 38 common frames omitted

【问题讨论】:

  • 这不应该发生:序列是安全的 :stackoverflow.com/questions/14215151/… 问题可能出在其他地方
  • 这个怎么样:@Id @GeneratedValue(strategy=GenerationType.AUTO, generator="records_id_seq") @SequenceGenerator(name="records_id_seq", sequenceName="MY_ENTITY_SEQ") private Long id;
  • 为什么您的应用程序会尝试同时插入两个具有相同 id 的对象?它们是同一个对象,还是只有 ID 相同?您是如何达到ID相同的程度的?就像 pdem 所说,我认为您的程序逻辑可能存在缺陷,这会导致问题(不一定是多线程)
  • 听起来好像多个线程必须访问相同的对象,因此您尝试从多个线程插入相同的对象。那是行不通的。
  • 在多线程中我正在创建一个新的Record实体实例,构造函数不做任何事情,并且id为null,然后我正在合并实体(它之前在数据库中不存在那)。我对每个线程都使用相同的 entityManager 会是问题吗?我为每个合并使用一个转换。

标签: java multithreading postgresql jpa


【解决方案1】:

好的,我找到了问题。

我什至尝试更改表架构,删除自动生成的列,所以没有序列,但仍然是应用程序生成值的主键。但仍有例外。

而且问题很容易解决:为每个线程使用一个新的实体管理器。

还有!不再有插入异常;)

谢谢大家。

【讨论】:

    猜你喜欢
    • 2020-10-25
    • 2011-10-07
    • 2019-03-25
    • 2023-03-23
    • 1970-01-01
    • 2014-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多