Лучший способ использовать JPA с отношениями

Когда у меня есть объект с отношениями, я не знаю, как лучше всего сохранить изменения в БД.

Вот упрощенная сущность. Пожалуйста, учтите, что я внес небольшие изменения в код, чтобы опубликовать его здесь, и я мог внести некоторые ошибки.

public class Permessitemporanei implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "ID_permesso")
    private Integer iDpermesso;
    @Column(name = "Stato_permesso")
    private Integer statopermesso;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "iDpermesso")
    private Collection<Accessiconpermesso> accessiconpermessoCollection;
    @OneToOne(cascade = CascadeType.ALL, mappedBy = "iDpermesso")
    private Ingressiconpermesso  ingressiconpermesso;

Как видите, он связан с двумя другими объектами отношениями OneToMany и OneToOne. Я использую Glassfish с jta, поэтому транзакции и entityManager управляются контейнером.

В то время у меня есть отдельный (терминология JPA) экземпляр Permessitetemporanei в памяти. Я должен сохранить следующие изменения в базе данных: 1- связанный ingressiconpermesso должен быть удален 2- должен быть создан новый ingressiconpermesso 3- поле statopermesso должно быть обновлено 4- новый Accessiconpermesso должен быть добавлен в коллекцию accessiconpermessoCollection

Каков наилучший способ сделать это? Возможно, я могу внести все необходимые изменения в экземпляр Permessitetemporanei и объединить его, но у меня было много проблем с этим, и я начал думать, что сохранять изменения — неправильная сторона отношений. Для меня более естественно сохранять объект за раз, поэтому исключаются все эти cascade = CascadeType.ALL.

Предположим, что мой экземпляр Permessitetemporanei называется «permesso»; мой код примерно такой:

  1. getEntityManager().remove(permesso.ingressiconpermesso);
  2. getEntityManager().persist(новый Ingressiconpermesso);
  3. getEntityManager().merge(permesso) // после обновления поля statopermesso;
  4. getEntityManager().perist(новое разрешение Accessicon);

Очевидно, что таким образом я должен вручную обновить «permesso» в памяти со всеми изменениями, которые я сделал в базе данных.

Есть ли лучший подход?

Кстати, все отношения JPA, которые я видел, являются двунаправленными. Могу ли я сделать их однонаправленными? Другими словами, могу ли я безопасно удалить код:

@OneToMany(cascade = CascadeType.ALL, mappedBy = "iDpermesso") доступ к частной коллекцииiconpermessoCollection;

из объекта Permessitetemporanei, сохраняя его в объекте Accessiconpermesso, или я нарушаю JPA?

Спасибо, Филиппо.


person Filippo    schedule 01.08.2011    source источник


Ответы (1)


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

  1. Начать новую транзакцию.
  2. Внесите все изменения, которые я хочу внести в свои объекты в памяти.
  3. Когда закончу, расскажи EntityManager, что я сделал.
  4. Зафиксируйте транзакцию.

Но обо всем по порядку, вам, вероятно, будет намного легче, если вы получите неотсоединенный экземпляр Permessitemporanei для начала:

Permessitemporanei persistentInstance = em.find(Permessitemporanei.class, detachedInstance.getId());

Затем сделайте все свои изменения в памяти, сообщите EntityManager и зафиксируйте транзакцию:

//begin a transaction
em.getTransaction().begin();

//remember the old Ingressiconpermesso instance
Ingressiconpermesso oldIngression = persistentInstance.getIngressiconpermesso();

//create a new Ingressiconpermesso instance
Ingressiconpermesso newIngression = new Ingressiconpermesso();
//call newIngression.set...() methods here

//associate the new Ingressiconpermesso with the Permessitemporanei 
persistentInstance.setIngressiconpermesso(newIngression);

//update statopermesso
persistentInstance.setStatopermesso(7); //replace '7' with whatever the real value is

//add a new Accessiconpermesso
Accessiconpermesso accession = new Accessiconpermesso();
//call accession.set...() methods here

//associate the Accessiconpermesso with the Permessitemporanei
accession.setPermissitemporanei(persistentInstance);

//now tell the EntityManager what we did
em.remove(oldIngression);        //delete the old Ingressiconpermesso 
em.persist(newIngression);       //add the new Ingressiconpermesso 
em.persist(accession);           //add the Accessiconpermesso
em.merge(persistentInstance);    //update the Permessitemporanei

//commit the transaction
em.getTransaction().commit();

Чтобы ответить на ваш другой вопрос, нет, вам обычно не нужно аннотировать обе стороны отношений. Вы можете удалить свою аннотацию @OneToMany, если хотите, и она не должна ничего ломать (в любом случае, насколько это касается JPA... у вас вполне может быть код приложения, который полагается на наличие и правильное заполнение этого Collection и удаление сопоставления JPA конечно, сломает любой такой код). Тем не менее, я не думаю, что вы что-то выиграете, удалив его, поэтому я бы рекомендовал оставить его в покое.

person aroth    schedule 01.08.2011
comment
Большое спасибо, Aroth, также за то, что вы потратили время на написание кода для лучшего понимания. Мне понадобится немного больше помощи. Прежде всего тривиальный вопрос. Я полагаю, вы забыли обновить коллекцию accessiconpermessoCollection в памяти, не так ли? - person Filippo; 02.08.2011
comment
Во-вторых, вы выполняете операцию удаления/сохранения/слияния для всех объектов. Я полагаю, это означает, что вы не используете каскад... и мне так удобнее. - person Filippo; 02.08.2011
comment
Опять же об отношениях oneToMany, связанных с внешним ключом в БД. Если я не заинтересован в поддержании согласованности объектов в памяти, потому что я планирую обновить/перезагрузить их, нужно ли мне все еще писать persistentInstance.getAccessiconpermessoCollection.add(accession) из моего первого вопроса? Жалуется ли JPA, когда я объединяю persistInstance, если его коллекция не обновляется? - person Filippo; 02.08.2011
comment
@Filippo - Обновление коллекции accessiconpermesso в памяти необязательно. Пока новый экземпляр accessiconpermesso сохраняется, а Permessitetemporanei объединяется, коллекция в памяти должна автоматически обновляться EntityManager. И да, в целом единственное, для чего я полагаюсь на каскад, это удаление. Для других операций я всегда прямо заявляю о желаемом намерении. Я думаю, что это приводит к более четкому коду. И нет, вам не нужно звонить persistentInstance.getAccessiconpermessoCollection.add(accession), как я уже говорил. JPA должна сделать это за вас без жалоб. - person aroth; 02.08.2011