How to enable Multitenancy with Spring Data Jpa

Background : I am building a Multitenant SaaS app and have chosen single database, shared schema as the Multitenancy approach. Every table has a discriminator column "tenantId" to isolate the tenant data. I am using spring boot as the application framework and leveraging spring data jpa for the data layer with Hibernate as the JPA provider. I really like the way spring data helps in eliminating boilerplate code and have currently coded the respositories like below,

@Repository
public interface UserRepository extends JpaRepository<User,Long>{

}

and services like below,

public class UserService{
    @Autowired
    private UserRepository userRepo;
    public User getUser(){
        User user = userRepo.findOne(id);
    }
}

Problem Statement: When i want to get user, i want to get the user for a particular org. I am wondering how do i add the tenant criteria, i do not want to write custom repository implementations as that will introduce boilerplate code.

Solutions Attempted:

i) Hibernate Interceptor - onPrepareStatememt : This is not useful as the sql is a string and i do not want to do string manipulation.

ii)Enabling Hibernate Filters with Spring AOP: Annotated entity with @Filter and tried setting the filter in session. This does not work as the aspect is never invoked.

@AfterReturning(pointcut = "execution(* org.hibernate.jpa.internal.EntityManagerImpl.OpenSession(..))", returning = "session")
public void forceFilter(JoinPoint joinPoint, Object session) {

    Session hibernateSession = (Session) session;
    session.enableFilter("tenantFilter")
}

The hibernate filters sounds like a promising approach but i have kind of hit the wall trying to put in a working solution. I would like to know if there is an alternate approach to enable the hibernate filter in the session that Spring data uses internally to query the data.


Solution 1:

I have blogged about Multi-tenant applications using Spring Boot, JPA, Hibernate and Postgres and even though I took the DB per tenant approach, the DISCRIMINATOR (One or multiple table columns used to specify different tenants) approach you are using most-likely requires less configuration.

Take a look at the CurrentTenantIdentifierResolver implementation (TenantDvdRentalIdentifierResolverImpl.java), the DvdRentalMultiTenantInterceptor.java Spring MVC interceptor and DvdRentalTenantContext.java that uses the ThreadLocal to store / pass the tenantId.