Im trying to setup my application for MultiTenancy.
My persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="3.0"
xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd">
<persistence-unit name="primary">
<jta-data-source>java:jboss/datasources/myapp</jta-data-source>
<properties>
<!-- Properties for Hibernate -->
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.jdbc.batch_size" value="100000000" />
<property name="hibernate.dialect"
value="org.hibernate.dialect.MySQL8Dialect" />
<!-- Multi-Tenancy Konfiguration -->
<property name="hibernate.multiTenancy" value="SCHEMA" />
<property name="hibernate.tenant_identifier_resolver"
value="my.app.TenantIdentifierResolver" />
<property
name="hibernate.multi_tenant_connection_provider_class"
value="my.app.TenantSchemaMultiTenantConnectionProvider" />
</properties>
</persistence-unit>
</persistence>
TenantContext.class:
public class TenantContext {
private static final ThreadLocal<String> currentTenant = new ThreadLocal<>();
public static final String DEFAULT_TENANT_ID = "myapp";
//////////////////////////////
//////// Functions
//////////////////////////////
public static void clear() {
currentTenant.remove();
}
//////////////////////////////
//////// Setter + Getter
//////////////////////////////
public static void setCurrentTenant(String tenant) {
currentTenant.set(tenant);
}
public static String getCurrentTenant() {
String result = currentTenant.get();
return result;
}
TenantIdentifierResolver.class:
public class TenantIdentifierResolver implements CurrentTenantIdentifierResolver {
@Override
public String resolveCurrentTenantIdentifier() {
String tenantId = TenantContext.getCurrentTenant();
if (tenantId == null) {
return null;
}
return tenantId;
}
@Override
public boolean validateExistingCurrentSessions() {
return true;
}
TenantSchemaMultiTenantConnectionProvider.class:
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl;
import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
import org.hibernate.engine.jdbc.connections.spi.AbstractMultiTenantConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.service.spi.ServiceRegistryAwareService;
import org.hibernate.service.spi.ServiceRegistryImplementor;
public class TenantSchemaMultiTenantConnectionProvider extends AbstractMultiTenantConnectionProvider {
private final Map<String, DriverManagerConnectionProviderImpl> connectionProviders = new HashMap<>();
private ConnectionProvider connectionProvider = null;
private final String baseUrl = "jdbc:mysql://localhost:3306/";
private final String username = "root";
private final String password = "test";
private DriverManagerConnectionProviderImpl createConnectionProvider(String tenantIdentifier) {
DriverManagerConnectionProviderImpl connectionProvider = new DriverManagerConnectionProviderImpl();
Map<String, Object> config = new HashMap<>();
config.put("hibernate.connection.url",
"jdbc:mysql://localhost:3306/" + tenantIdentifier + "?serverTimezone=Europe/Berlin&useSSL=false");
config.put("hibernate.connection.username", "root");
config.put("hibernate.connection.password", "test");
// Konfigurieren Sie den ConnectionProvider direkt
connectionProvider.configure(config);
return connectionProvider;
}
@Override
public Connection getAnyConnection() throws SQLException {
return DriverManager.getConnection(baseUrl, username, password);
}
@Override
public void releaseAnyConnection(Connection connection) throws SQLException {
if (connection != null) {
connection.close();
}
}
@Override
public Connection getConnection(Object cTenantIdentifier) throws SQLException {
String tenantIdentifier = (String) cTenantIdentifier;
// Hier wird die Verbindung für das spezifische Schema erstellt
String tenantDbUrl = baseUrl + tenantIdentifier + "?serverTimezone=Europe/Berlin&useSSL=false";
return DriverManager.getConnection(tenantDbUrl, username, password);
}
@Override
protected ConnectionProvider getAnyConnectionProvider() {
// TODO Auto-generated method stub
return null;
}
@Override
protected ConnectionProvider selectConnectionProvider(Object tenantIdentifier) {
// TODO Auto-generated method stub
return null;
}
}
I´m trying to setup my application for MultiTenancy.
My persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="3.0"
xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd">
<persistence-unit name="primary">
<jta-data-source>java:jboss/datasources/myapp</jta-data-source>
<properties>
<!-- Properties for Hibernate -->
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.jdbc.batch_size" value="100000000" />
<property name="hibernate.dialect"
value="org.hibernate.dialect.MySQL8Dialect" />
<!-- Multi-Tenancy Konfiguration -->
<property name="hibernate.multiTenancy" value="SCHEMA" />
<property name="hibernate.tenant_identifier_resolver"
value="my.app.TenantIdentifierResolver" />
<property
name="hibernate.multi_tenant_connection_provider_class"
value="my.app.TenantSchemaMultiTenantConnectionProvider" />
</properties>
</persistence-unit>
</persistence>
TenantContext.class:
public class TenantContext {
private static final ThreadLocal<String> currentTenant = new ThreadLocal<>();
public static final String DEFAULT_TENANT_ID = "myapp";
//////////////////////////////
//////// Functions
//////////////////////////////
public static void clear() {
currentTenant.remove();
}
//////////////////////////////
//////// Setter + Getter
//////////////////////////////
public static void setCurrentTenant(String tenant) {
currentTenant.set(tenant);
}
public static String getCurrentTenant() {
String result = currentTenant.get();
return result;
}
}
TenantIdentifierResolver.class:
public class TenantIdentifierResolver implements CurrentTenantIdentifierResolver {
@Override
public String resolveCurrentTenantIdentifier() {
String tenantId = TenantContext.getCurrentTenant();
if (tenantId == null) {
return null;
}
return tenantId;
}
@Override
public boolean validateExistingCurrentSessions() {
return true;
}
}
TenantSchemaMultiTenantConnectionProvider.class:
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl;
import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
import org.hibernate.engine.jdbc.connections.spi.AbstractMultiTenantConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.service.spi.ServiceRegistryAwareService;
import org.hibernate.service.spi.ServiceRegistryImplementor;
public class TenantSchemaMultiTenantConnectionProvider extends AbstractMultiTenantConnectionProvider {
private final Map<String, DriverManagerConnectionProviderImpl> connectionProviders = new HashMap<>();
private ConnectionProvider connectionProvider = null;
private final String baseUrl = "jdbc:mysql://localhost:3306/";
private final String username = "root";
private final String password = "test";
private DriverManagerConnectionProviderImpl createConnectionProvider(String tenantIdentifier) {
DriverManagerConnectionProviderImpl connectionProvider = new DriverManagerConnectionProviderImpl();
Map<String, Object> config = new HashMap<>();
config.put("hibernate.connection.url",
"jdbc:mysql://localhost:3306/" + tenantIdentifier + "?serverTimezone=Europe/Berlin&useSSL=false");
config.put("hibernate.connection.username", "root");
config.put("hibernate.connection.password", "test");
// Konfigurieren Sie den ConnectionProvider direkt
connectionProvider.configure(config);
return connectionProvider;
}
@Override
public Connection getAnyConnection() throws SQLException {
return DriverManager.getConnection(baseUrl, username, password);
}
@Override
public void releaseAnyConnection(Connection connection) throws SQLException {
if (connection != null) {
connection.close();
}
}
@Override
public Connection getConnection(Object cTenantIdentifier) throws SQLException {
String tenantIdentifier = (String) cTenantIdentifier;
// Hier wird die Verbindung für das spezifische Schema erstellt
String tenantDbUrl = baseUrl + tenantIdentifier + "?serverTimezone=Europe/Berlin&useSSL=false";
return DriverManager.getConnection(tenantDbUrl, username, password);
}
@Override
protected ConnectionProvider getAnyConnectionProvider() {
// TODO Auto-generated method stub
return null;
}
@Override
protected ConnectionProvider selectConnectionProvider(Object tenantIdentifier) {
// TODO Auto-generated method stub
return null;
}
}
In my Service class:
@Stateless
public class BranchServiceBean{
@PersistenceContext
private EntityManager entityManager;
public void addBranch(Branch branch) {
TenantContext.setCurrentTenant("your_schema");
entityManager.persist(branch);
}
I´m using Wildfly and I adjust in standalone.xml also this part:
<datasource jta="true" jndi-name="java:jboss/datasources/myapp" pool-name="gixxoffice" enabled="true" use-java-context="true" use-ccm="true">
<connection-url>jdbc:mysql://localhost:3306/?serverTimezone=Europe/Berlin&useSSL=false&allowPublicKeyRetrieval=true</connection-url>
<driver-class>com.mysql.cj.jdbc.Driver</driver-class>
<driver>mysql</driver>
<security user-name="root" password="test"/>
<validation>
<validate-on-match>false</validate-on-match>
<background-validation>false</background-validation>
</validation>
<statement>
<share-prepared-statements>false</share-prepared-statements>
</statement>
</datasource>
My error message is:
JDBC exception executing SQL [select b1_0.branch_id,b1_0.active_date,b1_0.create_date,b1_0.delete_date,b1_0.delete_flag,b1_0.id_hash,b1_0.inactive_date,b1_0.parent_branch_fk,b1_0.reactivate_date,b1_0.status,b1_0.tree_level,b1_0.unique_name,b1_0.update_date from branch b1_0 where b1_0.unique_name=? and b1_0.delete_flag=?] [No database selected] [n/a]
0
I´m trying to setup my application for MultiTenancy.
My persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="3.0"
xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd">
<persistence-unit name="primary">
<jta-data-source>java:jboss/datasources/myapp</jta-data-source>
<properties>
<!-- Properties for Hibernate -->
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.jdbc.batch_size" value="100000000" />
<property name="hibernate.dialect"
value="org.hibernate.dialect.MySQL8Dialect" />
<!-- Multi-Tenancy Konfiguration -->
<property name="hibernate.multiTenancy" value="SCHEMA" />
<property name="hibernate.tenant_identifier_resolver"
value="my.app.TenantIdentifierResolver" />
<property
name="hibernate.multi_tenant_connection_provider_class"
value="my.app.TenantSchemaMultiTenantConnectionProvider" />
</properties>
</persistence-unit>
</persistence>
TenantContext.class:
public class TenantContext {
private static final ThreadLocal<String> currentTenant = new ThreadLocal<>();
public static final String DEFAULT_TENANT_ID = "myapp";
//////////////////////////////
//////// Functions
//////////////////////////////
public static void clear() {
currentTenant.remove();
}
//////////////////////////////
//////// Setter + Getter
//////////////////////////////
public static void setCurrentTenant(String tenant) {
currentTenant.set(tenant);
}
public static String getCurrentTenant() {
String result = currentTenant.get();
return result;
}
}
TenantIdentifierResolver.class:
public class TenantIdentifierResolver implements CurrentTenantIdentifierResolver {
@Override
public String resolveCurrentTenantIdentifier() {
String tenantId = TenantContext.getCurrentTenant();
if (tenantId == null) {
return null;
}
return tenantId;
}
@Override
public boolean validateExistingCurrentSessions() {
return true;
}
}
TenantSchemaMultiTenantConnectionProvider.class:
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl;
import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
import org.hibernate.engine.jdbc.connections.spi.AbstractMultiTenantConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.service.spi.ServiceRegistryAwareService;
import org.hibernate.service.spi.ServiceRegistryImplementor;
public class TenantSchemaMultiTenantConnectionProvider extends AbstractMultiTenantConnectionProvider {
private final Map<String, DriverManagerConnectionProviderImpl> connectionProviders = new HashMap<>();
private ConnectionProvider connectionProvider = null;
private final String baseUrl = "jdbc:mysql://localhost:3306/";
private final String username = "root";
private final String password = "test";
private DriverManagerConnectionProviderImpl createConnectionProvider(String tenantIdentifier) {
DriverManagerConnectionProviderImpl connectionProvider = new DriverManagerConnectionProviderImpl();
Map<String, Object> config = new HashMap<>();
config.put("hibernate.connection.url",
"jdbc:mysql://localhost:3306/" + tenantIdentifier + "?serverTimezone=Europe/Berlin&useSSL=false");
config.put("hibernate.connection.username", "root");
config.put("hibernate.connection.password", "test");
// Konfigurieren Sie den ConnectionProvider direkt
connectionProvider.configure(config);
return connectionProvider;
}
@Override
public Connection getAnyConnection() throws SQLException {
return DriverManager.getConnection(baseUrl, username, password);
}
@Override
public void releaseAnyConnection(Connection connection) throws SQLException {
if (connection != null) {
connection.close();
}
}
@Override
public Connection getConnection(Object cTenantIdentifier) throws SQLException {
String tenantIdentifier = (String) cTenantIdentifier;
// Hier wird die Verbindung für das spezifische Schema erstellt
String tenantDbUrl = baseUrl + tenantIdentifier + "?serverTimezone=Europe/Berlin&useSSL=false";
return DriverManager.getConnection(tenantDbUrl, username, password);
}
@Override
protected ConnectionProvider getAnyConnectionProvider() {
// TODO Auto-generated method stub
return null;
}
@Override
protected ConnectionProvider selectConnectionProvider(Object tenantIdentifier) {
// TODO Auto-generated method stub
return null;
}
}
In my Service class:
@Stateless
public class BranchServiceBean{
@PersistenceContext
private EntityManager entityManager;
public void addBranch(Branch branch) {
TenantContext.setCurrentTenant("your_schema");
entityManager.persist(branch);
}
I´m using Wildfly and I adjust in standalone.xml also this part:
<datasource jta="true" jndi-name="java:jboss/datasources/myapp" pool-name="gixxoffice" enabled="true" use-java-context="true" use-ccm="true">
<connection-url>jdbc:mysql://localhost:3306/?serverTimezone=Europe/Berlin&useSSL=false&allowPublicKeyRetrieval=true</connection-url>
<driver-class>com.mysql.cj.jdbc.Driver</driver-class>
<driver>mysql</driver>
<security user-name="root" password="test"/>
<validation>
<validate-on-match>false</validate-on-match>
<background-validation>false</background-validation>
</validation>
<statement>
<share-prepared-statements>false</share-prepared-statements>
</statement>
</datasource>
My error message is:
JDBC exception executing SQL [select b1_0.branch_id,b1_0.active_date,b1_0.create_date,b1_0.delete_date,b1_0.delete_flag,b1_0.id_hash,b1_0.inactive_date,b1_0.parent_branch_fk,b1_0.reactivate_date,b1_0.status,b1_0.tree_level,b1_0.unique_name,b1_0.update_date from branch b1_0 where b1_0.unique_name=? and b1_0.delete_flag=?] [No database selected] [n/a]
I´ve also debugged and TenantSchemaMultiTenantConnectionProvider is never called. For my understanding it should be called everytime as soon I have a database request?
I´m using Hibernate version: 6.4.2.Final
Any idea what´s wrong?