2010-11-19 19 views
1

J'utilise Spring et JPA dans mon application, mais ces derniers temps, je me suis un peu perdue à utiliser les collections de DomainObjects.Printemps @Transactional - Utilisation des collections après persistance/fusion

Par exemple:

Imaginez que nous avons deux tables: parents et enfants. Un parent peut avoir plusieurs Childs (@OneToMany) et un enfant ne peut avoir qu'un parent (@ManyToOne). J'utilise Spring pour gérer mon contexte de transaction dans mon ServiceLayer (pas dans le modèle Dao - Unit of Work), qui est configuré dans context-application.xml par aop: config. Les collections enfants sont marquées comme Collection LazyLoading. Tout a bien fonctionné jusqu'à maintenant.

Le problème est maintenant le suivant:

je besoin d'une méthode dans mon service, ce qui crée un nouveau parent et d'appeler une autre méthode dans la même transaction pour faire des calculs avec le parent nouvellement créé. Dans ces calculs, je dois appeler parent.getChildren(). Évidemment, cela devrait retourner un Set vide, car un nouveau Parent ne peut pas avoir d'enfants au moment de la création, mais pour une raison quelconque, je récupère 'null' au lieu d'un ensemble vide, ce qui aboutit à une exception NullpointerException

Pour créer un parent, j'ai essayé Les méthodes EntityManager suivantes:

  1. en utilisant entityManager.persist (parent) - n'a pas fonctionné, la collection est nulle après la création.
  2. En utilisant entityManager.merge (parent) et renvoyer la nouvelle instance gérée de parent - avait le même résultat.

Les deux ont abouti à un ensemble nul. Mais je suis assez sûr, que ces appels sont dans la même transaction, car en dehors d'une transaction je recevrais une exception LazyLoadingException (au lieu de NullPointer) qui est (afaik) correcte à ce stade. D'autre part, en utilisant un parent déjà existant avec entityManager.findById() a abouti à une collection existante (ce qui signifie que cela fonctionne comme prévu) .Pour résoudre le problème j'ai essayé plusieurs approches jusqu'à ce que je trouve un travail:

entityManager. refresh (parent) après fusion/persistante élaborée, puisque les collections sont initialisées lors de l'appel à refresh.

Mais dans les tutoriels, je n'ai jamais vu que cette méthode est utilisée. Dans JavaDoc de EntityManager, il a été dit que cette fusion retourne les nouveaux beans gérés, tandis que persist rend la référence elle-même gérée. Je devinais géré signifierait que je peux déjà utiliser ces collections dans une transaction. Pourquoi les collections ne sont-elles pas initialisées au moment de la création? Dois-je les initialiser par moi-même dans le constructeur de Parent par exemple?

Je me demande, si j'ai fait quelque chose de mal, ou est-ce le bon moyen d'appeler refresh() après avoir créé une nouvelle instance de parent? Si oui devinez que je dois juste séparer entre persister et mettre à jour comme il est recommandé dans l'article suivant: JPA Implemenentations et rafraîchir l'objet nouvellement créé après persister? Ou devrais-je juste rafraîchir l'objet, quand j'ai vraiment besoin d'utiliser la collection?

J'espère que vous pourriez me suivre et j'apprécierais des recommandations ou des solutions pour moi d'obtenir clair (er) sur ce point.

Merci beaucoup,
ymene

Répondre

4

-vous vos champs de initialize collection?

@OneToMany 
private Set<Foo> foos = new HashSet<Foo>(); 

par opposition à un peu

@OneToMany 
private Set<Foo> foos; 

Parce que la dernière volonté de NPE jeter cours.

+0

encore je prends généralement cette approche en déclarant toutes les instances de collection comme « finale » je ne peux pas oublier d'initialiser leur. –

+0

Cela n'empêcherait-il pas le fournisseur JPA d'injecter ses collections personnalisées (PersistentBag, etc.)? –

+0

Je devinais que l'EntityManager de JPA devrait s'occuper de l'initialisation de ces champs de collection, donc je n'ai pas initilisé les champs de collection directement. Mais serait une solution que je peux vivre avec. Merci pour l'indice. Je vais essayer lundi, pour voir si ça marche. – crusam

0

Essayez d'utiliser CascadeType.ALL dans la classe parente.

@OneToMany(cascade = CascadeType.ALL) 
public List<Child> getChildren() { 
    return children; 
} 

Ensuite, utilisez OpenSessionInViewFilter. Cela garantira que vous pouvez appeler parent.getChildren() même à partir des vues et la session d'hibernation reste ouverte jusqu'à ce que la réponse soit renvoyée. Ci-dessous comment vous voulez configurer dans votre web.xml

<filter> 
<filter-name>OpenSessionInViewFilter</filter-name> 
<filter-class>com.isavera.hibernate.OpenSessionFilter</filter-class> 
    <init-param> 
    <param-name>singleSession</param-name> 
<param-value>false</param-value> 
    </init-param> 
</filter> 

<filter-mapping> 
<filter-name>OpenSessionInViewFilter</filter-name> 
<url-pattern>/*</url-pattern> 
</filter-mapping> 

Hope this helps

+0

Voilà la réponse pour LazyLoadException etc, mais pas pour NullPointerException. –