Como administrar EntiManager e EntityManagerFactory - hibernate + JPA

13 respostas
R

oi galera,

queria pedir a ajuda de vocês… quem não tiver viajado este feriado pode ajudar-me quebrando a cuca aki :smiley:

estou desenvolvendo uma aplicação desktop e tenho uma dúvida sobre qual a melhor forma de administrar o EntityManagerFactory, o EntityManager e o EntityTransaction…

eu já tinha conhecimento (por causa do meu estágio) para fazer uma aplicação web usando JBOSS e EJB

então resolvi usar JPA para ajudar no banco de dados… mas por se tratar de desktop não usarei JBOSS… então terei que criar as transactions na mão…

dae queria saber se o q eu pretendo implementar é adequado:

pensei em ter um “ManagerFactory” q é inicializado no começo da aplicação e ele cria uma EntiTyManagerFactory (q será única durante toda a aplicação)

dae eu acesso cada Manager individual pelo factory, passando o EntityManagerFactory como parametro (i.e, um getManagerX seria algo como return new ManagerX(entityManagerFactory); )

e cada método dentro de cada Manager individual usa o EntityManagerFactory para:

  1. criar um EntityManager
  2. criar uma EntityTransaction
  3. fazer begin e commit da transaction
  4. fechar o EntityManager

aí minha dúvida é se isso é o mais adequado ou se eu devo colocar o EntityManager tb como algo único q será carregado durante toda a aplicação e cada metodo individual cria apenas as transactions qdo precisarem…

e se tem jeito de eliminar essas transactions (eu não usava transaction com oJBOSS… ele fazia tudo automático)

uma outra coisa q eu não gosto é q geralmente as operações utilizam um método apenas do Manager… e os “getManagerX” sempre criam novas instancias desses Managers… ou seja… para cada vez q eu quiser executar apenas um método eu tenho q criar uma nova instancia do Manager Especifico…

uma opção q eu teria seria o ManagerFactory guardar instancias de cada Manager… mas não sei se isso consumiria muita memória… e pensei tb num meio termo q seria o ManagerFactory ter uma lista com os últimos 5 ManagersUtilizados… uma espécie de “cache de Manager”…

hum… tive uma idéia agora q seria os métodos dos Managers serem estáticos… acho q pra esse problema dos managers parece ser a melhor solução mesmo… q assim eu não crio instancias q ocupariam a memória desnecessariamente…

bom… de qqler forma espero a ajuda das feras q eu sei q têm por aki… e o problema dos EntityFactory, EntityManager e EntityTransaction eu não sei como resolver… principalmente pq eu acho q obter o EntityManager no começo de cada método torna a execução dos métodos lenta…

caso seja complexo de entender o q falei eu coloco alguns exemplos…

qqler ajuda será bem vinda

e agradeço desde já

abraços

13 Respostas

M

Fala rruppel!

Realmente não existe uma estratégia de transações em aplicações desktop stand-alone.
A que você está utilizando é uma sessão (EntityManager) por operação e não é recomendada. Além de não ser muito eficiente você não aproveita as vantagens de transações ACID.

O que você deveria implementar é algo semelhante ao sessionFactory.getCurrentSession() do Hibernate. É bem simples: cria uma classe utilitário que inicializa o EntityManagerFactory (que geralemente só é um por aplicação) e guarda os EntityManagers abertos em um ThreadLocal e os retira quando forem fecahados.

Seus managers então só devem pegar o EntityManager corrente (EntityManagerUtil.getCurrentEntityManager()) e executar as operações necessárias. Nem o inicio e fim da transação deve ser demarcados nos métodos dos Managers.

Aí é que você vai definir como vai ser sua estratégia de demarcação de transações. Você pode demarca-las explicitamente nos métodos que forem transacionais, num fachada, etc. Ou pode fazer como eu faço: utilizando aspectos. Fica bem simples e você pode implementar estratégias de propagação como é feito nos servidores de aplicação Java.

Cria uma annotation chamada @Transactional e depois cria uma aspecto que inicia uma transacao antes da chamada do método e dá commit e fecha o EntityManager no final desse método. Agora é só inserir a annotation nos métodos transacionais. Fica bem “clean”.

Se quiser informações mais detalhadas é só falar.

[]'s

R

vou aproveitar q vc acabou de responder para pedir mais informações sim… hehehehe

muito complexo :smiley:

de qqler forma valeu pela ajuda…

eu vou tentar implementar o q vc falou… mas se vc ler essa mensagem e puder me detalhar melhor como fazer isso eu agradeceria…

mas como eu falei… estarei aki tentando implementar isso aí…

obrigado

abraços

M

Essa classe seria algo parecido com o código abaixo (tirado dessa therad: http://www.guj.com.br/posts/list/61216.java)

public final class PersistenceUtil {
	
	private static final String UNIT_NAME = "trip";
 	
	private static EntityManagerFactory FACTORY;
	
	public static final ThreadLocal<EntityManager> SESSION = new ThreadLocal<EntityManager>();
	
	public static EntityManager currentEntityManager() {
		EntityManager manager = (EntityManager) SESSION.get();
        if (manager == null) {	
        	loadInstance();
        	manager = FACTORY.createEntityManager();
            SESSION.set(manager);
        }
        return manager;
    }
	
	public static void closeEntityManager() {
		EntityManager manager = (EntityManager) SESSION.get();
        if (manager != null) {
        	manager.close();
        }
        SESSION.set(null);
    }

	private static synchronized void loadInstance() {
		if (FACTORY == null) {
			FACTORY = Persistence.createEntityManagerFactory(UNIT_NAME);
		}
	}
	

}

A classe acima pode ser melhorada bastante, mas só estou colocando como exemplo de ponto de partida. Agora nos seus DAOs (ou Managers como queria) os métodos seriam mais ou menos dessa forma:

public void save(Client client) {
      PersistenceUtil.currentEntityManager().save(client);
}

Perceba que eu não demarquei a transação no DAO. Essa demarcação pode ser feita nos clientes do DAO. Por ex:

public void operacao() {
   ...
 
   Client c = new Client("Teste", "2");
   Client c1 = new Client("Teste1", "3");

   EntityManager manager = PersistenceUtil.currentEntityManager().beginTransaction();

   clientDAO.save(c);
   clientDAO.save(c1);

   manager.commit();
   PersistenceUtil.closeEntityManager();
   ...
}

A ultima sugestao que fiz foi eliminar esses códigos de transação e isolá-los em um Aspecto (de uma olhada em AspectJ) que intercepta métodos com uma anotação do tipo @Transactional. A operação então ficaria da seguinte forma:

@Transactional
public void operacao() {
   ...
 
   Client c = new Client("Teste", "2");
   Client c1 = new Client("Teste1", "3");

   clientDAO.save(c);
   clientDAO.save(c1);

   ...
}

Ta sacando?

[]'s

R

primeiramente muito obrigado novamente por responder… e desculpa a amolação :smiley:

acho q sakei o q vc falou…

basicamente com o AspectJ eu falarei para o programa q:

sempre q tiver a anotação @Transactional em um método ele deve pegar o EntityManager antes do método e iniciar a transação com ele…

e ao término do método deve dar commit e fechar o EntityManager

agora só queria entender isso: vc disse q o q eu pretendia fazer era uma sessão por operação… mas isso q vc tá fazendo tb não é uma sessão por operação?

agora eu não sei se vc foi genial ou se eu não entendi…

pq pensando agora mais no detalhe da minha aplicação, acho q eu teria uma camada de DAO´s (q não seriam os Managers!) e uma camada de Manager (q executariam as operação de negócio - não sei se o termo “cliente do DAO” se refere a isso… pois a camada de negócio utiliza os DAOs…)

assim para cada método do Manager (método de negócio) eu pretendia iniciar um EntityManager, chamar os DAO´s quantas vezes fosse necessário passando esse entityManager iniciado e no fim do método dar commit e finalizá-lo…

não sei se vc já tinha percebido isso e achou q eu me referia em criar um EntityManager a cada operação de DAO (digamos a cada select) ao invés de a cada operação de negócios (na qual deve valer o ACID). Assim eu acabaria tendo vários EntityManagers diferentes para uma mesma operação de negócios (o q eu tb concordo q eh errado)

mas enfim… não! eu não pretendia criar um EntityManager por operação de banco de dados… e sim! eu pretendia criar um EntityManager por operação de negócios.

agora… a sua solução com AspectJ eu achei fantástica! linda! mas será q vou conseguir entender para implementar rapidamente?

a grande vantagem do q vc falou, ao meu ver, é a limpeza do código… (evita q eu tenha q criar um entityManager a cada método de negócio) e evita q eu tenha q ficar passando meu entityManager para os DAO´s realizarem operações “unitárias”)

ou seja, agora q eu espero ter esclarescido exatamente o q eu pretendia fazer e a vantagem q eu vejo no seu método, vem a pergunta:

tem mais alguma vantagem ? (pq vc falou q a minha implementação ficava ineficiente, mas acho q vc tinha entendido um entityManager por operação de DAO)

ele realmente é mais eficiente? (pois no fundo essa sua solução tb cria um EntityManager por operação de Negócios… )

será fácil aprender e implementar em pouco tempo?

hehehehe tô judiando de vc nas perguntas… agradeço sua boa vontade…

se serve de consolo acho q eh bom ter um iniciante interessado e metralhando-o de dúvidas q assim mesmo quem já conhece mais o assunto pode colocar a prova os próprios conhecimentos :smiley:

abraços

e muito obrigado Marcio

ps: agora toca a entender AspectJ…rs

ps2 (IMPORTANTÍSSIMO): vc citou q eu deveria implementar o q o Hibernate já faz: sessionFactory.getCurrentSession()
o Hibernate já usa esse eskema de AspectJ ? se sim eu poderia simplesmente mudar o foco para Hibernate ao invés de JPA e utilizar o q já está pronto ao invés de “perder” tempo desenvolvendo essa funcionalidade (claro q eu ganharia o tempo… mas é só pq esse sistema eu tava fazendo pra loja de um amigo e pq eu gosto de fazer coisas novas e tava vendo como me saia criando uma aplicação desktop no feriado :D)

abraços

R

bom... usei um pouco do q vc falou.. tentei achar coisas na internet sobre AspectJ, e de fato achei bastante coisa.. mas nenhuma com exemplos bem práticos.. acho q entendi o conceito... e tb não sei se essa coisa de ter q compilar "fora do Java" seria fácil de juntar ao NetBeans (q eu toh usando)

um exemplo de método teste ficou assim

ProdutoEstoque produto = new ProdutoEstoque();
       produto.setCodigo(10);
       produto.setDescricao("O senhor é um fanfarrão 02!");
       produto.setNome("Produto 02");
       produto.setQtde(5);
       
       PersistenceUtil.beginTransaction();
       PersistenceUtil.persist(produto);
       PersistenceUtil.commitTransaction();

perceba q eu dei ao Persistence util os encargos de beginTransaction e commit transaction... tornando os metodos de negócios mais simples.. porém criando um risco: se dois metodos utilizarem o beginTransaction e commit Transaction de forma intercalada podem ocorrer coisa inesperadas...

porém, é uma aplicação desktop e não corro esse risco aparentemente...

e apesar q se o AspectJ funcionar modificando os métodos como se fosse alguém adicionando as linhas no programa ( q eu acredito q eh como ele funciona) então esse risco é o mesmo....

de qqler forma muito obrigado pela ajuda... e se vc quiser responder as perguntas do post de cima eu agradeceria pq aumentaria meu conhecimento =)

abraços

R

Ou então, vc pode baixar a versão “embedded” do EJB, que roda fora do JBOSS e utilizar normalmente, sem precisar implementar nada. Claro que seu projeto vai ficar “um pouco” grande em temor de MB, mas é uma alternativa.

http://www.jboss.com/products/jbossmc

[]'s

M

Fala!

Vou dar uma lida nos seus posts e responder…

valeu!

M

basicamente com o AspectJ eu falarei para o programa q:

sempre q tiver a anotação @Transactional em um método ele deve pegar o EntityManager antes do método e iniciar a transação com ele…

e ao término do método deve dar commit e fechar o EntityManager

Isso! Básicamente. E geralmente é só o que você vai precisar. Mas você pode implementar uma espécie de conversação, passando parâmetros para a Annotation @Transactional. O Spring tem algo bem semelhante implementado, mas nunca usei.

agora só queria entender isso: vc disse q o q eu pretendia fazer era uma sessão por operação… mas isso q vc tá fazendo tb não é uma sessão por operação?

agora eu não sei se vc foi genial ou se eu não entendi…

Bom… é verdade. Agora eu vi que os seus managers não sao os DAOs. Na verdade é outra camada acima dos DAOs.
Não deu pra sacar como você esta fazendo agora. Pelo jeito esta usando os managers, mas percebeu que não precisa mais passar a sessionFactory para eles (usa o PersistenceUtil).

Uma coisa que não entendi foi o PersistenceUtil.persist(produto). Por que o metodo persist nessa classe? Desistiu do DAO? Se você quiser acessar outros métodos do EntityManager vai ter que replica-los no PersistenceUtil? Sugiro apenas utilizar um método que retorne o EntityManager que se encontra aberto no momento. E voce persistiria da seguinte forma:

Agora você tem acesso a todas as funcionalidade do EntityManager.

Sua implementação é válida. Você tem uma “fachada” onde as operações são transacionais e abriu mão dos aspectos (pelo menos por enquanto) :).
Dessa forma você não tem mais um transação por operação. Acho que para seu escopo a arquitetura esta boa e suficiente.

Quanto ao uso do aspecto, eu prefiro. Ele é reutilizavel, é rapido de usar e “clean” e ainda implemento nele outras funcionalidades (conversação, propagação de transação), tornando o uso de transações no ambiente desktop bem parecido com o do servidor de aplicação.

O que eu faço é geralmente o seguinte. Todos os métodos do meu DAO são transacionais (anotados com @Transactional). Dessa forma eu posso evitar usar o @Transactional em algumas operações de negócio que envolvem operações pontuais. Mas caso eu coloque uma anotação @Transactional numa operação de negócio ela é propagada para as transações do DAO. Tipo, o DAO também é transacional, mas ao invés de ele abrir uma nova transação ele verifica se já existe alguma iniciada e a utiliza. Existem outras estratégias de propagação que você pode procurar no google (REQUIRED_NEW, READ ONLY). E ainda posso implementar conversações.

Mas sem tentar matar uma mosca com bala de canhão né? hehe

Quanto ao uso de aspecto, tenta procurar um plugin para o netbeans. Com o Eclipse fica fácil porque tem um plugin muito bom!

Quando tiver tempo tenta fazer um teste usando esse tipo de implementação. Vale pelo aprendizado, e depois de feito, é facilmente reusável em outros projetos.

[]'s

R

Exato! Agora nos DAO’s eu, quando quiser acessar o EntityManager, não preciso passá-lo como parametro… fica bem mais bonito =)

E o PersistenceUtil.persist é bixisse minha… eh q eu odeio fazer essas chamadas de método encadeada… PersistenceUtil.currentEntytiManager().persist

e mesmo que eu tenha q recriar vários pequenos métodos na minha classe PersistenceUtil eu acho mais bonito =)

é claro q sei usar o bom senso… e se eu tiver q usar muitos recursos do EntityManager eu mudarei de estratégia… é mais questão de gosto pessoal =)

Marcio Biza:

Mas caso eu coloque uma anotação @Transactional numa operação de negócio ela é propagada para as transações do DAO. Tipo, o DAO também é transacional, mas ao invés de ele abrir uma nova transação ele verifica se já existe alguma iniciada e a utiliza. Existem outras estratégias de propagação que você pode procurar no google (REQUIRED_NEW, READ ONLY).

Esse é um ponto chave q eu tinha pensado q tornaria as coisas um pouco mais chatas mas tinha eskecido de comentar

Sem dúvida q procurarei estudar e fazer um projeto usando essa estratégia… muito bom!

rapaz… vc foi ótimo! passou um pouco da sua experiência e me deixou até confiante com minhas escolhas :D… muito obrigado mesmo!

abraços

L

Desculpem reabrir esse post, mas aproveitando quero tirar minhas dúvidas.
Por que o JPA sempre sobe todas as classes novamente quando chamo a persistencia.
digamos.

Baseado na classe PersistenceUtil informada no post:

BsEmpresa a = PersistenceUtil.currentEntityManager().find(BsEmpresa.class, id); PersistenceUtil.closeEntityManager();

sempre quando chamo o metodo sobe as classes tudo novamente conforme abaixo.
já no hibernate isso não acontece, uma vez que sobe o hibernate.

Isso é normal? Sempre vai ocorrer isso?
Por que imagina são 8 segundos para subir cada vez que chamo o metodo, multiplica isso para 1000 pessoas utilizando a aplicação simultaneamente.

18:32:10,529 INFO  [AnnotationBinder] Binding entity from annotated class: entidades.BsClassificacaoFiscal
18:32:10,529 INFO  [EntityBinder] Bind entity entidades.BsClassificacaoFiscal on table bs_classificacao_fiscal
18:32:10,533 INFO  [AnnotationBinder] Binding entity from annotated class: entidades.BsCliente
18:32:10,533 INFO  [EntityBinder] Bind entity entidades.BsCliente on table bs_cliente
18:32:10,545 INFO  [AnnotationBinder] Binding entity from annotated class: entidades.BsColaborador
18:32:10,546 INFO  [EntityBinder] Bind entity entidades.BsColaborador on table bs_colaborador
18:32:10,560 INFO  [AnnotationBinder] Binding entity from annotated class: entidades.BsContatoCli
18:32:10,560 INFO  [EntityBinder] Bind entity entidades.BsContatoCli on table bs_contato_cli
18:32:10,565 INFO  [AnnotationBinder] Binding entity from annotated class: entidades.BsContatoForn
18:32:10,565 INFO  [EntityBinder] Bind entity entidades.BsContatoForn on table bs_contato_forn
18:32:10,567 INFO  [AnnotationBinder] Binding entity from annotated class: entidades.BsDepartamento
18:32:10,567 INFO  [EntityBinder] Bind entity entidades.BsDepartamento on table bs_departamento
18:32:10,571 INFO  [AnnotationBinder] Binding entity from annotated class: entidades.BsEmpresa
18:32:10,571 INFO  [EntityBinder] Bind entity entidades.BsEmpresa on table bs_empresa
18:32:10,591 INFO  [AnnotationBinder] Binding entity from annotated class: entidades.BsFabricante
18:32:10,591 INFO  [EntityBinder] Bind entity entidades.BsFabricante on table bs_fabricante
e vaii........................................
e continua............>>>
18:32:13,961 INFO  [TableMetadata] table found: public.bs_vendedor_banco
18:32:13,961 INFO  [TableMetadata] columns: [nome_banco, gerente_conta, conta, cod_banco, id_bs_vendedor_banco, agencia, fone, ano_abertura, id_bs_colaborador]
18:32:13,961 INFO  [TableMetadata] foreign keys: [relationship15, fkc81e711142958b42]
18:32:13,961 INFO  [TableMetadata] indexes: [key11]
18:32:14,028 INFO  [TableMetadata] table found: public.sy_cfop
18:32:14,028 INFO  [TableMetadata] columns: [id_sy_cfop, cfop]
18:32:14,028 INFO  [TableMetadata] foreign keys: []
18:32:14,028 INFO  [TableMetadata] indexes: [key20]
18:32:14,088 INFO  [TableMetadata] table found: public.sy_classificacao
18:32:14,088 INFO  [TableMetadata] columns: [id_sy_classificacao, classificacao]
18:32:14,088 INFO  [TableMetadata] foreign keys: []
18:32:14,088 INFO  [TableMetadata] indexes: [key17]
18:32:14,150 INFO  [TableMetadata] table found: public.sy_estado
18:32:14,150 INFO  [TableMetadata] columns: [id_sy_estado, estado]
18:32:14,150 INFO  [TableMetadata] foreign keys: []
18:32:14,150 INFO  [TableMetadata] indexes: [key8]
18:32:14,232 INFO  [TableMetadata] table found: public.sy_imagens
18:32:14,232 INFO  [TableMetadata] columns: [id_bs_cliente, id_sy_imagens, imagem]
18:32:14,232 INFO  [TableMetadata] foreign keys: [fkcfdbbb87c6ad7922, relationship11]
18:32:14,232 INFO  [TableMetadata] indexes: [id_sy_imagens, key10]
18:32:14,295 INFO  [TableMetadata] table found: public.sy_municipio
18:32:14,296 INFO  [TableMetadata] columns: [id_sy_estado, id_sy_municipio, municipio]
18:32:14,296 INFO  [TableMetadata] foreign keys: [fk8af259542ee4727e, relationship1]
18:32:14,296 INFO  [TableMetadata] indexes: [key7]
18:32:14,354 INFO  [TableMetadata] table found: public.sy_telas
18:32:14,354 INFO  [TableMetadata] columns: [ver_perm, caminho, id_sy_telas, descricao, sub_menu, menu]
18:32:14,354 INFO  [TableMetadata] foreign keys: []
18:32:14,354 INFO  [TableMetadata] indexes: [key22]
18:32:14,414 INFO  [TableMetadata] table found: public.sy_tp_cliente
18:32:14,414 INFO  [TableMetadata] columns: [tipo_cliente, id_sy_tp_cliente]
18:32:14,414 INFO  [TableMetadata] foreign keys: []
18:32:14,414 INFO  [TableMetadata] indexes: [key1, id_sy_tp_cliente]
18:32:14,415 INFO  [SchemaUpdate] schema update complete
K

Up!

Desculpem novamente reabrir o tópico, mas estou com o mesmo problema do ultimo post…

Alguém resolveu?

Um abs.

Rodrigo

L

Cara… sofri nesse negocio ate que mandei pro espaço e fiquei no Hibernate puro mesmo.
Sou fiel ao hibernate, JPA pra mim so reiventaram a roda.

D

leandrogmuller:
Cara… sofri nesse negocio ate que mandei pro espaço e fiquei no Hibernate puro mesmo.
Sou fiel ao hibernate, JPA pra mim so reiventaram a roda.

Desculpe eu reviver um topico de 2 anos atrás,
mas eu preciso dizer algo a respeito deste comentário…

Se eu não me engano JPA = especificação HIBERNATE = Implementação.
Portanto, como uma especificação pode “reinventar a roda”?
…“Hibernate puro”…

Desculpe se eu estiver errado :stuck_out_tongue:

Criado 16 de novembro de 2007
Ultima resposta 23 de nov. de 2012
Respostas 13
Participantes 6