Note that there are some explanatory texts on larger screens.

plurals
  1. POMulti-Tenancy with Spring + Hibernate: "SessionFactory configured for multi-tenancy, but no tenant identifier specified"
    primarykey
    data
    text
    <p>In a Spring 3 application, I'm trying to implement multi-tenancy via Hibernate 4's native <a href="http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html/ch16.html#d5e4661">MultiTenantConnectionProvider</a> and <a href="http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html/ch16.html#d5e4686">CurrentTenantIdentifierResolver</a>. I see that <a href="https://hibernate.onjira.com/browse/HHH-7306">there was a problem with this in Hibernate 4.1.3</a>, but I'm running 4.1.9 and still getting a similar exception:</p> <pre><code> Caused by: org.hibernate.HibernateException: SessionFactory configured for multi-tenancy, but no tenant identifier specified at org.hibernate.internal.AbstractSessionImpl.&lt;init&gt;(AbstractSessionImpl.java:84) at org.hibernate.internal.SessionImpl.&lt;init&gt;(SessionImpl.java:239) at org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl.openSession(SessionFactoryImpl.java:1597) at org.hibernate.internal.SessionFactoryImpl.openSession(SessionFactoryImpl.java:963) at org.springframework.orm.hibernate4.HibernateTransactionManager.doBegin(HibernateTransactionManager.java:328) at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:371) at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:334) 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.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631) at com.afflatus.edu.thoth.repository.UserRepository$$EnhancerByCGLIB$$c844ce96.getAllUsers(&lt;generated&gt;) at com.afflatus.edu.thoth.service.UserService.getAllUsers(UserService.java:29) at com.afflatus.edu.thoth.HomeController.hello(HomeController.java:37) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:746) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:687) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:915) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:811) at javax.servlet.http.HttpServlet.service(HttpServlet.java:735) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:796) at javax.servlet.http.HttpServlet.service(HttpServlet.java:848) at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:671) at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:448) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:138) at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:564) at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:213) at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1070) at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:375) at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:175) at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1004) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:136) at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:258) at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:109) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97) at org.eclipse.jetty.server.Server.handle(Server.java:439) at org.eclipse.jetty.server.HttpChannel.run(HttpChannel.java:246) at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:265) at org.eclipse.jetty.io.AbstractConnection$ReadCallback.run(AbstractConnection.java:240) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:589) at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:520) at java.lang.Thread.run(Thread.java:722) enter code here </code></pre> <p>Below is the relevant code. In the <code>MultiTenantConnectionProvider</code> I've simply wrote some dumb code for now that just returns a new connection every time, and the <code>CurrentTenantIdentifierResolver</code> always returns the same ID at this point. Obviously this logic was to be implemented after I managed to get the connections to instantiate.</p> <h1>config.xml</h1> <pre><code>&lt;bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"&gt; &lt;property name="dataSource" ref="dataSource" /&gt; &lt;property name="packagesToScan"&gt; &lt;list&gt; &lt;value&gt;com.afflatus.edu.thoth.entity&lt;/value&gt; &lt;/list&gt; &lt;/property&gt; &lt;property name="hibernateProperties"&gt; &lt;props&gt; &lt;prop key="hibernate.dialect"&gt;${hibernate.dialect}&lt;/prop&gt; &lt;prop key="hibernate.show_sql"&gt;${hibernate.show_sql}&lt;/prop&gt; &lt;prop key="hibernate.hbm2ddl"&gt;${hibernate.dbm2ddl}&lt;/prop&gt; &lt;prop key="hibernate.multiTenancy"&gt;DATABASE&lt;/prop&gt; &lt;prop key="hibernate.multi_tenant_connection_provider"&gt;com.afflatus.edu.thoth.connection.MultiTenantConnectionProviderImpl&lt;/prop&gt; &lt;prop key="hibernate.tenant_identifier_resolver"&gt;com.afflatus.edu.thoth.context.MultiTenantIdentifierResolverImpl&lt;/prop&gt; &lt;/props&gt; &lt;/property&gt; &lt;/bean&gt; &lt;bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"&gt; &lt;property name="autodetectDataSource" value="false" /&gt; &lt;property name="sessionFactory" ref="sessionFactory" /&gt; &lt;/bean&gt; </code></pre> <h1>MultiTenantConnectionProvider.java</h1> <pre><code>package com.afflatus.edu.thoth.connection; import java.util.Properties; import java.util.HashMap; import java.util.Map; import org.hibernate.service.jdbc.connections.spi.AbstractMultiTenantConnectionProvider; import org.hibernate.service.jdbc.connections.spi.ConnectionProvider; import org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.hibernate.cfg.*; public class MultiTenantConnectionProviderImpl extends AbstractMultiTenantConnectionProvider { private final Map&lt;String, ConnectionProvider&gt; connectionProviders = new HashMap&lt;String, ConnectionProvider&gt;(); @Override protected ConnectionProvider getAnyConnectionProvider() { System.out.println("barfoo"); Properties properties = getConnectionProperties(); DriverManagerDataSource ds = new DriverManagerDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://127.0.0.1:3306/test"); ds.setUsername("root"); ds.setPassword(""); InjectedDataSourceConnectionProvider defaultProvider = new InjectedDataSourceConnectionProvider(); defaultProvider.setDataSource(ds); defaultProvider.configure(properties); return (ConnectionProvider) defaultProvider; } @Override protected ConnectionProvider selectConnectionProvider(String tenantIdentifier) { System.out.println("foobar"); Properties properties = getConnectionProperties(); DriverManagerDataSource ds = new DriverManagerDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://127.0.0.1:3306/test2"); ds.setUsername("root"); ds.setPassword(""); InjectedDataSourceConnectionProvider defaultProvider = new InjectedDataSourceConnectionProvider(); defaultProvider.setDataSource(ds); defaultProvider.configure(properties); return (ConnectionProvider) defaultProvider; } private Properties getConnectionProperties() { Properties properties = new Properties(); properties.put(AvailableSettings.DIALECT, "org.hibernate.dialect.MySQLDialect"); properties.put(AvailableSettings.DRIVER, "com.mysql.jdbc.Driver"); properties.put(AvailableSettings.URL, "jdbc:mysql://127.0.0.1:3306/test"); properties.put(AvailableSettings.USER, "root"); properties.put(AvailableSettings.PASS, ""); return properties; } } </code></pre> <h1>CurrentTenantIdentifierResolver.java</h1> <pre><code>package com.afflatus.edu.thoth.context; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; public class CurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver { public String resolveCurrentTenantIdentifier() { return "1"; } public boolean validateExistingCurrentSessions() { return true; } } </code></pre> <p>Can anybody see anything specifically wrong? This throws an exception as soon as a transaction is opened. It <em>seems</em> like the <code>SessionFactory</code> isn't opening the Session correctly, or the <code>Session</code> is simply ignoring the value returned by the <code>CurrentTenantIdentifierResolver</code>, which I believe was the issue in Hibernate 4.1.3; this was supposed to have been resolved.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload