diff --git a/src/main/java/io/ebean/config/ServerConfig.java b/src/main/java/io/ebean/config/ServerConfig.java index 3522131f1..c36612208 100644 --- a/src/main/java/io/ebean/config/ServerConfig.java +++ b/src/main/java/io/ebean/config/ServerConfig.java @@ -129,6 +129,8 @@ public class ServerConfig { private TenantSchemaProvider tenantSchemaProvider; + private TenantCatalogProvider tenantCatalogProvider; + /** * List of interesting classes such as entities, embedded, ScalarTypes, * Listeners, Finders, Controllers etc. @@ -696,6 +698,20 @@ public class ServerConfig { this.tenantSchemaProvider = tenantSchemaProvider; } + /** + * Return the tenancy catalog provider. + */ + public TenantCatalogProvider getTenantCatalogProvider() { + return tenantCatalogProvider; + } + + /** + * Set the tenancy catalog provider. + */ + public void setTenantCatalogProvider(TenantCatalogProvider tenantCatalogProvider) { + this.tenantCatalogProvider = tenantCatalogProvider; + } + /** * Return the PersistBatch mode to use by default at the transaction level. *
diff --git a/src/main/java/io/ebean/config/TenantCatalogProvider.java b/src/main/java/io/ebean/config/TenantCatalogProvider.java
new file mode 100644
index 000000000..d8e8d1e0a
--- /dev/null
+++ b/src/main/java/io/ebean/config/TenantCatalogProvider.java
@@ -0,0 +1,16 @@
+package io.ebean.config;
+
+/**
+ * For multi-tenancy via DB CATALOG supply the catalog given the tenantId.
+ */
+@FunctionalInterface
+public interface TenantCatalogProvider {
+
+ /**
+ * Return the DB catalog for the given tenantId.
+ *
+ * @param tenantId The current tenant id.
+ * @return The DB catalog to use for the given tenant
+ */
+ String catalog(Object tenantId);
+}
diff --git a/src/main/java/io/ebean/config/TenantMode.java b/src/main/java/io/ebean/config/TenantMode.java
index 808d93044..1205fca58 100644
--- a/src/main/java/io/ebean/config/TenantMode.java
+++ b/src/main/java/io/ebean/config/TenantMode.java
@@ -20,6 +20,11 @@ public enum TenantMode {
*/
SCHEMA(true),
+ /**
+ * Each Tenant has their own Database but with in connection pool
+ */
+ CATALOG(true),
+
/**
* Tenants share tables but have a discriminator/partition column that partitions the data.
*/
diff --git a/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java b/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java
index ddad6db12..4f6c3b2f5 100644
--- a/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java
+++ b/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java
@@ -371,6 +371,8 @@ public class InternalConfiguration {
return new MultiTenantDbSupplier(serverConfig.getCurrentTenantProvider(), serverConfig.getTenantDataSourceProvider());
case SCHEMA:
return new MultiTenantDbSchemaSupplier(serverConfig.getCurrentTenantProvider(), serverConfig.getDataSource(), serverConfig.getTenantSchemaProvider());
+ case CATALOG:
+ return new MultiTenantDbCatalogSupplier(serverConfig.getCurrentTenantProvider(), serverConfig.getDataSource(), serverConfig.getTenantCatalogProvider());
default:
return new SimpleDataSourceProvider(serverConfig.getDataSource());
}
diff --git a/src/main/java/io/ebeaninternal/server/core/MultiTenantDbCatalogSupplier.java b/src/main/java/io/ebeaninternal/server/core/MultiTenantDbCatalogSupplier.java
new file mode 100644
index 000000000..3caf7dbc3
--- /dev/null
+++ b/src/main/java/io/ebeaninternal/server/core/MultiTenantDbCatalogSupplier.java
@@ -0,0 +1,125 @@
+package io.ebeaninternal.server.core;
+
+import io.ebean.config.CurrentTenantProvider;
+import io.ebean.config.TenantCatalogProvider;
+import io.ebeaninternal.server.transaction.DataSourceSupplier;
+import org.avaje.datasource.DataSourcePool;
+
+import javax.sql.DataSource;
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.logging.Logger;
+
+/**
+ * DataSource supplier that changes DB catalog based on current Tenant Id.
+ */
+public class MultiTenantDbCatalogSupplier implements DataSourceSupplier {
+
+ private final CurrentTenantProvider tenantProvider;
+
+ private final DataSource dataSource;
+
+ private final TenantCatalogProvider catalogProvider;
+
+ private final CatalogDataSource catalogDataSource;
+
+ MultiTenantDbCatalogSupplier(CurrentTenantProvider tenantProvider, DataSource dataSource, TenantCatalogProvider catalogProvider) {
+ this.tenantProvider = tenantProvider;
+ this.dataSource = dataSource;
+ this.catalogProvider = catalogProvider;
+ this.catalogDataSource = new CatalogDataSource();
+ }
+
+ @Override
+ public DataSource getDataSource() {
+ return catalogDataSource;
+ }
+
+ @Override
+ public Connection getConnection(Object tenantId) throws SQLException {
+ return catalogDataSource.getConnectionForTenant(tenantId);
+ }
+
+ @Override
+ public void shutdown(boolean deregisterDriver) {
+ if (dataSource instanceof DataSourcePool) {
+ ((DataSourcePool) dataSource).shutdown(deregisterDriver);
+ }
+ }
+
+ /**
+ * Returns the DB catalog for the current user Tenant Id.
+ */
+ private String tenantCatalog() {
+ return catalogProvider.catalog(tenantProvider.currentId());
+ }
+
+ private class CatalogDataSource implements DataSource {
+
+ CatalogDataSource() {
+ }
+
+ /**
+ * Return the connection where tenantId is optionally provided by a lazy loading query.
+ */
+ Connection getConnectionForTenant(Object tenantId) throws SQLException {
+ Connection connection = dataSource.getConnection();
+ connection.setCatalog(catalogProvider.catalog(tenantId));
+ return connection;
+ }
+
+ /**
+ * Return the connection with the appropriate DB catalog set.
+ */
+ @Override
+ public Connection getConnection() throws SQLException {
+
+ Connection connection = dataSource.getConnection();
+ connection.setCatalog(tenantCatalog());
+ return connection;
+ }
+
+ @Override
+ public Connection getConnection(String username, String password) throws SQLException {
+ return dataSource.getConnection(username, password);
+ }
+
+ @Override
+ public