Utilizando o EntityManager
Autor: ghzatomic | Publicado: 2015-05-28T10:17:00-07:00

A API Entity Manager tem métodos para inserir e remover entidades de um banco de dados e também mesclar atualizações das instâncias de entidade desacopladas. Há também uma rica API de consulta que pode ser acessada para criar objetos de consulta a partir de certos métodos EntityManager.

*Persistindo entidades
Persistir uma entidade é o ato de inseri-la dentro de um banco de dados. Podemos persistir entidades que ainda não foram criadas no banco de dados. Para criar uma entidade, você primeiro aloca uma instância dela, configura suas propriedades e ativa quaisquer relacionamentos que ela possa ter com outros objetos. Em outras palavras, você inicializa um bean de entidade assim como faria com qualquer outro objeto Java. Depois de fazer isso, você interage com o serviço do gerenciador de entidades chamando o método EntityManager.persist().


Quando esse método é chamado, o gerenciador da entidade enfileira o Address para inserção no banco de dados e a instâncias do objeto torna-se gerenciada. O momento em que a inserção real acontece depende de algumas variáveis. Se persist() for chamado dentro de uma transação, a inserção poderia acontecer imediatamente ou ser enfileirada até o fim da transação, dependendo do modo flush (de gravação). Você sempre pode forçar a inserção manualmente dentro de uma transação chamando o método flush(). Você pode chamar persist() fora de uma transação se e somente se o gerenciador de entidades for um contexto de persistência EXTENDED. Ao chamar persist() fora de uma transação com um contexto de persistência EXTENDED, a inserção é enfileirada até o contexto de persistência ser associado com uma transação. Um contexto de persistência estendido injetado é automaticamente associado a uma transação JTA pelo contêiner EJB. Para outros contextos estendidos criados manualmente com a API EntityManagerFactor, você precisa chamar Entity.Manager.joinTransaction() para realizar a associação da transação.
O método persist() lança uma IllegalArgumentException se seu parâmetro não for um tipo de entidade. TransactionRequiredException é lançada se esse método for invocado em um contexto de persistência com escopo de transação. Entretanto, se o gerenciador de entidades for um contexto de persistência estendido, é valido chamar persist() fora do escopo de uma transação; a inserção é enfileirada até o contexto de persistência interagir com uma transação.

*Localização entidades
O gerenciador de entidades fornece dois mecanismos para localizar objetos no seu banco de dados. Uma maneira é usar os métodos simples do gerenciador de entidades que localiza uma entidade de acordo com sua chave primaria. A outra é criando e executando consultas.
O EntityManager tem dois métodos diferentes que permitem localizar uma entidade de acordo com sua chave primaria.
Os dois métodos aceitam a classe da entidade como um parâmetro e também como uma instância da chave primária da entidade. Eles usam Java genérico de modo que você não precise aplicar nenhuma coerção. Como esses métodos diferem? O método find() retorna null se a entidade não for encontrada no banco de dados. Ele também inicializa o estado com base nas diretivas de carregamento sob demanda (lazy-loading) de cada propriedade.
Neste exemplo, estamos localizando um Address com um ID de chave primária 2. Como isso compila se o método find() espera um Object com seu segundo tipo de parâmetro? Bom, o Java 5 tem um recurso chamando autoboxing que converte tipos primitivos diretamente em seus tipos Object. Assim, a constante 2 é convertida em uma java.lang.Integer.
O getReference() difere de find() pelo fato de que, se a entidade não for encontrada no banco de dados, esse método lança uma javax.persistence.EntityNotFoundException e não a garantia de que o estado da entidade será inicializado.
Tanto o find() como o getReference() lançam uma IllegalArgumentException se seus parâmetros não forem um tipo de entidade. Eles podem ser invocados fora do escopo de uma transação. Nesse caso, qualquer objeto retornado é desacoplado se o EntityManager for uma transação com escopo, mas permanece gerenciado se estiver em um contexto de persistência estendido.
Objetos persistentes também podem ser localizados utilizando-se EJB QL. Diferentemente do EJB 2.1, não há métodos finder, e você precisa criar um objeto Query chamando os métodos createQuery(), createNamedQuery() ou createNativeQuery() do EntityManager.
Criar e executar uma consulta EJB QL é muito parecido com criar e executar uma PreparedStatement JDBC:
Todas as instâncias de objeto retornadas por find(), getResource(), ou por uma consulta, permanecerão gerenciadas enquanto o contexto de persistência estiver ativada. Isso significa que outras chamadas para find() (ou qualquer outra) retornarão a mesma instância do objeto de entidade.

*Atualizando entidades
Depois de localizar um bean de entidade chamando find(), chamando getReference() ou criando e executando uma consulta, a instância do bean de entidade permanece gerenciada pelo contexto de persistência até o contexto ser fechado. Durante esse período, podemos alterar o estado da instância do bean de entidade como faria com qualquer outro objeto, e as atualizações serão sincronizadas automaticamente (dependendo do modo flush) ou se você chamar o método flush() diretamente:

*Mesclando entidades

A especificação Java Persistence permite mesclar alterações de estado feitas em uma entidade desacoplada de volta para o armazenamento de persistência utilizando o método merge() do gerenciador de entidades.
O método merge() chamará uma IllegalArgumentException se seu parâmetro não for um tipo de entidade. A TransactionRequiredException é chamada se esse método for invocado em um contexto de persistência com escopo de transação. Mas se o gerenciador de entidades for um contexto de persistência estendido, será valido invocar esse método fora do escopo de uma transação e a atualização será enfileirada até o contexto de persistência interagir com uma transação.

*Removendo entidades
Uma entidade pode ser removida do banco de dados chamando o método EntityManager.remove().
Depois de remove() for chamado, a instância address não será mais gerenciada e se tornará desacoplada. Além disso, se a entidade tiver algum relacionamento com outros objetos de entidade, estes também poderão ser removidos, dependendo das regras em cascata. A operação remove() só pode ser desfeita recriando a instância de entidade com o método persist().
O método remove() chama uma IllegalArgumentException se seu parâmetro não for um tipo de entidade. A TransactionRequiredException é chamada se esse método for invocado em um contexto de persistência com escopo de transação. Entretanto, se o EntityManager for um contexto de persistência estendido, será válido invocar esse método fora do escopo de uma transação, e o remove será enfileirado até o contexto de persistência interagir com uma transação.

*refresh()
Se a preocupação for pelo fato de que uma entidade gerenciada não foi atualizada em relação ao banco de dados, poderá utilizar o método EntityManager.refresh(). Este método atualiza o estado da entidade, sobrescrevendo quaisquer alterações feitas na entidade.
Se o bean de entidade tiver entidades relacionadas, essas entidades também podem ser atualizadas, dependendo da diretiva da cascata configurada nos metadados da entidade de mapeamento.
O método refresh() chama uma IllegalArgumentException se seu parâmetro não for gerenciado pela instância atual do gerenciador de entidades. A TransactionRequiredException é chamada se esse método for invocado em um contexto de persistência com escopo de transação. Entretanto, se o gerenciador de entidades for um contexto de persistência estendido, será válido invocar esse método fora do escopo de uma transação. Se o objeto não mais estiver no banco de dados porque outra thread ou outro processo o removeram, esse método então lançará uma EntityNotFoundException.

*contains() e clear()
O método contains() recebe uma instância de entidade como um parâmetro. Se essa instância de objeto particular for gerenciada atualmente pelo contexto de persistência, ela retornará true. Ela lança uma IllegalArgumentException se o parâmetro não for uma entidade.
Se precisar desacoplar todas as instâncias de entidade gerenciadas de um contexto de persistência, podemos invocar o método clear() do EntityManager. Ao chamar clear(), todas as alterações feitas nas entidades gerenciadas serão perdidas. É prudente chamar flush() antes de clear() ser invocado, para que não seja perdida nenhuma alteração.
*flush() e FlushModeType
Ao chamar persist(), merge() ou remove(), essas alterações não são sincronizados com o banco de dados até o gerenciador de entidades decidir gravar. Você pode forçar a sincronização a qualquer momento chamando flush(). Por padrão, a atualização acontece automaticamente antes de uma consulta correlacionada ser executada (implementações ineficientes podem até mesmo atualizar antes de qualquer consulta) e em tempo de confirmação de uma transação. A exceção a essa regra padrão é find(). Uma gravação não precisa acontecer quando find() ou getReference() é chamado, porque localizar por meio de uma chave primária é algo que não seria afetado por nenhuma atualização.
Podemos controlar e alterar esse comportamento padrão usando a enumeração javax.persistence.FlushModeType:

AUTO é o comportamento padrão descrito no trecho do código anterior. COMMIT significa que alterações só são gravadas quando a transação é confirmada, não antes de nenhuma consulta.
FlushModeType.COMMIT faz sentido por razões de desempenho. A melhor maneira de ajustar um aplicativo de banco de dados é remover chamadas desnecessárias ao banco de dados. Algumas implementações de fornecedores farão todas as atualizações necessárias com uma chamada JDBC em lote. Utilizar COMMIT permite que o gerenciador de entidades execute todas as atualizações em um grande lote, limita a quantidade de tempo que a transação tem nesse bloqueio de banco de dados, mantendo-o somente até o fim da confirmação JTA.