Neste artigo iremos iniciar um assunto muito discutido quando trabalha-se com JPA, o PersistenceContext. Iremos mostrar como funcionam os seus dois tipos que são: Extended e Transient e dar exemplos de como utilizá-los de maneira correta.

Quando trabalha-se com JPA há diversos conceitos que no começo podem ser confusos e complexos de entender, mas que são extremamente necessário para um projeto robusto e bem “arquitetado”. Sendo assim, neste artigo trataremos de um assunto que é foco de grande discussão e dúvidas, pois muitos não sabem como utilizá-lo ou nem mesmo sabem para que serve.

Quer aprender ainda mais sobre Java? Confira os Cursos de Java Online da DevMedia.

PersistenceContext

Um PersistenceContext (Contexto Persistente) é um local onde ficam armazenados os objetos (entidades) que estão sendo manipulados pelo EntityManager corrente. Difícil de entender? Vamos explicar melhor.

Antes de tudo é necessário saber o que é um EntityManager (pelo menos o básico) e como não é foco deste artigo explicar em detalhes esse recurso, é suficiente você saber que EntityManager é o recurso responsável por realizar as operações de sincronismo com o banco de dados (inserir, remover, atualizar ou consultar - CRUD) e gerenciar o ciclo de vida das entidades. Sendo assim, quando você deseja realizar uma inserção no banco de dados, o objeto do EntityManager irá fazer essa tarefa para você.

O PersistenceContext funciona como Container que guarda as entidades que estão sendo gerenciadas pelo EntityManager, ele funciona como o CACHE L1 do EntityManager. Sendo assim, ele poupa o trabalho do EntityManager precisar buscar toda vez o mesmo objeto no banco de dados, sobrecarregando o mesmo.

Então vamos supor uma situação para ver como o PersistenceContext e o EntityManager trabalham, sem considerar o uso de um CACHE L2, o que tornaria o artigo um pouco mais complexo e este não é o nosso objetivo.

Cenário Hipotético Didático: Imagine o PersistenceContext (PC) como um container capaz de armazenar todas as entidades que serão manipuladas pelo EntityManager.

No início da nossa aplicação não temos nenhum PersistenceContext e nenhum EntityManager, pois não realizamos nenhuma operação com o banco de dados. Então você decide buscar o usuário “Mario” no banco de dados e neste momento são criados 1 EntityManager + 1 PersistenceContext (ligados diretamente um ao outro).

O EntityManager vai até o PersistenceContext e checa se o usuário “Mario” já está lá, e como não está, ele irá ao banco de dados realizar a consulta desejada retornando o objeto Mario preenchido e populado.

Depois de retornar o usuário “Mario” do banco de dados, o EntityManager grava esse objeto no PersistenceContext (Nosso Cache L1), pois da próxima vez que precisar deste objeto, ele não precisará ir no banco de dados.

Então como você pode perceber no cenário acima, o EntityManager sempre consulta o PersistenceContext, checando se o objeto já está lá e, caso não esteja, ai sim ele irá até o banco de dados.

Quando o EntityManager é destruído (no fim de um requisição) o PersistenceContext também será, tornando todos os objetos por ele gerenciado, “Detached”, impossibilitando de usá-los em outros EntityManager's com outros PersistenceContext's, ou seja, você não poderá realizar uma operação “EntityManager.persist(obj)” em um objeto que está “Detached”. Obviamente que existem meios de fazer isso, mas não explicaremos como, pois foge do nosso escopo que é explicar o PersistenceContext.

Nada melhor do que uma imagem para ilustrar todo o conteúdo descrito. Observe a Figura 1.

PersistenceContext
Figura 1. PersistenceContext

Temos acima o PC (PersistenceContext) fazendo CACHE de duas Entidades que estão sendo gerenciadas pelo EntityManager atual. Lembre-se que ao destruir o EntityManager o PersistenceContext também será destruído - explicaremos mais a frente porque.

PersistenceContext Trasient (PCT)

Vamos começar a aprofundar-nos mais em conceitos técnicos do funcionamento de tal recurso. Iremos começar estudando o funcionamento do PC Trasient.

O conceito de um Contexto Persistente Transiente (PCT) é que as entidades que estão salvas no PC estão disponíveis apenas enquanto a transação estiver sendo executada, pois quando um commit ou rollback é realizado, essa referência é perdida, tornando os objetos “detached”. Então você pode realizar diversos “finds” dentro de uma transação e utilizar o mesmo PC, porém se você estiver trabalhando com um EntityManager fora de uma transação, a cada requisição ele irá criar um novo PC, destruí-lo e retornar uma entidade “detached” para você. Vamos ver um exemplo na Listagem 1.


EntityManager em; // injetado
...

// fora de uma transacao:

// cada operacao abaixo irá ser executada em 1 PersistenceContext distinto
//e ao final dessa operação é retornado um objeto Detached.
//Na linha abaixo temos a criação de 2 PersistenceContext.
Magazine mag1 = em.find(Magazine.class, magId);
Magazine mag2 = em.find(Magazine.class, magId);
assertTrue(mag2 != mag1);
...

// dentro de uma transação

//inicio da transação - begin

//Com uma transação corrente não são retornados objetos detached,
//pois o PersistenceContext ainda está 'vivo'. Nesse caso o mag3 será igual ao mag4
//pois são exatamente o mesmo objeto retornado do PersistenceContext.     
Magazine mag3 = em.find(Magazine.class, magId);
assertTrue(mag3 != mag1 && mag3 != mag2);
Magazine mag4 = em.find(Magazine.class (magId);
assertTrue(mag4 == mag3);
...

// fim da transação - commit

// O mag5 será diferente do mag3 pois o mag5 foi criado em 
um outro PersistenceContext, fora de uma transação
Magazine mag5 = em.find(Magazine.class, magId);
assertTrue(mag5 != mag3);
Listagem 1. Comportamento do PersistenceContext Transient

Analisando um pouco mais o código acima conseguimos perceber o seguinte:

  1. Na execução das primeiras linhas fora de uma transação, os objetos mag1 e mag2 geram cada um uma consulta direta ao banco de dados.
  2. Na execução do mag3 e mag4 que já estão dentro de uma Transação, apenas uma consulta ao banco é gerada, pois o mag3 é carregado e salvo no PersistenceContext atual, evitando que uma próxima busca (mag4) gere novamente uma consulta ao banco de dados.

PersistenceContext Extended (PCE)

Indo para um lado totalmente diferente do Transient, temos aqui o PersistenceContext Extended. Este, como o próprio nome já sugere, se estende além das transações. Isso significa que mesmo que seja feito um commit ou rollback em uma transação, as entidades continuam salvas (gerenciadas) no PersistenceContext. Veja um exemplo no código da Listagem 2.


EntityManagerFactory emf = ...
EntityManager em = emf.createEntityManager();

// Aqui o persistenceContext já está ativo durante 
todo ciclo de vida do EntityManager
Magazine mag1 = em.find(Magazine.class, magId);
Magazine mag2 = em.find(Magazine.class, magId);
assertTrue(mag2 == mag1);

em.getTransaction().begin();

// mesmo PersistenceContext mesmo depois de uma nova transacao
Magazine mag3 = em.find(Magazine.class, magId);
assertTrue(mag3 == mag1);
Magazine mag4 = em.find(Magazine.class (magId);
assertTrue(mag4 == mag1);

em.getTransaction.commit ();

// mesmo PersistenceContext mesmo depois de um commit
Magazine mag5 = em.find(Magazine.class, magId);
assertTrue(mag5 == mag1);

//Quando o entitymanager é fechado então sim o 
PersistenceContext é destruído tornando as entidades detached
em.close();
Listagem 2. Comportamento do PersistenceContext Extended

Configurando o PersistenceContext

Para dizer se sua aplicação está utilizando um PersistenceContext Transient ou Extended você deve usar a anotação da listagem 3 com sua respectiva configuração:


@PersistenceContext(name="PersistentUnitName")
private EntityManager entityManager;
Listagem 3. Anotando para uso do PersistenceContext Transient

Por padrão o @PersistenceContext já é Transient então nada mais é necessário. Ele irá continuar 'vivo' durante toda a transação. Observe a Listagem 4.


@PersistenceContext(name="PersistentUnitName", type=PersistenceContextType.EXTENDED)
private EntityManager entityManager;
Listagem 4. Anotando para uso do PersistenceContext Extended

Tome muito cuidado ao utilizar o PersistenceContext Extended, pois ele pode trazer grandes problemas caso utilizado de forma errada.

É importante salientar que este artigo é apenas parte de um assunto macro que engloba outros assuntos como CACHE L1, CACHE L2, EntityManager e assim por diante. Mas com o entendimento deste você sentirá mais “conforto” em trabalhar com outros assuntos.