I use MySQL on my production server, where every customer has its own database and its own database user to connect to his database. Each database has the exact same structure. Customers (and therefore databases) are added during the lifetime of the server.
Currently I establish a MySQL database connection with the class java.sql.DriverManager
, which works just fine. The databases url, user, password and driver name are stored in several properties-files. The servlet reads the properties-file from the current customer and creates the java.sql.Connection
with the java.sql.DriverManager
.
I really want to use the JPA, but as far as I know the JPA can only be used by explicitly specifing the databases in the configuration of the webcontainer (in my case Tomcat). But that means, when a new customer registers on my platform, I have to add the new database to the webcontainer-configuration and restart the webcontainer, which does not sound like a good idea.
Is there a way to use the JPA and still have the flexibility to add new databases during the lifetime of the webcontainer?
You can definitely do what you ask, but the demon is in the details.
I would call it JPA Multitenancy, and here is an interesting article provided by a random search, that proposes a valid method using CDI. I will start with a more generic approach, taking into account the fact that you also require runtime additions of new tenants.
Note also that Hibernate offers native solutions for multitenancy. I do not know which JPA provider you are using, but I would guess others have similar features.
It has to be done using separate, tenant-specific EntityManagerFactory
instances. The exact DB connection URL can be given by the map passed to Persistence.createEntityManagerFactory()
. E.g., given that META-INF/persistence.xml
exists, the following code applies:
HashMap props = new HashMap();
props.put("javax.persistence.jdbc.url", /* tenant-specific JDBC URL*/);
EntityManagerFactory tenantSpecificEntityManagerFactory =
Persistence.createEntityManagerFactory("name-of-persistence-unit-from-persistence.xml", props);
The props
generally override any properties specified in persistence.xml
. Probably overriding only the JDBC URL is enough, maybe you will need to override user/password or other things as well. From that point on, you can get the tenant-specific persistence context and work with it:
EntityManager tenantSpecificEm = tenantSpecificEntityManagerFactory.createEntityManager();
You could execute the code above every time you need a tenant-specific EntityManager
(and then close the factory as well). But it would be grossly inefficient for the following reasons:
EntityManagerFactory
anew with each request is slowTo address these issues you will need to:
EntityManagerFactory
instancesEntityManagerFactory
instancesI assume there is a robust mechanism to relate each request with the appropriate tenant. Additionally you will need an application-scoped Map
of tenant name to the corresponding EntityManagerFactory
instance. How this is stored and used depends on the application, e.g. is there any dependency injection framework in place? The first link has a solution with CDI, similar solutions would apply for other DI containers.
Application servers offer DB connection pooling. Tomcat does too. Application servers might allow you to add DB connection pools at runtime without the need to restart the server. I do not know if the version of Tomcat you are using supports it (I would guess not, but please correct me). So:
props
to use itEntityManagerFactory
. Hibernate at least seems to have this feature.I guess you already have figured this out, but the settings applied at runtime (tenants, tenant name to connection properties mappings) have to be persisted somehow, so that they are reapplied when the server restarts. This could be a configuration file or another "administration" database.