Merge branch '3.4.x'

Closes gh-44626
This commit is contained in:
Stéphane Nicoll 2025-03-06 15:34:11 +01:00
commit 0a84ebf9f1
10 changed files with 360 additions and 45 deletions

View File

@ -16,8 +16,8 @@
package org.springframework.boot.actuate.autoconfigure.metrics.orm.jpa;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import javax.sql.DataSource;
@ -194,9 +194,10 @@ class HibernateMetricsAutoConfigurationTests {
}
private LocalContainerEntityManagerFactoryBean createSessionFactory(DataSource ds) {
Map<String, String> jpaProperties = new HashMap<>();
jpaProperties.put("hibernate.generate_statistics", "true");
return new EntityManagerFactoryBuilder(new HibernateJpaVendorAdapter(), jpaProperties, null).dataSource(ds)
Function<DataSource, Map<String, ?>> jpaPropertiesFactory = (dataSource) -> Map
.of("hibernate.generate_statistics", "true");
return new EntityManagerFactoryBuilder(new HibernateJpaVendorAdapter(), jpaPropertiesFactory, null)
.dataSource(ds)
.packages(PACKAGE_CLASSES)
.build();
}

View File

@ -145,8 +145,8 @@ class HibernateJpaConfiguration extends JpaBaseConfiguration {
}
@Override
protected Map<String, Object> getVendorProperties() {
Supplier<String> defaultDdlMode = () -> this.defaultDdlAutoProvider.getDefaultDdlAuto(getDataSource());
protected Map<String, Object> getVendorProperties(DataSource dataSource) {
Supplier<String> defaultDdlMode = () -> this.defaultDdlAutoProvider.getDefaultDdlAuto(dataSource);
return new LinkedHashMap<>(this.hibernateProperties.determineHibernateProperties(
getProperties().getProperties(), new HibernateSettings().ddlAuto(defaultDdlMode)
.hibernatePropertiesCustomizers(this.hibernatePropertiesCustomizers)));

View File

@ -120,15 +120,15 @@ public abstract class JpaBaseConfiguration {
public EntityManagerFactoryBuilder entityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter,
ObjectProvider<PersistenceUnitManager> persistenceUnitManager,
ObjectProvider<EntityManagerFactoryBuilderCustomizer> customizers) {
EntityManagerFactoryBuilder builder = new EntityManagerFactoryBuilder(jpaVendorAdapter, buildJpaProperties(),
persistenceUnitManager.getIfAvailable());
EntityManagerFactoryBuilder builder = new EntityManagerFactoryBuilder(jpaVendorAdapter,
this::buildJpaProperties, persistenceUnitManager.getIfAvailable());
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder;
}
private Map<String, ?> buildJpaProperties() {
private Map<String, ?> buildJpaProperties(DataSource dataSource) {
Map<String, Object> properties = new HashMap<>(this.properties.getProperties());
Map<String, Object> vendorProperties = getVendorProperties();
Map<String, Object> vendorProperties = getVendorProperties(dataSource);
customizeVendorProperties(vendorProperties);
properties.putAll(vendorProperties);
return properties;
@ -148,7 +148,24 @@ public abstract class JpaBaseConfiguration {
protected abstract AbstractJpaVendorAdapter createJpaVendorAdapter();
protected abstract Map<String, Object> getVendorProperties();
/**
* Return the vendor-specific properties for the given {@link DataSource}.
* @param dataSource the data source
* @return the vendor properties
* @since 3.4.4
*/
protected abstract Map<String, Object> getVendorProperties(DataSource dataSource);
/**
* Return the vendor-specific properties.
* @return the vendor properties
* @deprecated since 3.4.4 for removal in 3.6.0 in favor of
* {@link #getVendorProperties(DataSource)}
*/
@Deprecated(since = "3.4.4", forRemoval = true)
protected Map<String, Object> getVendorProperties() {
return getVendorProperties(getDataSource());
}
/**
* Customize vendor properties before they are used. Allows for post-processing (for

View File

@ -65,11 +65,14 @@ import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.Fly
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.OracleFlywayConfigurationCustomizer;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.PostgresqlFlywayConfigurationCustomizer;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.SqlServerFlywayConfigurationCustomizer;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.boot.jdbc.SchemaManagement;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.boot.test.context.FilteredClassLoader;
@ -489,6 +492,36 @@ class FlywayAutoConfigurationTests {
.run((context) -> assertThat(context).hasNotFailed());
}
@Test
@WithMetaInfPersistenceXmlResource
void jpaApplyDdl() {
this.contextRunner
.withConfiguration(
AutoConfigurations.of(DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class))
.run((context) -> {
Map<String, Object> jpaProperties = context.getBean(LocalContainerEntityManagerFactoryBean.class)
.getJpaPropertyMap();
assertThat(jpaProperties).doesNotContainKey("hibernate.hbm2ddl.auto");
});
}
@Test
@WithMetaInfPersistenceXmlResource
void jpaAndMultipleDataSourcesApplyDdl() {
this.contextRunner.withConfiguration(AutoConfigurations.of(HibernateJpaAutoConfiguration.class))
.withUserConfiguration(JpaWithMultipleDataSourcesConfiguration.class)
.run((context) -> {
LocalContainerEntityManagerFactoryBean normalEntityManagerFactoryBean = context
.getBean("&normalEntityManagerFactory", LocalContainerEntityManagerFactoryBean.class);
assertThat(normalEntityManagerFactoryBean.getJpaPropertyMap()).containsEntry("configured", "normal")
.containsEntry("hibernate.hbm2ddl.auto", "create-drop");
LocalContainerEntityManagerFactoryBean flywayEntityManagerFactoryBean = context
.getBean("&flywayEntityManagerFactory", LocalContainerEntityManagerFactoryBean.class);
assertThat(flywayEntityManagerFactoryBean.getJpaPropertyMap()).containsEntry("configured", "flyway")
.doesNotContainKey("hibernate.hbm2ddl.auto");
});
}
@Test
void customFlywayWithJdbc() {
this.contextRunner
@ -962,6 +995,13 @@ class FlywayAutoConfigurationTests {
};
}
private static Map<String, ?> configureJpaProperties() {
Map<String, Object> properties = new HashMap<>();
properties.put("configured", "manually");
properties.put("hibernate.transaction.jta.platform", NoJtaPlatform.INSTANCE);
return properties;
}
@Configuration(proxyBeanMethods = false)
static class FlywayDataSourceConfiguration {
@ -1057,10 +1097,8 @@ class FlywayAutoConfigurationTests {
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(DataSource dataSource) {
Map<String, Object> properties = new HashMap<>();
properties.put("configured", "manually");
properties.put("hibernate.transaction.jta.platform", NoJtaPlatform.INSTANCE);
return new EntityManagerFactoryBuilder(new HibernateJpaVendorAdapter(), properties, null)
return new EntityManagerFactoryBuilder(new HibernateJpaVendorAdapter(), (ds) -> configureJpaProperties(),
null)
.dataSource(dataSource)
.build();
}
@ -1083,16 +1121,54 @@ class FlywayAutoConfigurationTests {
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
Map<String, Object> properties = new HashMap<>();
properties.put("configured", "manually");
properties.put("hibernate.transaction.jta.platform", NoJtaPlatform.INSTANCE);
return new EntityManagerFactoryBuilder(new HibernateJpaVendorAdapter(), properties, null)
return new EntityManagerFactoryBuilder(new HibernateJpaVendorAdapter(),
(datasource) -> configureJpaProperties(), null)
.dataSource(this.dataSource)
.build();
}
}
@Configuration(proxyBeanMethods = false)
static class JpaWithMultipleDataSourcesConfiguration {
@Bean
@Primary
DataSource normalDataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseConnection.HSQLDB.getType())
.generateUniqueName(true)
.build();
}
@Bean
@Primary
LocalContainerEntityManagerFactoryBean normalEntityManagerFactory(EntityManagerFactoryBuilder builder,
DataSource normalDataSource) {
Map<String, Object> properties = new HashMap<>();
properties.put("configured", "normal");
properties.put("hibernate.transaction.jta.platform", NoJtaPlatform.INSTANCE);
return builder.dataSource(normalDataSource).properties(properties).build();
}
@Bean
@FlywayDataSource
DataSource flywayDataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseConnection.HSQLDB.getType())
.generateUniqueName(true)
.build();
}
@Bean
LocalContainerEntityManagerFactoryBean flywayEntityManagerFactory(EntityManagerFactoryBuilder builder,
@FlywayDataSource DataSource flywayDataSource) {
Map<String, Object> properties = new HashMap<>();
properties.put("configured", "flyway");
properties.put("hibernate.transaction.jta.platform", NoJtaPlatform.INSTANCE);
return builder.dataSource(flywayDataSource).properties(properties).build();
}
}
@Configuration
static class CustomFlywayWithJdbcConfiguration {

View File

@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.liquibase;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -26,6 +27,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.function.Consumer;
@ -33,6 +35,10 @@ import java.util.function.Consumer;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import liquibase.Liquibase;
import liquibase.UpdateSummaryEnum;
import liquibase.UpdateSummaryOutputEnum;
@ -40,6 +46,7 @@ import liquibase.command.core.helpers.ShowSummaryArgument;
import liquibase.integration.spring.Customizer;
import liquibase.integration.spring.SpringLiquibase;
import liquibase.ui.UIServiceEnum;
import org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
@ -49,6 +56,7 @@ import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
@ -56,6 +64,8 @@ import org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseAutoConfigurationRuntimeHints;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@ -71,6 +81,7 @@ import org.springframework.jdbc.datasource.SimpleDriverDataSource;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
@ -549,6 +560,38 @@ class LiquibaseAutoConfigurationTests {
});
}
@Test
@WithDbChangelogMasterYamlResource
@WithMetaInfPersistenceXmlResource
void jpaApplyDdl() {
this.contextRunner
.withConfiguration(
AutoConfigurations.of(DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class))
.run((context) -> {
Map<String, Object> jpaProperties = context.getBean(LocalContainerEntityManagerFactoryBean.class)
.getJpaPropertyMap();
assertThat(jpaProperties).doesNotContainKey("hibernate.hbm2ddl.auto");
});
}
@Test
@WithDbChangelogMasterYamlResource
@WithMetaInfPersistenceXmlResource
void jpaAndMultipleDataSourcesApplyDdl() {
this.contextRunner.withConfiguration(AutoConfigurations.of(HibernateJpaAutoConfiguration.class))
.withUserConfiguration(JpaWithMultipleDataSourcesConfiguration.class)
.run((context) -> {
LocalContainerEntityManagerFactoryBean normalEntityManagerFactoryBean = context
.getBean("&normalEntityManagerFactory", LocalContainerEntityManagerFactoryBean.class);
assertThat(normalEntityManagerFactoryBean.getJpaPropertyMap()).containsEntry("configured", "normal")
.containsEntry("hibernate.hbm2ddl.auto", "create-drop");
LocalContainerEntityManagerFactoryBean liquibaseEntityManagerFactory = context
.getBean("&liquibaseEntityManagerFactory", LocalContainerEntityManagerFactoryBean.class);
assertThat(liquibaseEntityManagerFactory.getJpaPropertyMap()).containsEntry("configured", "liquibase")
.doesNotContainKey("hibernate.hbm2ddl.auto");
});
}
@Test
@WithDbChangelogMasterYamlResource
void userConfigurationJdbcTemplateDependency() {
@ -669,6 +712,46 @@ class LiquibaseAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class JpaWithMultipleDataSourcesConfiguration {
@Bean
@Primary
DataSource normalDataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseConnection.HSQLDB.getType())
.generateUniqueName(true)
.build();
}
@Bean
@Primary
LocalContainerEntityManagerFactoryBean normalEntityManagerFactory(EntityManagerFactoryBuilder builder,
DataSource normalDataSource) {
Map<String, Object> properties = new HashMap<>();
properties.put("configured", "normal");
properties.put("hibernate.transaction.jta.platform", NoJtaPlatform.INSTANCE);
return builder.dataSource(normalDataSource).properties(properties).build();
}
@Bean
@LiquibaseDataSource
DataSource liquibaseDataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseConnection.HSQLDB.getType())
.generateUniqueName(true)
.build();
}
@Bean
LocalContainerEntityManagerFactoryBean liquibaseEntityManagerFactory(EntityManagerFactoryBuilder builder,
@LiquibaseDataSource DataSource liquibaseDataSource) {
Map<String, Object> properties = new HashMap<>();
properties.put("configured", "liquibase");
properties.put("hibernate.transaction.jta.platform", NoJtaPlatform.INSTANCE);
return builder.dataSource(liquibaseDataSource).properties(properties).build();
}
}
@Configuration(proxyBeanMethods = false)
static class CustomDataSourceConfiguration {
@ -805,4 +888,74 @@ class LiquibaseAutoConfigurationTests {
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@WithResource(name = "META-INF/persistence.xml",
content = """
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence https://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="manually-configured">
<class>org.springframework.boot.autoconfigure.flyway.FlywayAutoConfigurationTests$City</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
</persistence>
""")
@interface WithMetaInfPersistenceXmlResource {
}
@Entity
public static class City implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String state;
@Column(nullable = false)
private String country;
@Column(nullable = false)
private String map;
protected City() {
}
City(String name, String state, String country, String map) {
this.name = name;
this.state = state;
this.country = country;
this.map = map;
}
public String getName() {
return this.name;
}
public String getState() {
return this.state;
}
public String getCountry() {
return this.country;
}
public String getMap() {
return this.map;
}
@Override
public String toString() {
return getName() + "," + getState() + "," + getCountry();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -81,7 +81,8 @@ class CustomHibernateJpaAutoConfigurationTests {
.withPropertyValues("spring.datasource.url:jdbc:h2:mem:naming-strategy-beans")
.run((context) -> {
HibernateJpaConfiguration jpaConfiguration = context.getBean(HibernateJpaConfiguration.class);
Map<String, Object> hibernateProperties = jpaConfiguration.getVendorProperties();
Map<String, Object> hibernateProperties = jpaConfiguration
.getVendorProperties(context.getBean(DataSource.class));
assertThat(hibernateProperties).containsEntry("hibernate.implicit_naming_strategy",
NamingStrategyConfiguration.implicitNamingStrategy);
assertThat(hibernateProperties).containsEntry("hibernate.physical_naming_strategy",
@ -93,7 +94,8 @@ class CustomHibernateJpaAutoConfigurationTests {
void hibernatePropertiesCustomizersAreAppliedInOrder() {
this.contextRunner.withUserConfiguration(HibernatePropertiesCustomizerConfiguration.class).run((context) -> {
HibernateJpaConfiguration jpaConfiguration = context.getBean(HibernateJpaConfiguration.class);
Map<String, Object> hibernateProperties = jpaConfiguration.getVendorProperties();
Map<String, Object> hibernateProperties = jpaConfiguration
.getVendorProperties(context.getBean(DataSource.class));
assertThat(hibernateProperties).containsEntry("test.counter", 2);
});
}

View File

@ -75,6 +75,7 @@ import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.boot.testsupport.classpath.resources.WithResource;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
@ -419,8 +420,7 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes
@Test
void physicalNamingStrategyCanBeUsed() {
contextRunner().withUserConfiguration(TestPhysicalNamingStrategyConfiguration.class).run((context) -> {
Map<String, Object> hibernateProperties = context.getBean(HibernateJpaConfiguration.class)
.getVendorProperties();
Map<String, Object> hibernateProperties = getVendorProperties(context);
assertThat(hibernateProperties)
.contains(entry("hibernate.physical_naming_strategy", context.getBean("testPhysicalNamingStrategy")));
assertThat(hibernateProperties).doesNotContainKeys("hibernate.ejb.naming_strategy");
@ -430,8 +430,7 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes
@Test
void implicitNamingStrategyCanBeUsed() {
contextRunner().withUserConfiguration(TestImplicitNamingStrategyConfiguration.class).run((context) -> {
Map<String, Object> hibernateProperties = context.getBean(HibernateJpaConfiguration.class)
.getVendorProperties();
Map<String, Object> hibernateProperties = getVendorProperties(context);
assertThat(hibernateProperties)
.contains(entry("hibernate.implicit_naming_strategy", context.getBean("testImplicitNamingStrategy")));
assertThat(hibernateProperties).doesNotContainKeys("hibernate.ejb.naming_strategy");
@ -446,8 +445,7 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes
.withPropertyValues("spring.jpa.hibernate.naming.physical-strategy:com.example.Physical",
"spring.jpa.hibernate.naming.implicit-strategy:com.example.Implicit")
.run((context) -> {
Map<String, Object> hibernateProperties = context.getBean(HibernateJpaConfiguration.class)
.getVendorProperties();
Map<String, Object> hibernateProperties = getVendorProperties(context);
assertThat(hibernateProperties).contains(
entry("hibernate.physical_naming_strategy", context.getBean("testPhysicalNamingStrategy")),
entry("hibernate.implicit_naming_strategy", context.getBean("testImplicitNamingStrategy")));
@ -463,8 +461,7 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes
.withPropertyValues("spring.jpa.hibernate.naming.physical-strategy:com.example.Physical",
"spring.jpa.hibernate.naming.implicit-strategy:com.example.Implicit")
.run((context) -> {
Map<String, Object> hibernateProperties = context.getBean(HibernateJpaConfiguration.class)
.getVendorProperties();
Map<String, Object> hibernateProperties = getVendorProperties(context);
TestHibernatePropertiesCustomizerConfiguration configuration = context
.getBean(TestHibernatePropertiesCustomizerConfiguration.class);
assertThat(hibernateProperties).contains(
@ -556,8 +553,11 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes
private ContextConsumer<AssertableApplicationContext> vendorProperties(
Consumer<Map<String, Object>> vendorProperties) {
return (context) -> vendorProperties
.accept(context.getBean(HibernateJpaConfiguration.class).getVendorProperties());
return (context) -> vendorProperties.accept(getVendorProperties(context));
}
private static Map<String, Object> getVendorProperties(ConfigurableApplicationContext context) {
return context.getBean(HibernateJpaConfiguration.class).getVendorProperties(context.getBean(DataSource.class));
}
@Test

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +16,10 @@
package org.springframework.boot.docs.howto.dataaccess.usemultipleentitymanagers;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
@ -48,7 +52,9 @@ public class MyAdditionalEntityManagerFactoryConfiguration {
private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties jpaProperties) {
JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(jpaProperties);
return new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.getProperties(), null);
Function<DataSource, Map<String, ?>> jpaPropertiesFactory = (dataSource) -> createJpaProperties(dataSource,
jpaProperties.getProperties());
return new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaPropertiesFactory, null);
}
private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {
@ -56,4 +62,10 @@ public class MyAdditionalEntityManagerFactoryConfiguration {
return new HibernateJpaVendorAdapter();
}
private Map<String, ?> createJpaProperties(DataSource dataSource, Map<String, ?> existingProperties) {
Map<String, ?> jpaProperties = new LinkedHashMap<>(existingProperties);
// ... map JPA properties that require the DataSource (e.g. DDL flags)
return jpaProperties;
}
}

View File

@ -16,8 +16,6 @@
package org.springframework.boot.docs.howto.dataaccess.usemultipleentitymanagers
import javax.sql.DataSource
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties
import org.springframework.boot.context.properties.ConfigurationProperties
@ -27,6 +25,7 @@ import org.springframework.context.annotation.Configuration
import org.springframework.orm.jpa.JpaVendorAdapter
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter
import javax.sql.DataSource
@Suppress("UNUSED_PARAMETER")
@Configuration(proxyBeanMethods = false)
@ -51,7 +50,9 @@ class MyAdditionalEntityManagerFactoryConfiguration {
private fun createEntityManagerFactoryBuilder(jpaProperties: JpaProperties): EntityManagerFactoryBuilder {
val jpaVendorAdapter = createJpaVendorAdapter(jpaProperties)
return EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.properties, null)
val jpaPropertiesFactory = { dataSource: DataSource ->
createJpaProperties(dataSource, jpaProperties.properties) }
return EntityManagerFactoryBuilder(jpaVendorAdapter, jpaPropertiesFactory, null)
}
private fun createJpaVendorAdapter(jpaProperties: JpaProperties): JpaVendorAdapter {
@ -59,4 +60,10 @@ class MyAdditionalEntityManagerFactoryConfiguration {
return HibernateJpaVendorAdapter()
}
private fun createJpaProperties(dataSource: DataSource, existingProperties: Map<String, *>): Map<String, *> {
val jpaProperties: Map<String, *> = LinkedHashMap(existingProperties)
// ... map JPA properties that require the DataSource (e.g. DDL flags)
return jpaProperties
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,6 +22,7 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import javax.sql.DataSource;
@ -54,7 +55,7 @@ public class EntityManagerFactoryBuilder {
private final PersistenceUnitManager persistenceUnitManager;
private final Map<String, Object> jpaProperties;
private final Function<DataSource, Map<String, ?>> jpaPropertiesFactory;
private final URL persistenceUnitRootLocation;
@ -62,6 +63,42 @@ public class EntityManagerFactoryBuilder {
private PersistenceUnitPostProcessor[] persistenceUnitPostProcessors;
/**
* Create a new instance passing in the common pieces that will be shared if multiple
* EntityManagerFactory instances are created.
* @param jpaVendorAdapter a vendor adapter
* @param jpaPropertiesFactory the JPA properties to be passed to the persistence
* provider, based on the {@linkplain #dataSource(DataSource) configured data source}
* @param persistenceUnitManager optional source of persistence unit information (can
* be null)
* @since 3.4.4
*/
public EntityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter,
Function<DataSource, Map<String, ?>> jpaPropertiesFactory, PersistenceUnitManager persistenceUnitManager) {
this(jpaVendorAdapter, jpaPropertiesFactory, persistenceUnitManager, null);
}
/**
* Create a new instance passing in the common pieces that will be shared if multiple
* EntityManagerFactory instances are created.
* @param jpaVendorAdapter a vendor adapter
* @param jpaPropertiesFactory the JPA properties to be passed to the persistence
* provider, based on the {@linkplain #dataSource(DataSource) configured data source}
* @param persistenceUnitManager optional source of persistence unit information (can
* be null)
* @param persistenceUnitRootLocation the persistence unit root location to use as a
* fallback or {@code null}
* @since 3.4.4
*/
public EntityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter,
Function<DataSource, Map<String, ?>> jpaPropertiesFactory, PersistenceUnitManager persistenceUnitManager,
URL persistenceUnitRootLocation) {
this.jpaVendorAdapter = jpaVendorAdapter;
this.persistenceUnitManager = persistenceUnitManager;
this.jpaPropertiesFactory = jpaPropertiesFactory;
this.persistenceUnitRootLocation = persistenceUnitRootLocation;
}
/**
* Create a new instance passing in the common pieces that will be shared if multiple
* EntityManagerFactory instances are created.
@ -69,10 +106,13 @@ public class EntityManagerFactoryBuilder {
* @param jpaProperties the JPA properties to be passed to the persistence provider
* @param persistenceUnitManager optional source of persistence unit information (can
* be null)
* @deprecated since 3.4.4 for removal in 3.6.0 in favor of
* {@link #EntityManagerFactoryBuilder(JpaVendorAdapter, Function, PersistenceUnitManager)}
*/
@Deprecated(since = "3.4.4", forRemoval = true)
public EntityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter, Map<String, ?> jpaProperties,
PersistenceUnitManager persistenceUnitManager) {
this(jpaVendorAdapter, jpaProperties, persistenceUnitManager, null);
this(jpaVendorAdapter, (datasource) -> jpaProperties, persistenceUnitManager, null);
}
/**
@ -85,15 +125,21 @@ public class EntityManagerFactoryBuilder {
* @param persistenceUnitRootLocation the persistence unit root location to use as a
* fallback or {@code null}
* @since 1.4.1
* @deprecated since 3.4.4 for removal in 3.6.0 in favor of
* {@link #EntityManagerFactoryBuilder(JpaVendorAdapter, Function, PersistenceUnitManager, URL)}
*/
@Deprecated(since = "3.4.4", forRemoval = true)
public EntityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter, Map<String, ?> jpaProperties,
PersistenceUnitManager persistenceUnitManager, URL persistenceUnitRootLocation) {
this.jpaVendorAdapter = jpaVendorAdapter;
this.persistenceUnitManager = persistenceUnitManager;
this.jpaProperties = new LinkedHashMap<>(jpaProperties);
this.persistenceUnitRootLocation = persistenceUnitRootLocation;
this(jpaVendorAdapter, (datasource) -> jpaProperties, persistenceUnitManager, persistenceUnitRootLocation);
}
/**
* Create a new {@link Builder} for a {@code EntityManagerFactory} using the settings
* of the given instance, and the given {@link DataSource}.
* @param dataSource the data source to use
* @return a builder to create an {@code EntityManagerFactory}
*/
public Builder dataSource(DataSource dataSource) {
return new Builder(dataSource);
}
@ -255,7 +301,8 @@ public class EntityManagerFactoryBuilder {
else {
entityManagerFactoryBean.setPackagesToScan(this.packagesToScan);
}
entityManagerFactoryBean.getJpaPropertyMap().putAll(EntityManagerFactoryBuilder.this.jpaProperties);
Map<String, ?> jpaProperties = EntityManagerFactoryBuilder.this.jpaPropertiesFactory.apply(this.dataSource);
entityManagerFactoryBean.getJpaPropertyMap().putAll(new LinkedHashMap<>(jpaProperties));
entityManagerFactoryBean.getJpaPropertyMap().putAll(this.properties);
if (!ObjectUtils.isEmpty(this.mappingResources)) {
entityManagerFactoryBean.setMappingResources(this.mappingResources);