Antes de um objeto ser persistente precisamos fazer uma chamada específica à API de persistência para tornar os objetos persistidos na base de dados. Diferentes tipos de chamadas à API são necessárias para realizar diferentes operações sobre as entidades. Esta API é implementada pelo Entity Manager e quase tudo está encapsulado dentro de uma simples interface EntityManager.

Quando um Entity Manager obtém uma referência de uma entidade, seja através de um argumento passado numa chamada de método ou porque essa entidade foi lida de uma base de dados, este objeto é dito como gerenciado (managed) por um Entity Manager. O conjunto de instâncias de entidades gerenciadas dentro de um Entity Manager em um dado momento é chamado como contexto de persistência (persistence context). Apenas uma instância Java com a mesma identidade de persistência pode existir em um contexto de persistência em qualquer momento. Por exemplo, se uma classe Empregado com uma identidade de persistência (ou id) número 158 existe no contexto de persistência, tem-se que nenhuma outra instância da classe Empregado com o id 158 pode existir dentro deste mesmo contexto de persistência.

Os Entity Managers são configurados para serem capazes de persistir ou gerenciar tipos específicos de objetos, ler e escreve-los numa base de dados e ser implementado por um Provedor de Persistência (persistence provider) como Hibernate, TopLink, JDO, entre outros. Estes provedores são responsáveis por implementar a especificação da Java Persistence API, desde o Entity Manager até geração de SQL e muitas outras funcionalidades.

Todos Entity Managers vem de fábricas (factories) do tipo EntityManagerFactory. A configuração de um Entity Manager é modelado por um EntityManagerFactory que criou-o, mas ele é definido separadamente como uma unidade de persistência (persistence unit). A unidade de persistência ordena implicitamente ou explicitamente as configurações e classes de entidades usada por todos Entity Managers obtidos por uma única instância de um EntityManagerFactory ligada a uma unidade de persistência. Portanto, existe uma correspondência um-para-um (one-to-one) entre uma unidade de persistência e a sua EntityManagerFactory concreta.

Unidade de Persistência são nomeadas para permitir uma diferenciação de uma EntityManagerFactory para outra. Isto dá controle para aplicação sobre quais configurações ou unidade de persistência é para ser usada por uma operação em uma entidade particular.

Relacionamento entre os conceitos da JPA

Figura 1: Relacionamento entre os conceitos da JPA

A figura acima ilustra claramente que para cada unidade de persistência (Persistence Unit) existe um EntityManagerFactory e que diversos Entity Managers podem ser criados por um único EntityManagerFactory. Além disso, muitos Entity Managers podem apontar para o mesmo contexto de persistência (Persistence Context).

Obtendo um Entity Manager

Um Entity Manager é sempre obtido através de um EntityManagerFactory que determina os parâmetros de configuração que vão dizer como será o funcionamento do Entity Manager. O método estático createEntityManagerFactory() na classe Persistence retorna o EntityManagerFactory para um nome de unidade de persistência específico. Como um exemplo, abaixo temos a criação de um EntityManagerFactory para uma unidade de persistência nomeada como "minhaapppersistenceunit":

Listagem 1: Criando uma EntityManagerFactory

EntityManagerFactory factory = Persistence.createEntityManagerFactory("minhaapppersistenceunit");

O nome da unidade de persistência especificada como "minhaapppersistenceunit" que foi passada no método createEntityManagerFactory() identifica a unidade de persistência que tem o objetivo de determinar configurações como, por exemplo, os parâmetros de conexão que os Entity Managers gerados nesta fábrica usarão quando se conectar com a base de dados.

Agora que possuímos uma fábrica, podemos facilmente obter um Entity Manager. O exemplo abaixo demonstra a criação de um Entity Manager a partir da fábrica que foi criada anteriormente:

Listagem 2: Criando uma EntityManager através da fábrica

EntityManager entityManager = factory.createEntityManager();

Agora que temos a Entity Manager podemos iniciar o trabalho com entidades persistentes.

Persistindo uma Entidade

Persistir uma entidade é uma operação em que temos uma entidade transiente, ou uma entidade que não possui uma representação persistente na base de dados, e assim armazenamos o seu estado para que ela possa ser recuperada posteriormente.

O exemplo abaixo demonstra a persistência de uma instância de Artigo:

Listagem 3: Persistindo uma entidade na base de dados

Artigo artigo = new Artigo(158);

entityManager.persist(artigo);

No exemplo acima simplesmente criamos uma instância de Artigo na qual queremos persistir. Lembrando que a classe Artigo é uma entidade, ou seja, precisamos definir todas as propriedades que uma entidade deve possuir, para mais informações leia o artigo "Definindo Entidades na Java Persistence API" disponível aqui no portal. Claro que esta entidade não tem nada, apenas um id, faltaria setar o nome do artigo, o texto, assunto, data, entre outras características. O exemplo demonstra a forma mais simples de persistência.

No próximo exemplo usamos novamente um Entity Manager para persistir uma entidade chamando o método persist() que é requerido para iniciar a persistência deste objeto na base de dados. Se algum problema ocorrer durante o processo de persistência do objeto receberemos uma exceção PersistenceException.

O exemplo abaixo mostra um método que cria um novo artigo completo e persiste ele na base de dados:

Listagem 4: Persistindo um objeto artigo completo na base de dados

public void insereArtigo(int id, String titulo, String assunto, String texto) {

	Artigo artigo = new Artigo(id);

	artigo.setTitulo("Teste de Titulo");

	artigo.setAssunto("Teste de Assunto");

	artigo.setTexto("Teste de Texto");

	entityManager.persist(artigo);

}

Se uma exceção ocorrer ela será propagada para quem está chamando o método.

Encontrando uma Entidade

Após inserir uma entidade no Banco de Dados devemos encontrá-la. A Entity Manager também nos ajuda nessa operação. Abaixo segue um exemplo de como podemos encontrar uma entidade:

Listagem 5: Encontrando uma entidade com o id 158

Artigo artigo = entityManager.find(Artigo.class, 158);

No exemplo acima podemos verificar que precisamos passar uma classe que está sendo procurada e o id ou a chave primária que identifica esta entidade particular na base de dados. Esta é toda informação necessária pelo Entity Manager para encontrar a instância na base de dados, e quando a chamada completar, o artigo retornado será uma entidade gerenciada, o que indica que essa entidade existirá no contexto de persistência atual que está associado ao Entity Manager.

Caso a id passada não exista ou o objeto já foi deletado o método find() retornará null. Portanto, sempre devemos checar se o retorno veio nulo antes que a variável seja utilizada.

Removendo uma Entidade

A remoção de entidades da base de dados não é algo tão comum como imaginamos. Muitas aplicações nunca deletam objetos, ou apenas setam uma flag quando o objeto não é mais válido e não deve ser exibido aos clientes. No entanto, às vezes faz-se necessário efetuar a deleção lógica do dado na base de dados.

Quando queremos remover uma entidade ela deve ser gerenciada, ou seja, ela deve estar presente no contexto de persistência. Abaixo segue um exemplo de remoção de um artigo:

Listagem 6: Deletando uma entidade da base de dados

Artigo artigo = entityManager.find(Artigo.class, 158);

entityManager.remove(artigo);

Neste exemplo nós encontramos um artigo com o id 158 que retorna uma instância gerenciada de um Artigo, e então removemos a entidade usando uma chamada remove() no Entity Manager. Se o método find não encontrar nada e passarmos um objeto nulo para remove() o método remove() retornará uma exceção java.lang.IllegalArgumentException. Por isso sempre inclua uma checagem antes de chamar o método remove, isso evitará exceções. Segue abaixo um exemplo:

Listagem 7: Checando os parâmetros para exclusão de um artigo

public void exclui(int id) {

	Artigo artigo = entityManager.find(Artigo.class, id);

	if (artigo != null)

		entityManager.remove(entity);

}

Atualizando uma Entidade

Existem diferentes formas de atualizar uma entidade, mas iremos demonstrar a forma mais simples e comum de fazer essa operação. Na atualização já temos uma entidade e queremos modificar essa entidade. Quando nós ainda não temos a entidade devemos primeiramente encontra-la e depois efetuar as modificações necessárias. Segue abaixo um primeiro exemplo:

Listagem 8: Atualizando uma entidade Artigo

Artigo artigo = entityManager.find(Artigo.class, 158);

artigo.setAssunto("Outro assunto");

Observamos que não foi chamada nenhuma operação em especial do Entity Manager, apenas alteramos diretamente o objeto. Por isso é importante que a entidade seja uma instância gerenciada. Por outro lado, o provedor de persistência não conseguirá detectar alterações e nenhuma alteração será realizada na representação persistente do artigo.

No exemplo abaixo fizemos uma alteração no assunto do artigo:

Listagem 9: Atualizando o assunto de um artigo

public Artigo alteraAssunto(int id, String novoAssunto) {

	Artigo artigo = entityManager.find(Artigo.class, id);


	if (artigo != null)

		artigo.setAssunto(novoAssunto);


	return artigo;

}

Se não for encontrado nenhum artigo na base de dados então nenhuma alteração será realizada e um objeto nulo será retornado para o chamador do método. Caso contrário, o objeto alterado será retornado.

Encerrando EntityManager e EntityManagerFactory

Por fim, no final do programa devemos fechar o EntityManager e o EntityManagerFactory usando o método close(). Isto garante que todos os recursos que eles poderiam ter alocado são propriamente liberados.

Segue um exemplo de como utiliza-los:

Listagem 10: Utilizando o EntityManager e o EntityManagerFactory

entityManager.close();

entitiManagerFactory.close();

Vale ressaltar que em alguns servidores de aplicação isso não é necessário, pois eles tratam de fazer o encerramento correto deles. Por isso, vale a pena sempre dar uma conferida na especificação do servidor de aplicação.

Transações

As mudanças nas entidades devem ser feitas persistentes utilizando uma transação. Nas operações de inserção, atualização e deleção devemos utilizar transações, exceto no método find() que apenas busca dados. Quando transações não são utilizadas temos como retorno uma exceção ou então nada é feito na base de dados. Quando utilizamos Java SE utilizamos o serviço EntityTransaction. Utilizando o ambiente Java SE nós devemos iniciar e comitar a transação nos métodos operacionais, ou então precisamos iniciar e comitar uma transação antes e depois de chamar um método operacional. Em ambos os casos devemos chamar getTransaction() no Entity Manager para receber um EntityTransaction e então invocamos begin(). Para comitar a transação usamos commit() no EntityTransaction. O exemplo abaixo ilustra essa situação quando criamos um artigo:

Listagem 11: Criando transações para efetuar operações com EntityManager

//inicia a transação

entityManager.getTransaction().begin();

Artigo artigo = new Artigo(158);

entityManager.persist(artigo);

//comita a transação

entityManager.getTransaction().commit();

Consultas

Quando utilizamos um banco de dados efetuamos consultas utilizando a linguagem SQL. A diferença para JPA é que ao invés de utilizarmos SQL fazemos as consultas usando JPQL (Java Persistence Query Language).

Uma consulta é implementada no código como um objeto Query ou TypedQuery. Elas são construídas utilizando EntityManager como uma fábrica. A EntityManager inclui uma variedade de métodos que retornam uma nova Query ou TypedQuery.

As consultas podem ser definidas tanto estaticamente quanto dinamicamente. Consultas estáticas são definidas em anotações ou no XML, e devem incluir critérios de pesquisa assim como um nome designado pelo usuário. Este tipo de consulta também é chamado de named query e pode ser localizada posteriormente pelo seu nome.

Uma consulta dinâmica pode ser emitida em tempo de execução. Elas são mais caras do que as consultas estáticas, pois o provedor de persistência não possui nenhuma preparação para realizar a consulta, porém elas são mais simples de usar e podem ser emitidas em resposta a lógica do programa ou até mesmo à lógica do usuário.

Como um exemplo de criação de consultas dinâmicas vamos obter todos os artigos da base de dados.

Listagem 12: Criando uma consulta na base de dados

TypedQuery<Artigo> consulta = entityManager.createQuery("SELECT art FROM Artigo art", Artigo.class);

List<Artigo> artigos = consulta.getResultList();

Neste exemplo criamos um TypedQuery chamando createQuery() no EntityManager e passando a String JPQL que especifica o nosso critério de consulta (ou query criteria). Também verificamos que a JPQL se referencia a entidade e não a tabelas da base de dados, por isso a nossa consulta seleciona todos objetos Artigo.

Retornar os resultados basta invocar getResultList() que retorna uma lista contendo os objetos Artigo que combinam com o critério de pesquisa.

Unidade de Persistência (Persistence Unit)

A configuração que descreve a unidade de persistência é definida em um arquivo XML chamado persistence.xml. Cada unidade de persistência é nomeado, então quando uma aplicação quer uma configuração especifica para uma entidade é necessário apenas referenciar o nome da unidade de persistência que define as suas configurações. Um simples persistence.xml pode conter uma ou mais configurações de unidade de persistência nomeada, mas cada unidade de persistência é separada e distinta uma das outras, e elas podem ser logicamente pensadas como estando em arquivos persistence.xml separados.

Muitos dos elementos da unidade de persistência no arquivo persistence.xml se aplicam a unidades de persistência que são publicados (deployed) dentro de um container Java EE.

Para os exemplos acima seria necessário apenas definir o arquivo abaixo:

Listagem 13: Arquivo persistence.xml

<?xml version="1.0" encoding="UTF-8" ?>

<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
 http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"

  version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">

  <persistence-unit name="nomepersistenceunit" transaction-type="RESOURCE_LOCAL">

	<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

	

	<class>com.model.entidades.Artigo</class>

	

    <properties>

      <property name="javax.persistence.jdbc.driver" 
      	value="org.postgresql.Driver" />

      <property name="javax.persistence.jdbc.url"
        value="jdbc:postgresql://localhost:5432/nomedobanco" />

      <property name="javax.persistence.jdbc.user" 
      	value="nomeUsuario" />

      <property name="javax.persistence.jdbc.password" 
      	value="senha" />

    </properties>

  </persistence-unit>

</persistence>

O atributo name do elemento persistence-unit indica o nome da nossa unidade de persistência e essa também será a string necessária quando criarmos a nossa EntityManagerFactory no código. O atributo transaction-type indica que nossa unidade de persistência usa o nível de recurso EntityTransaction ao invés de transações JTA. O elemento class lista as entidades que são parte da unidade de persistência. Múltiplos elementos class podem ser especificados quando existe mais que uma entidade. Normalmente eles não serão necessários quando estamos publicando (deploying) num container Java EE porque o container já pesquisará por todas as entidades automaticamente como uma parte do processo de publicação (deploying), mas eles são necessários para que uma execução seja portável a diferentes ambientes, como por exemplo, quando rodarmos num ambiente Java SE. No exemplo temos apenas uma entidade Artigo.

A última seção apenas lista as propriedades do banco de dados para dizer ao provedor qual recurso devemos se conectar. Outras propriedades como log podem ser também especificados.

O arquivo persistence.xml é armazenado no diretório META-INF.

Conclusão

Vimos neste artigo o que é Entity Manager e como usá-lo na JPA. Diversas operações são utilizadas como inserção de dados, atualização, exclusão, consulta, gerenciamento de transações, consultas dinâmicas e estáticas entre outras operações disponíveis.

Bibliografia

Leia também

Veja também