SessionFactory configured for multi-tenancy, but no tenant identifier specified
Config:
* Spring 3.1.1.RELEASE
* Hibernate 4.1.0.FINAL
* Commons-DBCP 1.4
I'm trying to setup multi-tenancy with Hibernate 4.1 but it seems that SessionFactory isn't correctly configured by the transaction manager (or else).
Code:
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
p:packagesToScan="com.company"
>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLMyISAMDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.multiTenancy">DATABASE</prop>
<prop key="hibernate.tenant_identifier_resolver">com.mycompany.datasource.MultiTenantIdentifierResolver</prop>
<prop key="hibernate.multi_tenant_connection_provider">com.mycompany.datasource.MultiTenantConnectionProvider</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager" p:autodetectDataSource="false" p:sessionFactory-ref="sessionFactory"/>
* I removed the p:dataSource-ref from LocalSessionFactoryBena because if I read correctly the documentation, it overrides the other settings.
* Because of that, I also had to put "p:autodetectDataSource="false" in HibernateTransactionManager otherwise it tries to get the datasource & generates a NullPointerException
* I finally removed the hibernate.current_session_context_class (previous set up as "jta") because it doesn't seem to change anything anymore with HibernateTransactionManager of hibernate4 package.
So I have a simple MultiTenantConnectionProvider:
Code:
package com.mycompany.datasource;
public class MultiTenantConnectionProvider extends org.hibernate.service.jdbc.connections.spi.AbstractMultiTenantConnectionProvider {
private static final long serialVersionUID = 1L;
@Override
public org.hibernate.service.jdbc.connections.spi.ConnectionProvider getAnyConnectionProvider() {
return MultiTenantConnectionHolder.getAnyConnectionProvider();
}
@Override
public org.hibernate.service.jdbc.connections.spi.ConnectionProvider selectConnectionProvider(String tenantIdentifier) {
return MultiTenantConnectionHolder.getConnectionProvider(tenantIdentifier);
}
}
Whatever the details of MultiTenantConnectionHolder...
Then I have my TenantIdentifierResolver implementation which seems to be loaded correctly:
Code:
package com.mycompany.datasource;
public class MultiTenantIdentifierResolver implements org.hibernate.context.spi.CurrentTenantIdentifierResolver {
@Autowired
UserDetailsService userService;
public String resolveCurrentTenantIdentifier() {
return userService.getCurrentlyAuthenticatedUser().getTenantId();
}
public boolean validateExistingCurrentSessions() {
return true;
}
}
Then I have a webservice endpoint which is transactional
Code:
@Transactional @ResponsePayload @PayloadRoot(localPart = "ActionRequest", namespace = NAMESPACE_URI)
public ActionResponse handleAWSRequest(@RequestPayload ActionRequest request) throws ActionRequestException {
...
}
With all that, I get the exception "SessionFactory configured for multi-tenancy, but no tenant identifier specified".
* I can see with AOP that the MultiTenantIdentifierResolver isn't called.
* With the following stack trace, I can see that we are in a transaction context (HibernateTransactionManager.doBegin) but it can't pass the tenantIdentifier...
Code:
org.hibernate.HibernateException: SessionFactory configured for multi-tenancy, but no tenant identifier specified
at org.hibernate.internal.AbstractSessionImpl.<init>(AbstractSessionImpl.java:82)
at org.hibernate.internal.SessionImpl.<init>(SessionImpl.java:226)
at org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl.openSession(SessionFactoryImpl.java:1801)
at org.hibernate.internal.SessionFactoryImpl.openSession(SessionFactoryImpl.java:1009)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:191)
at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:176)
at org.springframework.orm.hibernate4.SessionFactoryUtils.openSession(SessionFactoryUtils.java:114)
at org.springframework.orm.hibernate4.HibernateTransactionManager.doBegin(HibernateTransactionManager.java:340)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:371)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:335)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:105)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at com.mycompany.webservice.endpoint.AWSEndPoint$$EnhancerByCGLIB$$f46141a4.handleAWSRequest(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.ws.server.endpoint.MethodEndpoint.invoke(MethodEndpoint.java:132)
at org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter.invokeInternal(DefaultMethodEndpointAdapter.java:229)
at org.springframework.ws.server.endpoint.adapter.AbstractMethodEndpointAdapter.invoke(AbstractMethodEndpointAdapter.java:53)
at org.springframework.ws.server.MessageDispatcher.dispatch(MessageDispatcher.java:231)
at org.springframework.ws.server.MessageDispatcher.receive(MessageDispatcher.java:172)
at org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection(WebServiceMessageReceiverObjectSupport.java:88)
at org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.handle(WebServiceMessageReceiverHandlerAdapter.java:59)
......
Searching for Hibernate implementation, I found in hibernate *SessionContext (JTA or Thread) the buildOrObtainSession() which calls baseSessionBuilder() which calls the tenantIdentifier, but obviously it's not going through this code. Probably because antother implementation is called...
org.hibernate.context.spi.AbstractCurrentSessionCo ntext
Code:
protected SessionBuilder baseSessionBuilder() {
final SessionBuilder builder = factory.withOptions();
final CurrentTenantIdentifierResolver resolver = factory.getCurrentTenantIdentifierResolver();
if ( resolver != null ) {
builder.tenantIdentifier( resolver.resolveCurrentTenantIdentifier() );
}
return builder;
}
I'm quite lost with this...
I feel like the usage of HibernateTransactionManager isn't using the Hibernate classes that supports the multi-tenancy configuration OR that it doesn't configure the sessionFactory with all parameters (TenantIdentifierResolver is this case)...
Any help?