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 connexion

Hibernates 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."

0
demandé sur Justas 2016-06-16 14:11:09

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();
0
répondu J.Arranz 2016-06-22 08:20:43

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();
    }
}
0
répondu Justas 2016-08-03 13:59:44

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

0
répondu taboubim 2016-09-08 23:32:16