#jpa #eclipselink
#jpa #eclipselink
Вопрос:
Возможно, я чрезмерно упрощаю это, предоставляя вам только небольшой фрагмент кода (и я опубликую больше, если это так), но я полагаю, что изначально чем меньше, тем лучше:
У меня есть объект типа Asset, который имеет поле типа Location, которое также является объектом. Когда я устанавливаю местоположение ресурса, я должен также установить местоположение его дочерних элементов.
Location location = asset.getLocation();
em.merge(location);
em.flush();
childAsset.setLocation(asset.getLocation());
em.flush();
Когда я запускаю flush (), я получаю следующее исключение:
Внутреннее исключение: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: нарушено уникальное ограничение (SWRADMIN.LOCATION_PK)
Мой вопрос в том…почему этот объект Location вообще пытается сохраниться? Все, что я делаю, это устанавливаю переменную в объекте.
Раньше это работало нормально, но мы только что переключились на использование Eclipselink и Java EE 6, и возникла эта проблема.
Решение?: Я использовал идею «отсоединения» снизу и внес следующее изменение:
Location location = asset.getLocation();
em.detach(childAsset);
childAsset.setLocation(asset.getLocation());
em.merge();
em.flush();
и это сработало! Однако я не понимаю, почему…можно подумать, что автоматическая синхронизация будет делать то же самое.
Ответ №1:
Если объект находится в управляемом состоянии, то entity manager синхронизирует его с базовой базой данных, сохраняя объект неявно (возможно, в конце транзакции) или явно при вызове em.flush()
метода.
Вы можете использовать em.detach(entity)
для отсоединения одного объекта или em.clear()
для отсоединения всех объектов. Тогда изменения, вносимые в объект / сущности, не будут отражены в базе данных.
Чтобы лучше справиться с этим, вы можете использовать BMT (Bean Managed Transaction Управляемая транзакция), где вам нужно вручную обрабатывать сохранение сущности, транзакцию.
Редактировать :
Location location = asset.getLocation();
childAsset.setLocation(location);
em.merge(childAsset);
em.flush();
Комментарии:
1. Итак, я выполнил em.detach (дочерний набор), затем childAsset.setLocation (местоположение), затем em.merge (дочерний набор), и это сработало! Мой вопрос … почему? Разве не с этого должна была начинаться синхронизация?
2. Глядя на ваш исходный код, em.merge (location) и затем em.flush() сохранят ‘location’ в базе данных, затем установят его в childAsset и затем снова сбросят, что вызовет ‘Исключение SQLIntegrityConstraintViolationException’, поскольку ‘location’ уже присутствует в базе данных. Измененный ответ, см. часть редактирования.
Ответ №2:
Итак, местоположение — это существующее местоположение или новый объект?
Как это было прочитано, было ли оно прочитано из другой транзакции или менеджера сущностей или каким-то образом отсоединено? Если это так, то вам нужно перечитать (найти) или объединить его.
Если это новое и связано с управляемым объектом, то да, его должен записать flush.
Комментарии:
1. Привет, Джеймс. Я добавил больше кода к сообщению. Местоположение — это существующее местоположение. Чтобы быть уверенным, я выполнил merge () для объекта location, а затем flush (). Это сработало нормально. Когда я назначил его дочернему набору, а затем выполнил другой flush (), я получаю ту же ошибку.
Ответ №3:
Из кода похоже, что вы используете неуправляемую версию location и связываете ее с дочерним набором. Если ваше отношение childAsset-> Location помечено как каскадное сохранение, то спецификация, требующая продолжения, будет вызвана для Location при сбросе или фиксации. Поскольку Location не является управляемым объектом, для его сохранения требуется исключение.
Когда местоположение управляется (например, при вызове merge в дочернем наборе или если вы использовали управляемый экземпляр Location, возвращенный из вызова em.merge(location);), операция сохранения в дочернем наборе-> Location не выполняется.
Не связывайте неуправляемые объекты со связями, помеченными как каскадные, сохраняются.