Flush () требуется для нескольких слияний Eclipselink в одной транзакции? - PullRequest
1 голос
/ 16 декабря 2011

У меня проблема с несколькими 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 меняется на противоположную?

Ответы [ 3 ]

0 голосов
/ 19 декабря 2011

Как упомянул Фрэнк, код, который вы показали, не устанавливает отношения A-> B, поэтому у поставщика нет никакого способа узнать, что этот объект B должен быть вставлен до того, как A. думаю, что вообще A нужно вставить первым.

Откладывание ограничений может быть сделано для некоторых баз данных и относится к настройке базы данных для отсрочки обработки ограничений до конца транзакции. Если вы отложите или удалите ограничения, вы сможете увидеть, является ли генерируемый SQL правильным, или есть другая проблема с пропущенным кодом и отображениями.

0 голосов
/ 19 декабря 2011

Похоже, что слияния выполняются в алфавитном порядке (по крайней мере, это одна из возможностей), если только нет двунаправленных аннотаций @OneToOne.

Ранее:

public class A {
    @OneToOne(targetEntity = B.class)
    @JoinColumn("ID")
    public B getB();
}

public class B {
    @Id
    @Column("ID")
    public Long getID();
}

Сейчас:

public class A {
    @OneToOne(targetEntity = B.class)
    @JoinColumn("ID")
    public B getB();
}

public class B {
    @Id
    @Column("ID")
    public Long getID();

    @OneToOne(targetEntity = A.class)
    @JoinColumn("ID")
    public A getA();
}

Для того, что я делаю, не имеет значения, что у B есть способ получить A, но я все еще не понимаю, почему аннотации в A не достаточны.

0 голосов
/ 17 декабря 2011

Если сущности A и B не имеют отношения (т. Е. @OneToOne, @OneToMany, ...), то поставщик сохраняемости не может вычислить правильный порядок вставки.IIRC EclipseLink не использует порядок создания объектов при отправке операторов SQL в базу данных.

Если вы хотите отказаться от использования flush (), просто установите отложенные ограничения.

...