I am trying to get Spring-Data JPA working with Hibernate with a custom MultiTenantConnectionProvider.
Everything in my configuration below seems to work. My MultiTenantConnectionProviderImpl
class gets called each time I try to call a Repository method.
The main problem is that there is no way to provide a tenant identifier. The Repository interfaces provided by Spring-Data take care of getting the Hibernate Session.
Is there any way to provide Spring-Data the tenant identifier? Or is there somewhere where we can intercept the creation of the Hibernate Session so we can appropriately call
sessionFactory.withOptions().tenantIdentifier(itendintifier).openSession();
Here is my Spring configuration XML file. I tried to keep it as bare-bones as I can.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.company"/>
<jpa:repositories base-package="com.company.repositories"/>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="packagesToScan" value="com.company.entities"/>
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true"/>
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServer2012Dialect</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.multi_tenant_connection_provider">com.company.hibernate.MultiTenantConnectionProviderImpl</prop>
<prop key="hibernate.multiTenancy">DATABASE</prop>
</props>
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--Vendor specific properties here-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver"/>
<property name="url" value="jdbc:jtds:sqlserver://localhost:1433/myDatabase"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
</beans>
Use CurrentTenantIdentifierResolver
:
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="hibernateProperties">
<map>
<entry key="hibernate.dialect" value="org.hibernate.dialect.SQLServer2012Dialect">
<entry key="hibernate.format_sql" value="true">
<entry key="hibernate.multi_tenant_connection_provider" value="com.company.hibernate.MultiTenantConnectionProviderImpl">
<entry key="hibernate.multiTenancy" value="DATABASE">
<!-- tenant resolver as spring bean -->
<entry key="hibernate.tenant_identifier_resolver" value-ref="currentTenantIdentifierResolver"/>
</map>
</property>
</bean>
<bean id="currentTenantIdentifierResolver"
class="com.xxx.CurrentTenantResolver">
</bean>
Simple tenant identifier resolver would be like this:
public class CurrentTenantResolver implements CurrentTenantIdentifierResolver {
public String resolveCurrentTenantIdentifier() {
// retrieve tenant from logged in user
User usr = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal() ;
return usr.getTenantName();
}
public boolean validateExistingCurrentSessions() {
return true;
}
}
Remember the above class is a spring bean, so you can autowired any spring bean(service/dao) just like regular spring bean.
Every time spring need session hibernate will retrieve the tenant identifier from that bean.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With