Удаление потомков в иерархической структуре и проблема порядка выполнения запросов jpa - PullRequest
0 голосов
/ 16 июня 2019

У меня есть Contract и Phase.

Contract хранит список Phase.

Phase реализован в виде иерархической структуры: имеет родителя Phase и список дочерних элементов Phase.

Код отображения:

public class Contract {
    @OneToMany(mappedBy = "contract", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Phase> phases;
}

public class Phase {
    private String display_no;

    @ManyToOne
    @JoinColumn(name = "contract_no", referencedColumnName = "contract_no", insertable = false, updatable = false)
    private Contract contract;

    @ManyToOne
    @JoinColumns({
        @JoinColumn(name = "contract_no", referencedColumnName = "contract_no", insertable = false, updatable = false),
        @JoinColumn(name = "parent_phase_no", referencedColumnName = "phase_no", insertable = false, updatable = false) })
    private Phase parentPhase;

    @OneToMany(mappedBy = "parentPhase", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Phase> subPhases;
}

Предположим, у меня есть Contract CTR1 и его Phase (phase_no | display_no):

  • 1 | 1
    • 2 | 1,1
      • 3 | 1.1.1
      • 4 | 1.1.2
    • 5 | 1.2
  • 6 | 2

Затем я хочу удалить Phase 1.1 и его дочерние элементы. У меня есть переменная с именем selectedPhses, содержащая фазы, которые будут удалены в дочернем родительском порядке: [1.1.2, 1.1.1, 1.1] (потому что я думаю, что фаза может быть удалена, только если удалены ее дочерние элементы)

// selectedPhses = [1.1.2 , 1.1.1 , 1.1]
for (Phase phs : selectedPhses) {
    Phase parent = phs.getParentPhase();

    // delete phase from parent
    if (parent != null) {
        parent.getSubPhases().remove(phs);
    }

    // delete phase from contract
    contract.getPhases().remove(phs);

    // entityManager.flush();
}

Как я и ожидал из приведенного выше кода, сначала будет удалено 1.1.2, затем 1.1.1, а последним - 1.1. Я также создаю слушателя на @PreRemove, чтобы увидеть порядок удаления. Это правильно (4 -> 3 -> 2):

DEBUG [2019-06-16 22:20:42,555] th.entity.Phase(preRemove:529) - Removing th.entity.Phase[PK: (contract_no=CTR1;phase_no=4;)];
DEBUG [2019-06-16 22:20:42,555] th.entity.Phase(preRemove:529) - Removing th.entity.Phase[PK: (contract_no=CTR1;phase_no=3;)];
DEBUG [2019-06-16 22:20:42,556] th.entity.Phase(preRemove:529) - Removing th.entity.Phase[PK: (contract_no=CTR1;phase_no=2;)];

Но когда jpa выполняет запрос DELETE, порядок отличается от приведенного выше журнала (2 -> 4 -> 3):

executing prepstmnt 661533276 DELETE FROM Phase WHERE contract_no= ? AND phase_no= ? [params=(String) CTR1, (int) 2]
executing prepstmnt 661533276 DELETE FROM Phase WHERE contract_no= ? AND phase_no= ? [params=(String) CTR1, (int) 4]
executing prepstmnt 661533276 DELETE FROM Phase WHERE contract_no= ? AND phase_no= ? [params=(String) CTR1, (int) 3]

Порядок выполнения нарушает ограничение БД и создает исключение. Я проверил, добавив flush() в конце цикла for (строка комментария), и это сработало. Но многие люди не рекомендуют flush(), поэтому я не буду его использовать.

Вопрос:

  1. Почему в заказах есть разные? Jpa переупорядочивает запрос?
  2. Как я могу избежать этого и заставить jpa работать с порядком, который я ожидал? Является ли мое отображение правильным?

Спасибо !!

...