У меня проблема с несколькими EntityManager.merge()
вызовами в одной транзакции. Это использует базу данных Oracle. Ни один объект еще не существует. Объекты:
public class A {
@Id
@Column("ID")
public Long getID();
@OneToOne(targetEntity = B.class)
@JoinColumn("ID")
public B getB();
}
public class B {
@Id
@Column("ID")
public Long getID();
}
Код слияния выглядит примерно так:
@Transactional
public void create(Object A, Object B) {
Object A = entitymanager.merge(A);
B.setId(A.getId());
entitymanager.merge(B);
}
Идентификатор объекта A генерируется с помощью последовательности и правильно устанавливается на B. При просмотре журнала слияние на A вызывается до вызова слияния на B. Существует преобразование @OneToOne из A в B. Однако в конце метода, когда он идет на фактическую фиксацию, он пытается сделать INSERT на B, прежде чем он сделает INSERT на A, который выдает IntegrityConstraintViolation
потому что «родительский ключ не найден».
Если я добавлю entitymanager.flush()
до 2-го слияния, все будет нормально.
@Transactional
public void create(Object A, Object B) {
Object A = entitymanager.merge(A);
entitymanager.flush();
B.setId(A.getId());
entitymanager.merge(B);
}
Однако flush()
- это дорогостоящая операция, которая не обязательна. Все это должно происходить в одной и той же транзакции (распространение по умолчанию @Transactional
равно Propagation.REQUIRED
).
Есть идеи, почему это не работает без flush()
, и почему, хотя слияние на A происходит до слияния на B, фактическая INSERT на COMMIT меняется на противоположную?