15

Is it possible to configure Spring Boot to use a MultiTenantConnectionProvider so that each client of my system connects to their own private database?

Specifically I am looking to use the built-in hibernate support for multi-tenancy:

And this is an example of the sort of config I am after, but I can't figure out how to use this in a Spring Boot setup:

I've tried adding these properties to application.properties:

spring.jpa.hibernate.multiTenancy=DATABASE
spring.jpa.hibernate.tenant_identifier_resolver=com.mystuff.MyCurrentTenantIdentifierResolver
spring.jpa.hibernate.multi_tenant_connection_provider=com.mystuff.MyMultiTenantConnectionProviderImplX

I've also tried coding up my own CurrentTenantIdentifierResolver and MultiTenantConnectionProvider and tried serving these up from my main @Configuration bean:

@Bean
public CurrentTenantIdentifierResolver currentTenantIdentifierResolver() {
    return new CurrentTenantIdentifierResolver() {
        public String resolveCurrentTenantIdentifier() {
            // this is never called ...
        }
        public boolean validateExistingCurrentSessions() {
            // this is never called ...
        }
    };
}

@Bean
public MultiTenantConnectionProvider multiTenantConnectionProvider() {
    return new AbstractMultiTenantConnectionProvider() {
        protected ConnectionProvider getAnyConnectionProvider() {
            // this is never called ...
        }
        protected ConnectionProvider selectConnectionProvider(String s) {
            // this is never called ...
        }
    };
}

None of this seems to have any affect so my question is really how to get spring-boot / spring-data to use these multi-tenant classes?

Thanks for your help!

Community
  • 1
  • 1
zonski
  • 335
  • 2
  • 3
  • 7
  • See my question [here](https://stackoverflow.com/questions/44366221/hibernate-multitenancy-with-spring-jpa).
    I use a `LocalContainerEntityManagerFactoryBean` and it works.
    But I'm not entirely understand the difference between this and `.yml`or`.properties`.
    I agree with @M.Deinum , in `yml` hibernate controls the lifecycle of `mulitTenantConnectionProvider` and `CurrentTenantIdentifierResolver`.But I don't know why.
    – linghu Jun 06 '17 at 03:10

1 Answers1

10

Any property for JPA/Hibernate that isn't defined can be set using the spring.jpa.properties property in the application.properties.

The sample you link to has 3 properties for multitenancy:

<prop key="hibernate.multiTenancy">SCHEMA</prop>
<prop key="hibernate.tenant_identifier_resolver">com.webapp.persistence.utility.CurrentTenantContextIdentifierResolver</prop>
<prop key="hibernate.multi_tenant_connection_provider">com.webapp.persistence.utility.MultiTenantContextConnectionProvider</prop>

That converted to Spring Boot would be the following properties in the application.properties file.

spring.jpa.properties.hibernate.multiTenancy=SCHEMA
spring.jpa.properties.hibernate.tenant_identifier_resolver=com.mystuff.MyCurrentTenantIdentifierResolver
spring.jpa.properties.hibernate.multi_tenant_connection_provider=com.webapp.persistence.utility.MultiTenantContextConnectionProvider

For your situation (as stated in your question).

spring.jpa.properties.hibernate.multiTenancy=DATABASE
spring.jpa.properties.hibernate.tenant_identifier_resolver=com.webapp.persistence.utility.CurrentTenantContextIdentifierResolver 
spring.jpa.properties.hibernate.multi_tenant_connection_provider=com.mystuff.MyMultiTenantConnectionProviderImplX

It will not work with Spring manged beans as hibernate controls the lifecycle of those instances.

For more properties see the the Spring Boot reference guide.

Glenn
  • 8,932
  • 2
  • 41
  • 54
M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • 1
    Thanks, I missed the '.properties.' bit (the docco is clear in hindsight but I did miss it the first few times I read it). Bit of a challenge now around the lifecycle stuff as you highlighted. Specifically getting access to other Spring services from within the ConnectionProvider. I've tried the approach of keeping a static reference to the service I need but the hibernate stuff gets created first so it's null when I need it. Fallback is to just not use Spring services in these classes but it makes the code messy. If you have any ideas on that side of it, I'd love to hear them. – zonski Nov 14 '14 at 09:38
  • 1
    ALthough a bit hacky you could use `ContextLoader.getCurrentWebApplicationContext` to gain access to the context and do the lookups. Or create a helper class which you can use to retrieve the services. – M. Deinum Nov 14 '14 at 09:42
  • 1
    Yea, thanks - I tried both of those but whatever Spring Boot is doing causes the MultiTenantConnectionProvider to get instantiated before the context exists so ContextLoader. getCurrentWebApplicationContext() is null when getAnyConnectionProvider() is first called, and helper classes can't find the services at this point either. I think I'm stuck with just not being able to use Spring services from within the MultiTenantConnectionProvider. Shame, but not the end of the world. Thanks heaps for your help, really appreciate it! – zonski Nov 14 '14 at 22:52
  • Also trying to move from single-tenant to mult-tenant with database per tenant strategy using `Spring Data JPA` and it looks really unsupported with `@Service` instances and `@Transactional` methods ... – Yuriy Nakonechnyy May 08 '15 at 10:22
  • No it isn't has nothing to do with that. – M. Deinum May 08 '15 at 10:23
  • Actually it is possible to manage the life cycle of instances of these two Hibernate classes with Spring and inject them on EntityManager. Check my answer: http://stackoverflow.com/a/31825971/710780 – mhnagaoka Aug 06 '15 at 12:22