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).
* I removed the p:dataSource-ref from LocalSessionFactoryBena because if I read correctly the documentation, it overrides the other settings.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"/>
* 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:
Whatever the details of MultiTenantConnectionHolder...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); } }
Then I have my TenantIdentifierResolver implementation which seems to be loaded correctly:
Then I have a webservice endpoint which is transactionalCode: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; } }
With all that, I get the exception "SessionFactory configured for multi-tenancy, but no tenant identifier specified".Code:@Transactional @ResponsePayload @PayloadRoot(localPart = "ActionRequest", namespace = NAMESPACE_URI) public ActionResponse handleAWSRequest(@RequestPayload ActionRequest request) throws ActionRequestException { ... }
* 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...
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...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) ......
org.hibernate.context.spi.AbstractCurrentSessionCo ntext
I'm quite lost with this...Code:protected SessionBuilder baseSessionBuilder() { final SessionBuilder builder = factory.withOptions(); final CurrentTenantIdentifierResolver resolver = factory.getCurrentTenantIdentifierResolver(); if ( resolver != null ) { builder.tenantIdentifier( resolver.resolveCurrentTenantIdentifier() ); } return builder; }
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?


Reply With Quote
Fixed in Hibernate, no change required in Spring!
