Multi-Tenancy with Spring + Hibernate: “SessionFactory configured for multi-tenancy, but no tenant identifier specified”

前端 未结 6 1532
一个人的身影
一个人的身影 2020-12-13 20:58

In a Spring 3 application, I\'m trying to implement multi-tenancy via Hibernate 4\'s native MultiTenantConnectionProvider and CurrentTenantIdentifierResolver. I see that the

相关标签:
6条回答
  • 2020-12-13 21:17

    Are you using @Transactional anywhere in your code (ie mark a service or dao class/method)?
    I was running into the same error until I commented out the @Transactional in my service class.
    I think it's related to the default openSessionInThread behavior of Hibernate 4.

    I also have hibernate configured without a custom implementation of the ConnectionProvider and TenantIdentifierResolver. I'm using the jndi-based approach, setting the hibernate.connection.datasource to java://comp/env/jdbc/, and then passing in the name of the jndi resource into my dao methods, which call

    sessionFactory.withOptions().tenantIdentifier(tenant).openSession();

    I'm still playing around to see if I can get a configuration working with @Transactional, but the jndi-based approach with the default session in thread behavior seems to be working now.

    0 讨论(0)
  • 2020-12-13 21:21

    As I explained in this article, Hibernate defines the CurrentTenantIdentifierResolver interface to help frameworks like Spring or Java EE to allow using the default Session instantiation mechanism (be it from an EntityManagerFactiry).

    So, the CurrentTenantIdentifierResolver must be set via a configuration property which is exactly where you went wrong because you didn't supply the right fully-qualified class name. The CurrentTenantIdentifierResolver implementation being CurrentTenantIdentifierResolverImpl, the hibernate.tenant_identifier_resolver has to be:

    <prop key="hibernate.tenant_identifier_resolver">com.afflatus.edu.thoth.context.CurrentTenantIdentifierResolverImpl</prop>
    

    After you fix this, when the HibernateTransactionManager calls getSessionFactory().openSession(), Hibernate will use the CurrentTenantIdentifierResolverImpl to resolve the tenant identifier.

    0 讨论(0)
  • 2020-12-13 21:29

    Perhaps you need to upgrade version of hibernate to last 4.X and use annotation or aspects to start transaction

    0 讨论(0)
  • 2020-12-13 21:38

    Even though this might be an older topic, and the answer might be already taken care of. What I noticed is the following:

    In your define the class CurrentTenantIdentifierResolverImpl:

    public class CurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver
    

    But in your config you reference MultiTenantIdentifierResolverImpl:

    <prop key="hibernate.tenant_identifier_resolver">com.afflatus.edu.thoth.context.MultiTenantIdentifierResolverImpl</prop>
    

    Just pointing this out because I did the same mistake today, after that it all worked like a charm.

    0 讨论(0)
  • 2020-12-13 21:40

    Foreward: Although I accepted this answer which (will) contains code, please upvote Darren's answer if you think this was useful. He's the reason I was able to solve this at all.

    Okay, so here we go....

    As Darren pointed out, this is really an issue with SessionFactory's instantiating a Session improperly. If you were to instantiate the session manually, you have no issue. eg:

    sessionFactory.withOptions().tenantIdentifier(tenant).openSession();
    

    However, the @Transactional annotation causes the SessionFactory to open a session with sessionFactory.getCurrentSession(), which does not pull the tenant identifier from the CurrentTenantIdentifierResolver.

    Darren suggested opening the Session manually in the DAO layer, but this means that each DAO method will have a locally scoped transaction. The better place to do this is on the service layer. Each service layer call (ie, doSomeLogicalTask()) may call multiple DAO methods. It makes sense that each of these should be bound to the same transaction, as they're logically related.

    Furthermore, I didn't like the idea of duplicating code in each service layer method to create and manage a transaction. Instead, I used AOP to wrap each method in my service layer with with the advice to instantiate a new Session and handle the transaction. The aspect stores the current Session in a TheadLocal stack which can be accessed by the DAO layer for querying.

    All of this work will allow the interfaces and implementations to stay identical to their bug-fixed counterparts, except one line in the DAO superclass that will get the Session from the ThreadLocal stack rather than the SessionFactory. This can be changed once the bug is fixed.

    I will post the code shortly, once I clean it up a little. If anybody sees any problems with this, do feel free to discuss below.

    0 讨论(0)
  • 2020-12-13 21:41

    I had a similar issue when my CurrentTenantIdentifierResolver implementation returned null for the resolveCurrentTenantIdentifier() method

    0 讨论(0)
提交回复
热议问题