Vraptor + vários bancos

Pessoal,

Estou desenvolvendo uma aplicação que vai acessar bancos diferentes, mas com o mesmo schema.
Dependendo do login do usuário ele vai acessar determinado banco de dados.
Existe a possibilidade de injetar o entityManager com o persistenceUtil com as configurações do banco do usuário em tempo de execução.
Alguma outra ideia para fazer isso?

Tipo assim tu vai ter Oracle e Postgresql, e tu vai setar valores nos 2 ou trocar valores entre eles ?

cada cliente vai ter seu banco e só poderá persistir nele!

Isso é um recurso chamado multi-tenancy e não está associado ao VRaptor, mas sim a persistencia (JPA/Hibernate).

Atualmente nenhuma ferramenta de ORM possui suporte nativo a multi-tenancy, e como em ambiente JTA você precisa que o JPA construido na inicialização do container, há duas soluções:

  • Criar um datasource para cada cliente, e um entity-manager para cada cliente. Assim você precisa decidir qual entity manager injetar com o uso de qualifiers do CDI.

  • No Oracle você pode usar um dblink, que nada mais é que uma conexão remota ao outro banco. Tenho usado isso em um projeto atual, onde eu possuo um banco master e os bancos dos clientes. A base master possui os links para as outras bases, e é injetada no entity-manager. Com uso de um query-interceptor eu reescrevo cada query com o banco do cliente, algo como: de select * from contas para select * from contas@cliente01.

O Hibernate 4 está com suporte a multi-tenancy, e tem até uma webminar com o Emmanuel Bernard, um dos desenvolvedores do Hibernate, explicando as diversas estratégias de tenant. Procure por isso no vimeo.com/jbossdeveloper.

Você vai usar o que para determinar qual banco de dados acessar? Essa informação será passada a cada request ou estará na sessão?

Aqui alguns testes que fiz há algum tempo atrás:
http://www.guj.com.br/java/230902-multi-tenant-com-vraptor

se isso for direto (cada usuário/tenant acessa um e somente um banco), vc pode criar um ComponentFactory que recebe o usuário no construtor (vc vai precisar prover esse usuário de algum jeito) e retorna a EntityManagerFactory correta.

Lembre-se de cachear as EntityManagerFactory

Lucas,

No caso o persistence.xml teria um persistence-unit para cada configuração dos bancos.

Tenho um sistema que faz isso :

Struts + Hibernate
Oracle + Postgresql

segue :

[url] http://www.4shared.com/file/Gk15hEvD/UFC-ContasReceberWin.html

[quote=lfcdtv]Lucas,

No caso o persistence.xml teria um persistence-unit para cada configuração dos bancos.[/quote]

Não sou o Lucas, mas pelo que entendi da solução dele, sim, você precisa ter um persistence-unit para cada tenant. Atualmente não é possível ter mais de uma conexão por entity-manager. Talvez com o Hib4 seja possível, mas não sei informar porque ainda não olhei como é feito o multi-tenancy deles.

uma persistence-unit e uma EntityManagerFactory pra cada user/tenant

essa abordagem de multi-tenant torna bem difícil o deploy de um novo usuário/tenant.

mais sobre essa discussão:

http://www.tectura.com.br/topics/abordagens_de_multitenant

Crei um componente simples só para testar:


@Component
@ApplicationScoped
public class CreateEntityManagerFactory implements ComponentFactory<EntityManagerFactory> {

	private EntityManagerFactory factory; 

	@PostConstruct
	public void create() {
		factory = Persistence.createEntityManagerFactory("default");
	}

	public EntityManagerFactory getInstance() {
		return factory;
	}

	@PreDestroy
	public void destroy() {
		factory.close();
	}

}

Ocorreu o seguinte erro:
http://pastie.org/1840723

???

tire o pacote …vraptor.util.jpa configuração de packages do web.xml

Lucas,

O erro continua mesmo tirando a configuração do web.xml

como tá o seu web.xml?

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">

	<display-name>project</display-name>

	<context-param>
		<param-name>br.com.caelum.vraptor.encoding</param-name>
		<param-value>UTF-8</param-value>
	</context-param>

	<filter>
		<filter-name>sitemesh</filter-name>
		<filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class>
	</filter>

	<filter>
		<filter-name>vraptor</filter-name>
		<filter-class>br.com.caelum.vraptor.VRaptor</filter-class>
	</filter>

	<filter-mapping>
		<filter-name>sitemesh</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<filter-mapping>
		<filter-name>vraptor</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<jsp-config>
		<jsp-property-group>
			<url-pattern>*.jsp</url-pattern>
			<page-encoding>UTF-8</page-encoding>
			<include-prelude>/WEB-INF/jsp/prelude.jspf</include-prelude>
		</jsp-property-group>
	</jsp-config>
</web-app>

então a menos que vc tenha duas componentFactory registradas, tá tudo certo. Dê um clean no projeto e no servidor e reinicie o servidor, e teste de novo por favor

Lucas,

Criei dois componentes (EntityManager e EntityManagerFactory) e funcionou!

Lucas,

Em relação ao cache dos componentes, seria uma cache de segundo nível, por exemplo utilizando o EhCache?

cachear = não recriar as EntityManagerFactory.

tanto faz como vc faz isso…

pode até ser com um Map<AlgumaCoisa, EntityManagerFactory> no component factory