Oi,
Que garoto de sorte vc por estar usando o Seam :lol:
Bom, existe um projeto do hibernate chamado Shards, mas ainda está muito cru, inviável para um ambiente de produção.
Ele visa trabalhar com arquiteturas como a nossa e oferece algumas coisas mais complexas(que eu precisei, mas vc não vai) como um “join cross-schema”
Bom, eu ia bloggar sobre essa solução se eu tivesse saco e tempo pra manter um blog, como não é o caso, vou tentar ser sucinto aqui indicando os passos que fiz, caso vc encontre dificuldade em algum , vou te ajudando com código na medida que eu puder, pq a solução completa é grande e não posso expor os fontes por completo.
-
Crie um hibernate.cfg.xml e coloque suas propriedades específicas de banco lá(Cuidado para não colocar um name na parte session-factory se não ele vai atribuir ao JNDI). Não coloque atributos específicos de algum esquema, como connection.url, user_name ou password(mto menos dataSource)
-
Crie uma classe que possa montar sua SessionFactory a partir desse arquivo, o típico HibernateUtil, mas coloque ele como um componente Seam, deixe-o Stateless. Lembre-se, o método deve retornar a SessionFactory, e não armazená-la em alguma variável. Esse método vai receber como parâmetro um Usuario de seu sistema, procure deixar na tabela do seu usuário o nome do schema ao qual ele vai se conectar, importante essa info lá.
Aqui vc vai informar, programaticamente, as configurações que vc não passou no passo 1, para que seja tudo relativo ao usuário que entrou como parâmetro do método.
-
Por motivos de performance, é melhor vc iniciar todas suas SessionFactories em tempo de deploy, para isso vamos criar um SessionFactoryPreloader. Essa classe vai ler do banco ‘global’(aquele que não é específico de cada usuário) todos os usuário cadastrados para que vc possa montar suas respectivas SessionFactories, mais abaixo mostro como vc vai conectar nesse banco. Ainda nessa classe, sempre que vc criar uma SessionFactory(vc vai fazer um loop para cada cliente seu) vc vai jogar ela em escopo de aplicação usando como alias aquele atributo que especifiquei no item 2, eu estou usando o nome do schema do usuario em questão, que é um atributo que está na minha classe Usuário.
for ( Customer customer : allCustomers ) {
SessionFactory sessionFactory = this.hibernateUtil.createSessionFactory(customer);
Contexts.getApplicationContext().set(customer.getMysqlDatabase(), sessionFactory);
}
Edit: Para que sua classe suba em tempo de deploy, não se esqueça de anotá-la com @Startup, essa classe deve ser ScopeType.APPLICATION tb
- Vamos agora criar uma classe que possa nos devolver a sessionFactory correta dado um usuario, isso será usado no item seguinte.
Algo bem simples como:
@In(value="#{customer}")
private customer customer;
@Factory(scope=ScopeType.SESSION, value="correctSessionFactory", autoCreate = true)
public SessionFactory getCorrectSessionFactory() {
SessionFactory sessionFactory =
(SessionFactory) Contexts.getApplicationContext().get(customer.getMysqlDatabase());
if ( sessionFactory == null )
throw new InfraRuntimeException("Erro ao obter SessionFactory para " + customer.getMysqlDatabase());
return sessionFactory;
}
Como você pode ver, devemos ter em algum escopo(de preferencia session) a variável customer, que é o usuário dono do schema ao qual queremos conectar
- Agora vem a mágica do seam, aqui que você vê de fato a diferença dele
No seu components.xml, coloque o seguinte:
<persistence:managed-hibernate-session name="hibernateUserSession" auto-create="false"
session-factory="#{correctSessionFactory}" />
Como você pode ver, estamos usando #{correctSessionFactory} como a sessionFactory correta, ai que está o truque, ele só vai dar um evaluate nessa expressão quando vc solicitar a hibernateUserSession, ou seja, em tempo de execução, e quando isso acontecer, ela terá um valor, desde que você tenha um usuário em sessão.
Basicamente a idéia é essa, o acesso ao seu banco ‘global’ pode ser configurado da forma convencional, a partir dele vc obtem o usuário correto e joga-o no escopo Session do Seam, daí pra frente, caso você tenha feito todos esses passos que te disse, sempre que vc precisar da sessão do usuário você pode injetá-la via #{hibernateUserSession} que o Seam se encarrega de jogar lá justamente a session que você precisa.
Qualquer dúvida posta ai que eu procuro ajudar,
Abraço,
Pedro Sena