EJB3 - Criando EntityManagers dinamicamente (pegando informações da base de dados por examplo)

Como perguntei no título, como criar EntityManagers dinamicamente?
Um exemplo: Minha aplicação tem 4 clientes (EmpresaA, EmpresaB, EmpresaC, EmpresaD).
Amanhã, quando eu for adicionar o quinto cliente, não quero criar uma nova variável de instância (a quinta) to tipo EntityManager anotada com @PersistenceContext que me exigiria um novo build e um novo deploy.
Li no tutorial Java EE 6 que eu posso usar um @PersistenceUnit para criar um EntityManagerFactory e através dele, usar o método createEntityManager() para criar EntityManagers (e armazená-los em um HashMap por exemplo).
É esta a maneira correta de fazer? É uma boa eu mesmo gerenciar esta criação de EntityManagers?
Estou preocupado com concorrência, melhores práticas e etc. Eu uso o JBoss 7 como servidor de aplicação.
Muito obrigado.

Bem, não sei qual o contexto geral, mas aparentemente seu design não esta muito legal.

Você pode procurar algo sobre “multitenancy” (o hibernate 4 suporta). Falando por cima, é como suportar varias instancias “isoladas” de um sistema (como que para N clientes) em apenas uma instancia.

Obrigado Mikhas.
Excelente keyword pra eu pesquisar no Google sobre o assunto.
Assim que eu achar uma implementação confiável posto aqui.
Enquanto isso se mais alguém puder ajudar vai ser muito bem vindo.
T+

Eu acho que multi-tenacidade é a solução para seu problema.

Se você passar mais detalhes e talvez algum diagrama de classes, podemos ajudar a encontrar alguma outra saida.

É o seguinte Mikhas.
Tenho uma aplicação que tem 10 clientes. Para cada um desses clientes eu tenho uma variável de instância do tipo EntityManager anotada com @PersistenceContext.
Então:


@PersistenceContext
private EntityManager cliente1;

@PersistenceContext
private EntityManager cliente2;

E assim por diante.
Mas como a empresa onde trabalho está expandindo depressa, provavelmente em uns 6 meses vamos ter uns 40 clientes em uns 3 servidores diferentes.
O jeito como está hoje, vai dar trabalho na hora de adicionar novos clientes, exigindo novo deploy e etc…
Gostaria de manter as informações dos clientes da aplicação na base de dados, assim, quando o servidor subir, eu já pego no banco as informações dos clientes atuais e construo os EntityManagers dinamicamente.
Mas pelo que eu ando lendo, se eu usar o createEntityManager() do EntityManagerFactory, vou ter que gerenciar transações na mão e perder muitos recursos do gerenciamento do container, coisa que eu não quero.
Então minha missão agora é construir a melhor arquitetura possível para suportar estes cenários.

Gostaria de saber o que muda em cada EntityManager? a base de dados? o servidor? o modelo? o schema?

Cada EntityManager representa uma base de dados, isto é, uma base para cada cliente (as bases são exatamente iguais, as mesmas tabelas).
Portanto, cada EntityManager representa a conexão com a base para um determinado cliente.

E em algum lugar da sua aplicação tem um “if” que faz cada cliente usar o entity manager correto…

Bem, na minha opinião esse não um bom design mesmo :confused: .

Minhas sugestões são: 1 - Criar varias instancias da aplicação, cada uma cuidando de apenas um cliente; 2 - Implementar multi-tenacidade;

Acredito que 2 seja melhor para um grande numero de clientes.
Basicamente, você vai ter que criar uma coluna nas suas tabelas indicando a qual cliente (tenant) o registro pertence.
O bom dessa abordagem é que torna muito facil você adicionar um novo tenant e é possivel optimizar a base de dados para trabalhar dessa maneira. Pelo menos o Oracle e o Sybase eu sei que rola.

Mikhas, já trabalhei desta forma, adicionando uma coluna para identificar o cliente, mas a experiência não foi boa.
E o nosso sistema vai ficar muito grande a nível de dados, por isto descartei esta hipótese.
A única coisa que está me impedindo é a criação destes EntityManagers de forma dinâmica (o que acho q não vou conseguir sem usar o EntityManagerFactory e ter que controlar as transações na mão).

Essa é uma abordagem bastante utilizada. Se você te um bom SGBD, não precisa se preocupar com volume de dados e como disse, pode se optimizar a base para trabalhar dessa maneira.

Você deve ter em mente que criar varios Entity managers é bastante custoso em termos de memoria. Se você trabalha com poll de conexões, tera uma grande numero de threads rodando para gerenciar as conexões e tambem varias conexões abertas simultaneamente.

Mas será que chega a ser tão custoso assim? Em uma máquina com 8 GB de memória?
Tive uma experiência uma vez onde colocamos 20 clientes rodando em multitenancy (cada um com sua base, utilizando Hibernate e subindo todas as Sessions utilizando AnnotationConfiguration na hora de subir o JBoss 4.2.3, dentro de um SAR) e o servidor aguentava tranquilamente.
Não sei como é no JPA com EntityManager.

Olá jaabax,

Estou conseguindo resolver esse problema usando o Hibernate 4 com o conceito de multi-tenancy. O único problema, é que ainda não é 100% compativel com JTA, ai da uns erros… mas se configurar o JPA como RESOURCE_LOCAL funciona perfeitamente, bem transparente. Quanto as transações, estou controlando com um interceptador. Se tu tiver a opção de atualizar a versão do Hibernate no teu projeto, vai abrir novas possibilidades. Ele faz exatamente isso que queremos… de acordo com um identificador na sessão tu usa um ConnectionProvider pra uma base especifica. Na verdade existem três estratégias que tu pode escolher, por base, schema ou um atributo na tabela. Essa última opção do atributo ainda não está funcionando transparentemente, pelo que vi. Eu estou usando a estratégias de tenancy pot base.

E quanto a SessionFactory, pelo que pude perceber tu cria apenas uma para todos os bancos, então não tem o custo e o peso dela para cada tenancy, e o cache de segundo nível, segundo a documentação é suportado, pois alem do identificador do objeto, ele passa a guardar junto o identificador do tenancy.

Aqui a referência da versão: http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html/ch16.html

flw

Muito obrigado pelo link Ricardo.
Lí e fiquei muito feliz que os frameworks finalmente estão começando a considerar multitenancy (sério, é um absurdo isso estar sendo implementado agora).
Mas por enquanto vou continuar usando vários EntityManagers, até o JPA implementar este pattern (como foi dito na documentação que vc passou).
Eu tb não posso ficar preso no Hibernate.
Preciso estar isolado pelo JPA, é um dos requisitos que eu tenho que cumprir.
:smiley:

[quote=jaabax]Muito obrigado pelo link Ricardo.
Lí e fiquei muito feliz que os frameworks finalmente estão começando a considerar multitenancy (sério, é um absurdo isso estar sendo implementado agora).
Mas por enquanto vou continuar usando vários EntityManagers, até o JPA implementar este pattern (como foi dito na documentação que vc passou).
Eu tb não posso ficar preso no Hibernate.
Preciso estar isolado pelo JPA, é um dos requisitos que eu tenho que cumprir.
:D[/quote]

Olá Jaabax !!
Cara estou passando por essa mesma situação. Tenho uma única aplicação e para cada cliente novo vou ter uma nova base de dados, consequentemente um novo PersistenceUnity que precisa ser injetado.
Vi que seu post é de 2012, por acaso você encontrou alguma solução para conseguir usar o @PersistenceContext do EJB e passar dinamicamente o PU ? Porque atualmente estou considerando usar sem EJB e criar os EntityManager na mão como você disse, porém vou perder todo o recurso do EJB e também não queria perder isso.

Estou procurando isso a alguns dias e não encontrei nada que seja nativo do JPA + EJB.
Se puder compartilhar como resolveu esse problema fico grato.

Abraço.

Acredito eu que não seja possível passar o nome do persistence unit dinamicamente, usando @PersistenceContext. Acho inclusive que isso é algo que falta na especificação. Se duvidar já tem até ticket aberto pra isso. (usando Java EE puro CDI + EJB).

Você consegue programaticamente, mas ai seria um application managed entity manager, e não um container managed. O que tem seus downsides.

É uma pena pois no Spring isso é facil de fazer, e até no falecido JBoss Seam.