Multi Tenancy com Interceptor

Pessoal, tenho um sistema multi tenancy com hibernate e postgree, cada cliente possui um schema.
Quando estava iniciando o projeto tinha feito uma SessionFactory para cada cliente conectada no seu devido schema, mas isto comeu muita memória e desisti, fiz da seguinte maneira:

Tenho um interceptor que para todas as querys antes de rodar no banco e troca o schema de acordo com o cliente conectado, eu guardo o schema do cliente na session, por exemplo:

O hibernate gera

select * from schemapadrao.Alunos

e o interceptor troca por por select * from schemacliente.Alunos.

Tudo funciona maravilhosamente bem, com dois clientes.

Eu gostaria de saber, se isto pode acarretar um problema no futuro, não posso usar 2nd level neste caso?

Obrigado

Amigo, eu uso exatamente esta estrategia no meu sistema, e realmente, as session factores ocupa muita memoria, porem até o momento nao tem sido um problema.
Eu ja tentei fazer o que vc fez com o interceptor, e SIM, acarreta problemas. Eu nao sei te dizer quais, pois faz tempo que tentei essa estrategia, mais recomendo vc desistir.

Agora recetemente descobri sobre o multi-tenancy do hibernate4, to correndo atras para ver como implementa, se vc tiver novidades, ou alguma outra pessoa que ler este post, agradeço.

Abraços.

mizael86, faz um tempo que eu vi o hibernate 4, eu tentei implementar na época, mas não era a versão final, devia faltar alguma coisa, sei lá, abandonei a idéia, agora ja lançaram a versão final, vou dar uma olhada também
se quiser, podemos ver juntos, só manter este post.

Eu tenho extremo interesse nisto.

Noticia boa para ti entao, depois que postei aqui, continuei testando, e consegui fazer. Realmente funciona, e ficou perfeito.

A questão é que ta la na empresa, vou ver se baixo aqui de casa, são 3 classes que resolve todo o esquema de multi tenant.
E melhor, eu fiz usando o pool de conexoes do c3p0, pq na net vc só encontra ConnectionProvider nonPooled como exemplo :slight_smile:
Vou ver se pego aqui e posto para ti as classes.

Esqueci de deixar meu logmein ativo na empresa, mais sem problemas, amanha pego e posto para ti. :slight_smile:
Abraços

Nossa mizael86, vou lembrar de você para o resto da vida.

Por favor, posta as classes pra mim.

Como prometido, segue em anexo as classes. Qualquer duvida sobre o funcionamento, é so postar :slight_smile:
Só acrescentando, utilizei o hibernate 4.1.4 Final

Vou testar e posto aqui o que encontrar de anormal ou idéias!

Olá.
Estou desenvolvendo uma aplicação multi tenancy onde quero manter a mesma aplicação porém bancos de dados distintos.
Utilizo Spring 3.0.5, Hibernate 3.6.9. Uso uma camada para regra de negócios (service), outra para persistência (dao) e outra para controllers (web).
Minha idéia é na tela inicial (login), incluir 3 campos: dominio, login e senha.

Esse campo domínio saberei qual o banco de dados.
Podem me ajudar com isso?

Minha solução foi exatamente assim, três campos no login.

Eu estou usando ainda o interceptor, para trocar o schema no momento da persistencia com o banco, mas estou migrando para o hibernate 4 que já dá o suporte para multi tenancy

qualquer coisa estamos ae

Para mim ainda não está claro onde preciso mudar as configurações…

No application-context, tenho definido os seguintes beans:

	<!-- DATABASE -->

	<context:property-placeholder location="classpath*:META-INF/database.properties" />
	
	<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
		<property name="driverClassName" value="${database.driverClassName}" />
		<property name="url" value="${database.url}" />
		<property name="username" value="${database.username}" />
		<property name="password" value="${database.password}" />
	</bean>

	<!-- ENTITY MANAGER -->

	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="persistenceUnitName" value="persistenceUnit" />
	</bean>

	<!-- TRANSACTION MANAGER -->

	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
		<property name="dataSource" ref="dataSource" />
	</bean>
	
	<tx:annotation-driven transaction-manager="transactionManager" />
	<mvc:annotation-driven />

No DAO eu tenho a injeção do @PersistenceContext, ao meu ver isso que precisa mudar.

	@PersistenceContext
	public void setEntityManager(EntityManager entityManager)
	{
		this.entityManager = entityManager;
	}

Você pode me ajudar a fazer essa configuração ou me dar os passos?
Eu imagino que preciso remover essa injeção automática do PersistenceContext, mudar as configurações no persistence.xml mas ainda não ficou claro para mim.

Eu não sou especialista em spring.

Eu tenho um interceptor

public class Interceptor extends EmptyInterceptor {

	// http://valdemarjr.net/2010/02/21/hibernate-interceptor-para-auditar-entidades.html]
	// http://java.dzone.com/articles/using-a-hibernate-interceptor-

	private static final long serialVersionUID = 1L;

	@Override
	public String onPrepareStatement(String sql) {

		// EFETUA A O REPLACE DO SCHEMA_DEFAULT PARA O SCHEMA DO CLIENTE
		// CONECTADO
		return sql.replace(Constantes.SCHEMA_DEFAULT,
				ParametrosSistema.getSchemaCliente());
	}
}

Veja ae

Pessoal, estou pesquisando sobre multi tenancy e tentando agregar isso a uma implementação em JSF 2 + glassfish 3 + hibernate + postgresql.

Bem, a parte de configuração do banco não cheguei ainda, na verdade não comecei muita coisa, apenas criei um projeto base para testes e estou um pouco perdido em qual abordagem utilizar para identificar os multiplos tentants e isolar cada um deles.

No meu exemplo, eu pretendo criar os contextos da seguinte maneira:
http://tentant1.myapp.com.br, http://tentant2.myapp.com.br, http://tentantn.myapp.com.br.

Eu vi que vocês sugeriram utilizar 3 campos no login, no entanto, eu acho isso um pouco ruim para o usuário. Gostaria de deixar o mais transparente possível, bastante que ele digite a url (uma url que ele possa salvar em favoritos por exemplo) e ao acessar o sistema, no login entrasse apenas com o usuário e senha e o sistema fizesse o resto!

Poderiam me sugerir como tratar isso da melhor maneira, respeitando boas práticas.

Agradeço!

obrigado

Pirado18, existe poucas boas práticas para este tipo de sistema, falando de hibernate, é algo muito novo e só na versão 4 que multi-tenancy é nativo.

Bom, eu não acho interessante este esquema de colocar o tenant na url, vc vai ter um puta trabalho para programar o dns, redirecionamentos e tal, eu não abordaria desta forma.

A forma mais simples de fazer é ter 3 campos em tela, para ficar mais simples para o usuário, o campo que identifica o tenant vc pode deixar sempre preenchido(usa cookies para isto), sei lá.

Eu não usaria desta forma que você está sugerindo

Raiduster, neste caso, TODOS os usuários do sistema, seja ele admin ou não, de um cliente, por exemplo, digamos que eu tenha um dos tenants o “cliente 1”, e ele tenha acesso com 3 usuários: admin, joao e maria.

no caso todos os 3 usários precisariam passar a string do tenant, certo?
Exemplo da tela:

Entre com a chave do cliente: [cliente1]
Entre com o seu login: [admin]
Entre com sua senha:[senhaadmin]

Entre com a chave do cliente: [cliente1]
Entre com o seu login: [joao]
Entre com sua senha:[senhajoao]

Seria isso?

No caso do hibernate 4, está estável a implementação? Confiável?

Obrigado.

pirado18, exatamente isto, pode ter 100 usuários no tenant, mas todos terão que digitar o id do tenant o usuário e a senha no momento de logar.

Eu ainda não migrei para o hibernate 4, já fiz vários testes, não tive nenhuma falha ainda.

Hoje eu estou com o hibernate 3.6, utilizando interceptor, como mostrei nuns post acima, não tive nenhum problema até agora, estou a 12 meses operando desta forma

mizael86, migrei para o hibernate 4 e estou usando a sua solução para multi-tenancy, mas estou tendo problemas com o c3p0, ele não libera as conexões de maneira nenhuma.
Você sabe o que é?

Esta é a minha configuração

c3p0.acquireIncrement = 1 c3p0.autoCommitOnClose = false c3p0.minPoolSize = 3 c3p0.maxPoolSize = 50 c3p0.maxIdleTime = 10 c3p0.idleConnectionTestPeriod = 10