我遇到问题duplicate key value violates unique constraint "my_obj_pk"
。
其中my_obj_pk
是映射到Spring实体并基于id
的表的主键:
@Entity
@Table(name = "MY_TABLE")
@SequenceGenerator(name = "MY_OBJ_SEQ", allocationSize = 500)
public class MyObject {
@Id
@GeneratedValue(generator = "MY_OBJ_SEQ", strategy = GenerationType.TABLE)
private Long id;
...
}
例外是:
Internal Exception: java.sql.BatchUpdateException: Batch entry 1 INSERT INTO MY_TABLE (ID, ...) VALUES (257521, ...) was aborted: ERROR: duplicate key value violates unique constraint "my_obj_pk"
Detail: Key (id)=(257521) already exists. Call getNextException to see other errors in the batch.
Error Code: 0
Call: INSERT INTO MY_TABLE (ID, ...) VALUES (?, ...)
bind => [8 parameters bound]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:524)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:478)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:272)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy341.saveMyObject(Unknown Source)
at MyWorker.doWork(MyWorker.java:172)
at java.util.concurrent.CompletableFuture$AsyncRun.run$$$capture(CompletableFuture.java:1626)
... 4 more
Caused by: javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.7.3.v20180807-4be1041): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.BatchUpdateException: Batch entry 1 INSERT INTO MY_TABLE (ID, ...) VALUES (257521, ...) was aborted: ERROR: duplicate key value violates unique constraint "my_obj_pk"
Detail: Key (id)=(257521) already exists. Call getNextException to see other errors in the batch.
Error Code: 0
您可能会看到JPA实现是Eclipse Persistence Services - 2.7.3.v20180807-4be1041
。数据库是PostgreSQl 9.4.0。
MyWorker
是一个Spring组件,它每10分钟产生35个CompletableFuture
对象,这些对象通过具有20个线程的执行程序运行。每个这样的Future
会多次调用
Spring Service MyServiceImpl
,其中服务中的每个功能都用Transactional
和PermitAll
进行注释。这些函数通过Repository
和X
这两个不同的Collection<Y>
对象来拉取,
如果还不存在,则会创建这些对象的一部分,但是不会显式保存。
X
具有以下内容:
@OneToMany(mappedBy = "xObj", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
List<MyObject> myObjects;
Y
具有以下内容:
@OneToMany(mappedBy = "yObj", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
List<MyObject> myObjects;
MyObject
具有:
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "X_ID")
private X xObj;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "Y_ID")
private Y yObj;
因此,基本上,X
和Y
的列表中包含MyObject
和X
的{{1}}
最后,每个Y
分别从同一Future
调用saveMyObject()
和X
的{{1}}:
Collection<Y>
在任何地方都没有手动分配ID ...
到目前为止,我还不知道如何复制MyObject的PK。如果可能的话,我宁愿不要触摸MyServiceImpl
属性,因为这些类已经存在很长时间了
并且被广泛使用。谢谢!
编辑:我要补充一点,在同一@Override
@PermitAll
@Transactional
public void saveMyObject(X x, Collection<Y> colY) {
xRepository.save(x);
yRepository.save(colY);
final List<MyObject> myObjects = x.getMyObjects() == null ?
new ArrayList<>(colY.size()) : x.getMyObjects();
colY.forEach(p -> {
MyObject o = new MyObject();
myObjects.add(o);
List<MyObject> pobj = p.getMyObjects();
if (pobj == null) {
pobj = new ArrayList<>();
p.setMyObjects(pobj);
}
pobj.add(o);
});
x.setMyObjects(myObjects);
myObjectsRepository.save(myObjects);
}
和CASCADE
上可能使用不同的Future
,所以设计可能不太正确