diff --git a/docs/src/main/asciidoc/hibernate-orm.adoc b/docs/src/main/asciidoc/hibernate-orm.adoc index def7a4f8bf355..febded3b4cb11 100644 --- a/docs/src/main/asciidoc/hibernate-orm.adoc +++ b/docs/src/main/asciidoc/hibernate-orm.adoc @@ -1132,7 +1132,7 @@ quarkus.hibernate-orm.database.generation=none # Enable SCHEMA approach and use default datasource quarkus.hibernate-orm.multitenant=SCHEMA # You could use a non-default datasource by using the following setting -# quarkus.hibernate-orm.multitenant-schema-datasource=other +# quarkus.hibernate-orm.datasource=other # The default data source used for all tenant schemas quarkus.datasource.db-kind=postgresql diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfigPersistenceUnit.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfigPersistenceUnit.java index bdf64462f2836..1f29cbd83ca83 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfigPersistenceUnit.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfigPersistenceUnit.java @@ -229,7 +229,10 @@ public interface HibernateOrmConfigPersistenceUnit { /** * Defines the name of the datasource to use in case of SCHEMA approach. The datasource of the persistence unit will be used * if not set. + * + * @deprecated Use {@link #datasource()} instead. */ + @Deprecated @WithConverter(TrimmedStringConverter.class) Optional multitenantSchemaDatasource(); diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java index f8533053f2012..656443066024a 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java @@ -670,33 +670,51 @@ public void multitenancy(HibernateOrmRecorder recorder, boolean multitenancyEnabled = false; for (PersistenceUnitDescriptorBuildItem persistenceUnitDescriptor : persistenceUnitDescriptors) { - if (persistenceUnitDescriptor.getConfig().getMultiTenancyStrategy() == MultiTenancyStrategy.NONE) { - continue; - } + String persistenceUnitConfigName = persistenceUnitDescriptor.getConfigurationName(); + var multitenancyStrategy = persistenceUnitDescriptor.getConfig().getMultiTenancyStrategy(); + switch (multitenancyStrategy) { + case NONE -> { + } + case DISCRIMINATOR -> multitenancyEnabled = true; + case DATABASE, SCHEMA -> { + multitenancyEnabled = true; + + String multiTenancySchemaDataSource = persistenceUnitDescriptor.getMultiTenancySchemaDataSource(); + Optional datasource; + if (multitenancyStrategy == MultiTenancyStrategy.SCHEMA && multiTenancySchemaDataSource != null) { + LOG.warnf("Configuration property '%1$s' is deprecated. Use '%2$s' instead.", + HibernateOrmRuntimeConfig.puPropertyKey(persistenceUnitConfigName, + "multitenant-schema-datasource"), + HibernateOrmRuntimeConfig.puPropertyKey(persistenceUnitConfigName, "datasource")); + datasource = Optional.of(multiTenancySchemaDataSource); + } else { + datasource = persistenceUnitDescriptor.getConfig().getDataSource(); + } - multitenancyEnabled = true; - - ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem.configure(DataSourceTenantConnectionResolver.class) - .scope(ApplicationScoped.class) - .types(TenantConnectionResolver.class) - .setRuntimeInit() - .defaultBean() - .unremovable() - .supplier(recorder.dataSourceTenantConnectionResolver(persistenceUnitDescriptor.getPersistenceUnitName(), - persistenceUnitDescriptor.getConfig().getDataSource(), - persistenceUnitDescriptor.getConfig().getMultiTenancyStrategy(), - persistenceUnitDescriptor.getMultiTenancySchemaDataSource())); - - if (PersistenceUnitUtil.isDefaultPersistenceUnit(persistenceUnitDescriptor.getPersistenceUnitName())) { - configurator.addQualifier(Default.class); - } else { - configurator.addQualifier().annotation(DotNames.NAMED) - .addValue("value", persistenceUnitDescriptor.getPersistenceUnitName()).done(); - configurator.addQualifier().annotation(PersistenceUnit.class) - .addValue("value", persistenceUnitDescriptor.getPersistenceUnitName()).done(); - } + ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem + .configure(DataSourceTenantConnectionResolver.class) + .scope(ApplicationScoped.class) + .types(TenantConnectionResolver.class) + .setRuntimeInit() + .defaultBean() + .unremovable() + .supplier(recorder.dataSourceTenantConnectionResolver( + persistenceUnitDescriptor.getPersistenceUnitName(), + datasource, + persistenceUnitDescriptor.getConfig().getMultiTenancyStrategy())); + + if (PersistenceUnitUtil.isDefaultPersistenceUnit(persistenceUnitDescriptor.getPersistenceUnitName())) { + configurator.addQualifier(Default.class); + } else { + configurator.addQualifier().annotation(DotNames.NAMED) + .addValue("value", persistenceUnitDescriptor.getPersistenceUnitName()).done(); + configurator.addQualifier().annotation(PersistenceUnit.class) + .addValue("value", persistenceUnitDescriptor.getPersistenceUnitName()).done(); + } - syntheticBeans.produce(configurator.done()); + syntheticBeans.produce(configurator.done()); + } + } } if (multitenancyEnabled) { diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java index 5050d7e5c648e..01259ea60f49c 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java @@ -83,12 +83,11 @@ public void created(BeanContainer beanContainer) { public Supplier dataSourceTenantConnectionResolver(String persistenceUnitName, Optional dataSourceName, - MultiTenancyStrategy multiTenancyStrategy, String multiTenancySchemaDataSourceName) { + MultiTenancyStrategy multiTenancyStrategy) { return new Supplier() { @Override public DataSourceTenantConnectionResolver get() { - return new DataSourceTenantConnectionResolver(persistenceUnitName, dataSourceName, multiTenancyStrategy, - multiTenancySchemaDataSourceName); + return new DataSourceTenantConnectionResolver(persistenceUnitName, dataSourceName, multiTenancyStrategy); } }; } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/tenant/DataSourceTenantConnectionResolver.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/tenant/DataSourceTenantConnectionResolver.java index 29b38ebebadbe..4bc6dbed953d0 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/tenant/DataSourceTenantConnectionResolver.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/tenant/DataSourceTenantConnectionResolver.java @@ -11,7 +11,6 @@ import org.jboss.logging.Logger; import io.agroal.api.AgroalDataSource; -import io.agroal.api.configuration.AgroalDataSourceConfiguration; import io.quarkus.agroal.DataSource; import io.quarkus.arc.Arc; import io.quarkus.datasource.common.runtime.DataSourceUtil; @@ -35,17 +34,14 @@ public class DataSourceTenantConnectionResolver implements TenantConnectionResol private MultiTenancyStrategy multiTenancyStrategy; - private String multiTenancySchemaDataSourceName; - public DataSourceTenantConnectionResolver() { } public DataSourceTenantConnectionResolver(String persistenceUnitName, Optional dataSourceName, - MultiTenancyStrategy multiTenancyStrategy, String multiTenancySchemaDataSourceName) { + MultiTenancyStrategy multiTenancyStrategy) { this.persistenceUnitName = persistenceUnitName; this.dataSourceName = dataSourceName; this.multiTenancyStrategy = multiTenancyStrategy; - this.multiTenancySchemaDataSourceName = multiTenancySchemaDataSourceName; } @Override @@ -53,49 +49,28 @@ public ConnectionProvider resolve(String tenantId) { LOG.debugv("resolve((persistenceUnitName={0}, tenantIdentifier={1})", persistenceUnitName, tenantId); LOG.debugv("multitenancy strategy: {0}", multiTenancyStrategy); - AgroalDataSource dataSource = tenantDataSource(dataSourceName, tenantId, multiTenancyStrategy, - multiTenancySchemaDataSourceName); + AgroalDataSource dataSource = tenantDataSource(dataSourceName, tenantId, multiTenancyStrategy); if (dataSource == null) { throw new IllegalStateException( String.format(Locale.ROOT, "No instance of datasource found for persistence unit '%1$s' and tenant '%2$s'", persistenceUnitName, tenantId)); } - if (multiTenancyStrategy == MultiTenancyStrategy.SCHEMA) { - return new SchemaTenantConnectionProvider(tenantId, dataSource); - } - return new QuarkusConnectionProvider(dataSource); - } - - /** - * Create a new data source from the given configuration. - * - * @param config Configuration to use. - * - * @return New data source instance. - */ - private static AgroalDataSource createFrom(AgroalDataSourceConfiguration config) { - try { - return AgroalDataSource.from(config); - } catch (SQLException ex) { - throw new IllegalStateException("Failed to create a new data source based on the existing datasource configuration", - ex); - } + return switch (multiTenancyStrategy) { + case DATABASE -> new QuarkusConnectionProvider(dataSource); + case SCHEMA -> new SchemaTenantConnectionProvider(tenantId, dataSource); + default -> throw new IllegalStateException("Unexpected multitenancy strategy: " + multiTenancyStrategy); + }; } private static AgroalDataSource tenantDataSource(Optional dataSourceName, String tenantId, - MultiTenancyStrategy strategy, String multiTenancySchemaDataSourceName) { - if (strategy != MultiTenancyStrategy.SCHEMA) { - return Arc.container().instance(AgroalDataSource.class, new DataSource.DataSourceLiteral(tenantId)).get(); - } - - if (multiTenancySchemaDataSourceName == null) { - // The datasource name should always be present when using SCHEMA multi-tenancy; + MultiTenancyStrategy strategy) { + return switch (strategy) { + case DATABASE -> Arc.container().instance(AgroalDataSource.class, new DataSource.DataSourceLiteral(tenantId)).get(); + // The datasource name should always be present when using a multi-tenancy other than DATABASE; // we perform checks in HibernateOrmProcessor during the build. - AgroalDataSource dataSource = getDataSource(dataSourceName.get()); - return createFrom(dataSource.getConfiguration()); - } - - return getDataSource(multiTenancySchemaDataSourceName); + case SCHEMA -> getDataSource(dataSourceName.get()); + default -> throw new IllegalStateException("Unexpected multitenancy strategy: " + strategy); + }; } private static AgroalDataSource getDataSource(String dataSourceName) {