To understand how tenants work in Spring, it is important to first understand what multi tenancy is and how it is implemented in JPA. Check the following link for more information:
As I said in part 1, Hibernate/JPA knows the current tenant through CurrentTenantIdentifierResolver which is resolved by Spring. So the real question here is “How does Spring know what tenants exist?” The answer to this question has a lot of different ways to be implemented, that’s why it’s difficult to find a good reference.
In our case, before implementing it, we will set the basis to know how tenants are defined:
- Each tenant has a property file that describe its values for a dataSource in the form: database.<tenantId>.properties . We will call it a tenant-file in this post.
- Each <tenantId> written in a filename is what identifies a tenantId. In this way we don’t need to duplicate the information, if you need tenantX just create a filename like database.tenantX.properties
- There is a Map<String, DataSource>, the key will be the tenantId string, this map will be initialized by getting the properties from the tenant-files.
- There is a default datasource called defaultDatasource, it contains default properties so if a property is not specified in the tenant-file, we will get it from the default datasource. In this way, we can set an collection of properties as default and avoid property duplication.
As you can see from above, tenants are acquired from a tenant-file. Now let’s continue with our implementation.
OUR IMPLEMENTATION IN SPRING TO GET TENANTS
DataSourceLookup is a Spring interface which allows us to look up DataSources by name, we chose it since we want to find DataSource by TenantId. I found in the javadoc from Spring a class named MapDataSourceLookup which has a Map<String,DataSource> and implements DataSourceLookup.
I decided to implement my own class so I could get the tenants from the properties file in a folder:
As there are comments in the code already I will focus just on the important parts.
Firstly, we have some constants:
- tenantDbConfigs: The directory used in development where tenant-files are located inside a folder of the project
- tenantDbConfigsOverride: The directory in production where tenant-files are located outside the project. In this way if the application is redeployed it’s not deleted.
- tenantRegex: Regular expression that we use to iterate about different tenant-files and get the tenantId.
It is in the constructor where we initialize the datasources, the method initializeDataSources(DataSource defaultDataSource) will put the defaultDataSource for the default tenant in the map firstly and then the others.
All the magic to get the tenants happens in addTenantDataSources(DataSource defaultDatasource, String folder), basically what we do is to use a regular expression to get the properties from each tenant in a folder, we also get the tenantId from the filename and override the DefaultDataSource properties with the ones in the property file of each tenantId. Finally we add the entry to the map as <tenantId, DataSource>.
In this class we have 2 different processes:
- Add the datasources for each tenant. These datasources will be used for Hibernate.
- Get the properties for each tenant
The ideal should be to separate these processes: “Add datasources” and “get tenant properties”, this can be done creating a new singleton class PropertyManager and use it in our MultiTenantDataSourceLookup. In this way, you could have every access to any property centralized in the PropertyManager. But for the sake of shortening and understanding I decided to post it in this way.
In future posts we will talk about:
- Hibernate schema export in multi-tenancy
- Different ways to save the tenant id in an http session
I hope that you find the article informative, if you have any questions please don’t hesitate to ask.