У меня проблема, где я получаю 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
и Y
внутри
В конце каждый Future
звонит saveMyObject()
с X
и Collection<Y>
с одного и того же 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);
}
НИЧЕГО не назначено вручную.
На данный момент я не понимаю, как PK MyObject может дублироваться. Я предпочитаю, если возможно, не трогать свойства CASCADE
, так как эти классы существуют в течение длительного времени
и широко используются. Спасибо!
Редактировать: я должен добавить, что разные Future
могут работать на тех же Y
и MyObject
, поэтому, возможно, дизайн не совсем правильный