Neste artigo iremos explicar o uso adequado do Persistent Context (Contexto Persistente), com suas características e peculiariedades. Mas antes de tudo é preciso entender afinal o que vem a ser um Persistent Context.
Um Persistent Context é uma unidade que armazena várias instancias de entidades, quando estas entidades estão dentro de um Persistent Context, são chamadas de managed. Quando um Persistent Context é finalizado, as entidades que antes eram gerenciadas por ele, tornam-se detached. Uma entidade detached possui algumas consequências por não estar mais atrelado a nenhum EntityManager:
- Entidades detached não podem carregar estados persistentes adicionais, em outras palavras, você não poderá realizar mas operações com esta entidade, apenas visualizar o que ela já possui.
- Quando executado um novo find com o EntityManager, ele não retornará as detached entitys, ele criará novas entidades, em outras palavras, novos finds retornarão novas entidades, diferente das detached, pois estas serão managed.
Funcionamento em Transações
A cada nova transação (begin) que ocorre, o EntityManager cria um novo Persistent Context e durante todo o ciclo de vida desta transação, as entidades são gerenciadas por este EntityManager neste Persistent Context que fora criado.
Quando uma transação é finalizada, seja por um commit ou rollback, o Persistent Context é destruido e todas as entidades antes vinculadas a este EntityManager tornam-se detached.
Caso você opte por usar o EntityManager fora de transações, ainda assim um Persistent Context temporário é criado e todas as entidades retornadas nessa transação são gerenciadas por este EntityManager específico. Quando o processo é terminado (um find, por exemplo) o Persistent Context é finalizado e as entidades são postas em modo detached. Perceba que o funcionamento com ou sem transação acaba sendo o mesmo, a única diferença é que com transações você pode executar diversos comandos usando um mesmo Persistent Context.
Vamos a alguns exemplos práticos para entender na prática o funcionamento do Persistent Context juntamente com o EntityManager.
Listagem 1: Uso do Persistent Context fora de transações
// cada operação ocorre em um separado persistence context e retorna
// uma instancia deatched
Magazine mag1 = em.find (Magazine.class, magId);
Magazine mag2 = em.find (Magazine.class, magId);
assertTrue (mag2 != mag1);
...
Perceba que os objetos são diferentes, pois fazem parte de um Persistent Context diferente. Ao executar o mag1 é criado e destruído um Persistent Context, e o mesmo ocorre com o mag2. Ao final criamos 2 Persistent Context's distintos.
Vamos ver agora como ficaria usando os métodos dentro de uma transação.
Listagem 2: Uso do Persistent Context em transações
//Fora da Transacao
Magazine mag1 = em.find (Magazine.class, magId);
Magazine mag2 = em.find (Magazine.class, magId);
assertTrue (mag2 != mag1);
// Inicia transação:
Magazine mag3 = em.find (Magazine.class, magId);
assertTrue (mag3 != mag1 && mag3 != mag2);
Magazine mag4 = em.find (Magazine.class (magId);
assertTrue (mag4 == mag3);
...
// Finaliza Transação (commit ou rollback):
O que podemos ver acima é que mag4 é igual a mag3 pois estão na mesma transação, consequentemente no mesmo Persistent Context. Por outro lado, ao comparar o mg3 com o mag1 e mag2, que estão fora da transação, o resultado é False já que estes fazem parte de outro contexto.
Para reflexão e aprendizado pense no que acontecerá quando executarmos o código da listagem 3.
Listagem 3: Para reflexão
// Inicia transação:
Magazine mag3 = em.find (Magazine.class, magId);
// Finaliza Transação (commit ou rollback):
Magazine mag5 = em.find (Magazine.class, magId);
assertTrue (mag5 != mag3);
Dados os devidos conceitos, vamos a um problema que ocorre com muita frequência e a falta de conhecimento sobre Persistent Context pode resultar em dias de pesquisa e tentativas inúteis.
Imagine a seguinte situação: Você possui um método find que busca uma lista de Funcionários no banco, como na listagem abaixo:
Listagem 4: Buscando Funcionarios
entityManager.find(“SELECT f FROM Funcionario f”);
O que nós temos acima é o retorno de uma lista de funcionários, e se retornarem 20 entidades funcionários, as 20 estarão em estado managed. Mas perceba que o método acima não está dentro de nenhuma transação, ou seja, o Persistent Context é criado, os beans são retornados e este é destruído. Automaticamente de managed eles tornam-se detached. No caso acima seus beans sempre serão detached, pois ao final do final sua Persistent Context é eliminada.
E qual o problema disso ? Imagine agora que você está trabalhando com esse bean detached (e nem sabe disso), então decide removê-lo do banco, como na listagem abaixo.
Listagem 5: Tentando remover bean detached
entityManager.remove(funcionario001);
Você receberá uma exception, para ser mais específico, esta exception: IllegalArgumentException. Provavelmente a mensagem será algo como: Problema ao remover detached …
Isso ocorre pois, como dissemos anteriormente, você não pode remover uma entidade detached com outro EntityManager.
A solução para o problema citado acima é usar um Persistent Context Extended. Nesta solução um EntityManager permanece por todo ciclo de vida, independente se abrir ou fechar uma nova transação, o mesmo Persistent Context será utilizado. Este só será fechado quando explicitamente através do EntityManager você fizer um close, ou seja, encerrar aquele Persistent Context. Juntando todas as listagens anteriores em apenas 1 vamos demonstrar o uso do Persistent Context Extended.
Listagem 6: Usando Persistent Context Extended
EntityManagerFactory emf = ...
EntityManager em =
emf.createEntityManager (PersistenceContextType.EXTENDED);
Magazine mag1 = em.find (Magazine.class, magId);
Magazine mag2 = em.find (Magazine.class, magId);
assertTrue (mag2 == mag1);
em.getTransaction ().begin ();
Magazine mag3 = em.find (Magazine.class, magId);
assertTrue (mag3 == mag1);
Magazine mag4 = em.find (Magazine.class (magId);
assertTrue (mag4 == mag1);
em.getTransaction.commit ();
Magazine mag5 = em.find (Magazine.class, magId);
assertTrue (mag5 == mag1);
em.close ();
CONCLUSÃO
É entendimento correto do Persistent Context torna a aplicação mais robustas e sem inconsistência. Muitos problemas decorrentes deste assunto podem ser resolvidos com facilidade se o entendimento do mesmo for satisfatório.