У меня есть 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):
Затем я хочу удалить 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()
, поэтому я не буду его использовать.
Вопрос:
- Почему в заказах есть разные? Jpa переупорядочивает запрос?
- Как я могу избежать этого и заставить jpa работать с порядком, который я ожидал? Является ли мое отображение правильным?
Спасибо !!