JPA – EntityManager x EntityManagerFactory x Multithreading

Opa!

Neste post vou falar um pouco sobre EntityManager e EntityManagerFactory em relação a ambiente multithread.

EntityManager não é thread-safe

Diferente do EntityManagerFactory, o EntityManager não é thread-safe. É importante relevar isso principalmente em aplicações web, onde cada requisição do cliente é tratada por um thread diferente.

Neste cenário normalmente são utilizados singletons para prover os serviços, onde pode ser um problema usar injeção de dependência (DI) para injetar um EntityManager como membro de instância dos singletons.

Por isso, o mais indicado é injetar o EntityManagerFactory. Exemplo:

public class UserDAO {
    @PersistenceUnit
    private EntityManagerFactory emf;

    public void persist(User user) {
        EntityManager em = null;
        try {
            em = emf.createEntityManager();
            return em.persist(user);
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }
}

Usando a injeção de dependências do Spring

Com o Spring a situação é diferente…

O seguinte post é bem interessante, onde o autor explica o mecanismo de controle de transação do Spring para métodos anotados com @Transactional do Spring:

http://blog.jhades.org/how-does-spring-transactional-really-work/

A parte em que ele fala do EntityManager vem bem a calhar. No caso, ele explica que o Spring não injeta o EntityManager diretamente, invés disso é injetado um proxy. Este proxy, com ajuda de um ThreadLocal, sempre resolve um EntityManager por thread, eliminando o problema do multithreading.

Antes que o leitor possa pensar em ignorar a ideia do proxy e injetar sempre o EntityManagerFactory, quando usando o Spring, o melhor mesmo é injetar direto o EntityManager.

Dentro de um escopo transacional do Spring, a abertura e commit/rollback são controlados pelo container, o que é feito em cima do EntityManager controlado pelo Spring (vinculado ao thread atual).

Se injetamos o EntityManagerFactory e criamos o EntityManager manualmente, o Spring não irá realizar o controle da transação no EntityManager que criamos, mas em algum outro controlado por ele. Neste caso você poderá notar que suas alterações não serão persistidas no banco.

Então o correto, que é até mais simples, é usar direto o EntityManager. Segue exemplo:

public class MyComponent {
    @PersistenceContext
    private EntityManager entityManager;

    @Transactional
    public void doStuff() {
        Animal animal = new Animal("Fish");
        Toy toy = new Toy("Ball");

        entityManager.persist(animal);
        entityManager.persist(toy);
    }
}

Como já citado lá naquele post, o mais legal é que se o método inicial da transação chamar outro componente, e este componente também injetar o EntityManager, novamente estaremos trabalhando com o proxy que irá resolver o mesmo EntityManager, possibilitando continuar na mesma transação.

Outras referências:
Tutorial JEE 6 (EntityManager em ambiente gerenciado):
http://docs.oracle.com/javaee/6/tutorial/doc/bnbqw.html#indexterm-1674

Esta entrada foi publicada em Java, JPA, Spring. Adicione o link permanente aos seus favoritos.