JPA сохраняет уже сохраненные объекты из отношения ManyToMany

#java #jpa #jpql

#java #jpa #jpql

Вопрос:

У меня есть отношение @ManyToMany между классом A и классом B: класс A ссылается на коллекцию экземпляров класса B, и это отношение настроено как CascadeType.ALL. Поэтому, когда A A сохраняется в диспетчере сущностей, экземпляры B, на которые ссылается A, также сохраняются.

Оба A и B имеют идентификатор, объявленный с помощью GenerationType .Стратегия идентификации для использования auto_inc из базы данных MySQL.

Проблема заключается в :

  1. Я создаю новый
  2. Я загружаю некоторые существующие объекты B из EntityManager с помощью JPQL
  3. Я добавляю объекты B в коллекцию Bs

=> JPA пытается сохранить объекты B, хотя они уже сохранены (они только что были загружены).

Я не понимаю, почему он пытается сохранить их снова, они были правильно загружены, и их автоматически сгенерированный идентификатор также был хорошо загружен.

Объявление ManyToMany :

 @ManyToMany(cascade={CascadeType.ALL})
@JoinTable(name = "ENTRY_ENTITIES", joinColumns =
@JoinColumn(name = "ENTRY", referencedColumnName = "ID"),
inverseJoinColumns = @JoinColumn(name = "ENTITY", referencedColumnName = "ID"))
private List<Entity> entities;
  

Запрос для загрузки существующих объектов из базы данных :

 T result = (T) entityManager.createQuery("SELECT x FROM "   entityName   " x WHERE x.externalId='"   externalId   "'").getSingleResult();
  

Сохраняющийся :

 UserTransaction transaction = getTransaction();
try {
    transaction.begin();
    entityManager.persist(entity);
    transaction.commit();
} catch (Throwable t) {
    Logger.getLogger(JpaDao.class.getName()).log(Level.SEVERE, null, t);
}
  

Большое спасибо за вашу помощь!

Комментарии:

1. Покажите нам код объектов, их сопоставление и код, обеспечивающий сохранение. Сообщите нам, какой движок JPA вы используете. Обратите внимание, что cacscade ALL на ManyToMany неверен: вы не хотите, чтобы все Bs A были удалены при удалении A, поскольку на Bs ссылаются другие As .

Ответ №1:

Я не совсем уверен, что это является причиной вашей проблемы, но вы должны включить все это в транзакцию. Не только часть сохранения:

 start transaction
load B from DB
create new A
add B to A
commit transaction
  

Как я уже говорил в своих комментариях, у вас есть другие проблемы с дизайном и кодированием:

  • CascadeType .ВСЕ неправильно в ассоциации ManyToXxx. Вы не хотите, чтобы все Bs A были удалены при удалении A, поскольку на эти Bs ссылаются другие As. Это приведет к нарушению ограничений (в лучшем случае) или к несовместимости базы данных (в худшем случае, если у вас не определено ограничение)
  • Не используйте конкатенацию строк в своих запросах. Используйте параметризованные запросы. Это позволит избежать проблем с цитированием и атак с использованием инъекций: SELECT x FROM A x WHERE x.externalId = :externalId

Комментарии:

1. Спасибо, к сожалению, я не могу поместить загрузку и сохранение в одну и ту же транзакцию, потому что сначала я загружаю Bs, затем отображаю их на странице JSF, а затем, когда пользователь отправляет форму, я создаю A и сохраняю ее. Так что это не в том же http-запросе. Или я должен загрузить их снова во второй раз?

2. Нет, вам не нужно их перезагружать. Какой движок JPA вы используете? Является ли ассоциация двунаправленной? Покажите нам полный код обоих объектов и код, используемый для их связывания.

Ответ №2:

Эти объекты B могут быть частью другого контекста сохранения в момент времени, когда вы добавляете их в A. Пробовали ли вы использовать операцию слияния для объектов B после запуска транзакции, прежде чем добавлять их в свой объект A.