Перед полным объяснением небольшая заметка: попробуйте опубликовать код, который на самом деле компилируется и работает как рекламируется.
- Ваш
main()
не компилируется, - вы не устанавливаете полную связь между родителем и ребенком.
- Также попробуйте явно разграничить транзакции в опубликованном примере.
Как работает ваш код
Вы вызываете save в репозитории.Ниже этот метод вызывает entityManager.merge()
, так как вы сами установили идентификатор.Объединение вызывает SQL Выберите, чтобы проверить, существует ли объект, и впоследствии вызывает вставку или обновление SQL для объекта.(Предложения, которые сохраняются с объектом с идентификатором, который существует в db, неверны)
Чтобы решить эту проблему
Child child = new Child();
child.parent = parent;
child.childPK.cid = 1;
child.childPK.parentPk = 1;
Это также объясняет, почему код работает, когда вы меняете PK Child на long - нет способа испортить его и получить неполный PK.
ПРИМЕЧАНИЕ
Приведенное выше решение создает беспорядок с сиротами.
Я все еще думаю, что оригинальное решение лучше, поскольку сироты удалены.Кроме того, добавление обновленного решения в оригинальное решение является полезным обновлением.Удаление всего списка и его повторная вставка вряд ли будут работать хорошо под нагрузкой.К сожалению, он удаляет список при первом слиянии родителя и повторно добавляет их при втором слиянии родителя.(Вот почему очистка не нужна)
Еще лучше, просто найдите родительскую сущность и внесите в нее обновления (как предлагают другие ответы).
Еще лучше, попробуйте взглянуть на решение и добавить / заменить только определенные дочерние элементы родителя, а не смотреть на родительский элемент и его дочерние элементы.Это, вероятно, будет наиболее производительным.
Исходное решение
Я предлагаю следующее (обратите внимание, что полная замена списка детей не разрешена, так как это Hibernate-прокси).
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
public List<Child> children = new ArrayList<>();
@SpringBootTest
public class ParentOrphanRepositoryTest {
@Autowired
private ParentOrphanRepository parentOrphanRepository;
@Test
public void testDoubleAdd() {
addEntity();
addEntity();
}
@Transactional
public void addEntity() {
Parent parent = new Parent();
parent.pid = 1;
parent.name = "Parent 1";
parent = parentOrphanRepository.save(parent);
Child child = new Child();
List<Child> childList = new ArrayList<>();
child.parent = parent;
child.childPK.cid = 1;
child.name = "Child 1";
childList.add(child);
// parent.children.clear(); Not needed.
parent.children.addAll(childList);
parentOrphanRepository.save(parent);
parentOrphanRepository.flush();
}
}