Une longue question, merci de me soutenir.
Nous utilisons Spring + JPA pour une application Web. Mon équipe débat sur l'injection de EntityManagerFactory
dans le GenericDAO
(un DAO basé sur Generics quelque chose sur les lignes fournies par APPFUSE, nous n'utilisons pas JpaDaosupport
pour une raison quelconque) sur l'injection un EntityManager
. Nous utilisons la "persistance gérée par l'application".
L'argument contre l'injection d'un EntityManagerFactory
est que c'est trop lourd et n'est donc pas nécessaire, le EntityManager
fait ce dont nous avons besoin. De plus, comme Spring créerait une nouvelle instance d'un DAO pour chaque demande Web (j'en doute), il n'y aura pas de problèmes de concurrence car dans la même instance EntityManager
est partagée par deux threads.
L'argument en faveur de l'injection d'EFM est que c'est une bonne pratique par-dessus tout, il est toujours bon d'avoir une poignée pour une usine.
Je ne sais pas quelle est la meilleure approche, quelqu'un peut-il m'éclairer?
Les avantages et les inconvénients d'injecter EntityManagerFactory vs EntityManager sont tous expliqués dans les documents Spring ici , je ne sais pas si je peux améliorer cela.
Cela dit, certains points de votre question devraient être clarifiés.
... Spring créerait une nouvelle instance d'un DAO pour chaque demande Web ...
Ce n'est pas correct. Si votre DAO est un bean Spring, alors c'est un singleton, sauf si vous le configurez autrement via l'attribut scope
dans la définition du bean. Instancier un DAO pour chaque demande serait fou.
L'argument pour injecter EMF est que c'est une bonne pratique par-dessus tout, il est toujours bon d'avoir un handle vers une usine.
Cet argument ne tient pas vraiment la route. Les bonnes pratiques générales indiquent qu'un objet doit être injecté avec le minimum de collaborateurs dont il a besoin pour faire son travail.
Je dépose ce que j'ai finalement rassemblé. Dans la section " Implémentation des DAO basés sur du JPA ordinaire " dans la référence Spring:
Bien que les instances EntityManagerFactory soient thread-safe, les instances EntityManager ne le sont pas. Le JPA EntityManager injecté se comporte comme un EntityManager extrait de l'environnement JNDI d'un serveur d'applications, tel que défini par la spécification JPA. Il délègue tous les appels à l'EntityManager transactionnel actuel, le cas échéant; sinon, il revient à un EntityManager nouvellement créé par opération, ce qui rend son utilisation thread-safe.
Cela signifie que selon les spécifications JPA, les instances EntityManager ne sont pas thread-safe, mais si Spring les gère, elles sont rendues thread-safe.
Si vous utilisez Spring, il est préférable d'injecter EntityManagers au lieu de EntityManagerFactory.
Je pense que cela a déjà été bien couvert, mais juste pour renforcer quelques points.
Le DAO, s'il est injecté par Spring, est un singleton par défaut . Vous devez explicitement définir la portée sur prototype pour créer une nouvelle instance à chaque fois.
Le gestionnaire d'entités injecté par @PersistenceContext est thread-safe .
Cela étant dit, j'ai eu quelques problèmes avec un DAO singleton dans mon application multi-thread. J'ai fini par faire du DAO un bean instancié et cela a résolu le problème. Ainsi, bien que la documentation puisse dire une chose, vous souhaiterez probablement tester votre application à fond.
Suivi:
Je pense qu'une partie de mon problème est que j'utilise
@PersistenceContext(unitName = "unit",
type = PersistenceContextType.EXTENDED)
Si vous utilisez PersistenceContextType.EXTENDED, n'oubliez pas que vous devez, si je comprends bien, fermer manuellement la transaction. Voir this thread pour plus d'informations.
Un autre suivi:
L'utilisation d'un DAO instancié est une très mauvaise idée. Chaque instance du DAO aura son propre cache de persistance et les modifications apportées à un cache ne seront pas reconnues par les autres beans DAO. Désolé pour les mauvais conseils.
J'ai trouvé que la définition de l'annotation @Repository Spring sur nos DAO et le fait que EntityManager soit géré par Spring et injecté par l'annotation @PersistenceContext est le moyen le plus pratique pour que tout fonctionne correctement. Vous bénéficiez de la sécurité des threads du EntityManager partagé et de la traduction des exceptions. Par défaut, l'EntityManager partagé gère les transactions si vous combinez plusieurs DAO à partir d'un gestionnaire par exemple. À la fin, vous constaterez que vos DAO deviendront anémiques.