JPA cascade de persister et de références à des détaché entités jette PersistentObjectException. Pourquoi?
J'ai une entité Toto qui fait référence à une entité Bar:
@Entity
public class Foo {
@OneToOne(cascade = {PERSIST, MERGE, REFRESH}, fetch = EAGER)
public Bar getBar() {
return bar;
}
}
Lorsque je persiste un nouveau Foo, il peut obtenir une référence à un nouveau Bar ou dans un Bar. Quand il arrive un existant Bar, qui se trouve être détaché, mon fournisseur JPA (Hibernate) jette l'exception suivante:
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.example.Bar
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:102)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:636)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:628)
at org.hibernate.engine.EJB3CascadingAction$1.cascade(EJB3CascadingAction.java:28)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:291)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:239)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:192)
at org.hibernate.engine.Cascade.cascade(Cascade.java:153)
at org.hibernate.event.def.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:454)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:288)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130)
at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49)
at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:154)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:110)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:645)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:619)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:623)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:220)
... 112 more
Quand je l'ai assurez-vous que la référence à la Bar est réussi (ci-joint) ou quand je omettre la cascade de PERSISTER dans la relation, tout fonctionne bien.
Ni solution est cependant 100% satisfaisante. Si je retire la cascade persistent, évidemment je ne peux pas conserver un Foo avec une référence à une nouvelle Barre de plus. De renvoi de Barre réussi nécessite un code comme celui-ci avant de la persistance:
if (foo.getBar().getID() != null && !entityManager.contains(foo.getBar())) {
foo.setBar(entityManager.merge(foo.getUBar()));
}
entityManager.persist(foo);
Pour une seule Barre cela pourrait ne pas sembler une grosse affaire, mais si je dois prendre tous en compte les propriétés des comme ça, je vais finir avec assez horrible code qui semble à la défaite de la raison de l'utilisation de l'ORM en premier lieu. Je pourrais aussi bien persister mon objet graphique manuellement à l'aide de JDBC de nouveau.
Lorsque compte tenu de l'actuelle référence de la Barre de la seule chose JPA a à faire est de prendre son ID et l'insérer dans une colonne de la table qui contient Foo. C'est exactement ce que lorsque la Barre est attaché, mais lève l'exception lorsque la Barre est détachée.
Ma question est; pourquoi faut-il le Bar pour être fixé? Sûrement son ID ne change pas lors de l'occurrence de la Barre des transitions de détaché, attaché de l'état, et que l'ID semble être la seule chose nécessaire ici.
Est-ce peut-être un bug en mode veille prolongée ou ai-je raté quelque chose?
- Vous pouvez supprimer PERSISTENT en cascade, et le check if (id == null) { em.persiste(foo.getBar ()) }, Au moins il rend votre si de plus simple.
- Vrai, ce serait en effet, il est un peu plus simple. Merci pour la suggestion.
- Bien sûr, même avec un peu plus simple si les déclarations, la persistance de code serait encore à marcher à travers la totalité de l'objet graphique, qui est quelque chose que je suis désespérément essayer de les éviter.
Vous devez vous connecter pour publier un commentaire.
Vous pouvez utiliser
merge()
au lieu depersist()
dans ce cas:Lorsqu'il est appliqué à la nouvelle instance,
merge()
rend persistante (en fait - retourne l'instance persistante avec le même état), et fusionne en cascade de références, comme vous essayez de le faire manuellement.Si je comprends bien, vous avez juste besoin de la
Bar
de référence pour permettre à la nouvelleFoo
pour avoir la valeur de clé étrangère (à l'existantBar
) lors de la persistance. Il y a une JPA méthode sur laEntityManager
appelégetReference()
qui pourraient vous être utiles pour ce cas. LegetReference()
méthode est similaire àfind()
sauf qu'il ne prend pas la peine de retourner une instance gérée (deBar
), sauf si il arrive à être déjà mis en cache dans le contexte de persistance. Il sera de retour d'un objet proxy qui saura satisfaire vos principaux besoins afin de conserver leFoo
objet. Je ne suis pas sûr si c'est le genre de solution que vous espérez, mais essayez-le et voyez si cela fonctionne pour vous.J'ai aussi remarqué à partir de votre code que vous êtes à l'aide de la propriété "style" de l'accès au lieu de "le terrain" le style de l'accès par les annotations de votre méthode de lecture (pour la
Bar
relation). Une raison pour cela? Il est recommandé que vous annoter les membres plutôt que de les getters pour des raisons de performances. Il est censé être plus efficace pour le fournisseur JPA pour accéder au champ directement, plutôt que par des getters et setters.EDIT:
Que quelqu'un d'autre a mentionné, à l'aide d'une cascade de fusion() va persister nouvelles entités ainsi que la fusion modifié entités et de rattacher détaché les entités qui ont une relation avec la FUSION option cascade. À l'aide de PERSISTER option cascade de ne pas rattacher quoi que ce soit ou de fusion de rien et est destinée à être utilisée lorsque c'est le comportement que vous souhaitez.