Jersey, Guice et hibernation-EntityManager thread safety
j'ai utilisé ce tutoriel de la même manière dans mon application: http://www.benmccann.com/hibernate-with-jpa-annotations-and-guice /
mon application est le service web JAX-RS qui recevra de nombreuses requêtes simultanées et fera des mises à jour à la base de données.
GenericDAOImpl.java mise en œuvre:
public class GenericDAOImpl<T> implements GenericDAO<T> {
@Inject
protected EntityManager entityManager;
private Class<T> type;
public GenericDAOImpl(){}
public GenericDAOImpl(Class<T> type) {
this.type = type;
}
@Override
public void save(T entity) {
entityManager.getTransaction().begin();
entityManager.persist(entity);
entityManager.getTransaction().commit();
}
}
si 2 threads concurrents essaient de sauver entity, je reçois
java.lang.IllegalStateException: Transaction already active
La sauvegarde fonctionne bien si je commente la transaction.
j'ai essayé d'utiliser
@Inject
protected Provider<EntityManager> entityManagerProvider;
ou
@Inject
protected EntityManagerFactory entityManagerProvider;
et pour chaque demande:
EntityManager entityManager = entityManagerProvider.get()
mais alors je reçois:
org.hibernate.PersistentObjectException: detached entity passed to persist
Quelle est la bonne façon de mettre en œuvre Guice + Hibernate EntityManager injection / thread-safe Générique DAO class?
mise à JOUR
commentaire de Andrew Rayner http://www.benmccann.com/hibernate-with-jpa-annotations-and-guice /
"La logique n'est pas vraiment prêt pour la production – au moins si elle est utilisée dans une application web.
Le pool de connexionHibernates est très basique et n'est pas prêt pour la production – il est recommandé d'utiliser un pool de sources de données tel que c3p0.
EntityManager ne devrait pas être réutilisé – il est destiné à être créé par transaction/demande. Il y a de bonnes chances de polluer les demandes ultérieures.
il n'y a pas non plus de réduction de transaction si quelque chose tourne mal.
une approche intéressante – mais il serait beaucoup plus sûr pour les applications web d'utiliser le module Persist extension propre pour gérer le cycle de vie des instances et des transactions EntityMananger."
3 réponses
tout d'abord, quel genre D'EntityManager utilisez-vous? En regardant votre code, je pense que ce genre de logiciel est géré par EntityManager. Il serait important pour vous de comprendre les différents types de EntityManager.
s'il vous Plaît voir: http://docs.oracle.com/javaee/6/tutorial/doc/bnbqw.html
sur cette base, vous devez créer un objet EntityManagerFactory et ensuite créer un objet EntityManager.
Exemple De Base:
private static EntityManagerFactory emf;
EntityManager em = null;
public static EntityManagerFactory getEmf(){
if(emf == null){
emf = Persistence.createEntityManagerFactory("nameOfYourPersistenceUnit");
}
return emf;
}
em = getEmf().createEntityManager();
em.getTransaction().begin();
em.persist(entity);
em.getTransaction().commit();
em.close();
le problème était que mon point final était annoté avec @Singleton donc il a réutilisé le même EntityManager lors d'appels simultanés. Après avoir supprimé @Singleton, pendant les appels simultanés, différents objets EntityManager sont utilisés. Si les appels de fin sont subséquents, il se peut que l'ancien/ancien EntityManager soit utilisé.
exemple très simplifié:
@Path("/v1/items")
public class ItemsService {
@Inject
private EntityManager entityManager;
@POST
@Path("/{id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public void saveItem(){
entityManager.getTransaction().begin();
entityManager.persist(new Item());
entityManager.getTransaction().commit();
}
}
S'il est dit que la transaction est déjà ouverte, cela signifie qu'elle a été ouverte par un autre processus et non fermée ...
je suggère d'utiliser @Transactionl au lieu d'écrire:
em.getTransaction().begin();
et
em.getTransaction().commit();
em.close();
qui gérera les choses pour vous ...
alors pour vous ce sera par là :
@Transactionl
@POST
@Path("/{id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public void saveItem(){
entityManager.persist(new Item());
}
j'Espère que c'est de l'aide