Create spring-boot-jpa module

This commit is contained in:
Stéphane Nicoll
2025-03-19 18:07:28 +01:00
committed by Phillip Webb
parent 81d4528eee
commit 079713039d
89 changed files with 385 additions and 316 deletions

View File

@@ -0,0 +1,326 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa;
import java.net.URL;
import java.util.HashMap;
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;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Convenient builder for JPA EntityManagerFactory instances. Collects common
* configuration when constructed and then allows you to create one or more
* {@link LocalContainerEntityManagerFactoryBean} through a fluent builder pattern. The
* most common options are covered in the builder, but you can always manipulate the
* product of the builder if you need more control, before returning it from a
* {@code @Bean} definition.
*
* @author Dave Syer
* @author Phillip Webb
* @author Stephane Nicoll
* @since 4.0.0
*/
public class EntityManagerFactoryBuilder {
private final JpaVendorAdapter jpaVendorAdapter;
private final PersistenceUnitManager persistenceUnitManager;
private final Function<DataSource, Map<String, ?>> jpaPropertiesFactory;
private final URL persistenceUnitRootLocation;
private AsyncTaskExecutor bootstrapExecutor;
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.
* @param jpaVendorAdapter a vendor adapter
* @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 4.0.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, (datasource) -> jpaProperties, 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 jpaProperties the JPA properties to be passed to the persistence provider
* @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 1.4.1
* @deprecated since 3.4.4 for removal in 4.0.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, (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);
}
/**
* Configure the bootstrap executor to be used by the
* {@link LocalContainerEntityManagerFactoryBean}.
* @param bootstrapExecutor the executor
* @since 2.1.0
*/
public void setBootstrapExecutor(AsyncTaskExecutor bootstrapExecutor) {
this.bootstrapExecutor = bootstrapExecutor;
}
/**
* Set the {@linkplain PersistenceUnitPostProcessor persistence unit post processors}
* to be applied to the PersistenceUnitInfo used for creating the
* {@link LocalContainerEntityManagerFactoryBean}.
* @param persistenceUnitPostProcessors the persistence unit post processors to use
* @since 2.5.0
*/
public void setPersistenceUnitPostProcessors(PersistenceUnitPostProcessor... persistenceUnitPostProcessors) {
this.persistenceUnitPostProcessors = persistenceUnitPostProcessors;
}
/**
* A fluent builder for a LocalContainerEntityManagerFactoryBean.
*/
public final class Builder {
private final DataSource dataSource;
private PersistenceManagedTypes managedTypes;
private String[] packagesToScan;
private String persistenceUnit;
private final Map<String, Object> properties = new HashMap<>();
private String[] mappingResources;
private boolean jta;
private Builder(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* The persistence managed types, providing both the managed entities and packages
* the entity manager should consider.
* @param managedTypes managed types.
* @return the builder for fluent usage
*/
public Builder managedTypes(PersistenceManagedTypes managedTypes) {
this.managedTypes = managedTypes;
return this;
}
/**
* The names of packages to scan for {@code @Entity} annotations.
* @param packagesToScan packages to scan
* @return the builder for fluent usage
* @see #managedTypes(PersistenceManagedTypes)
*/
public Builder packages(String... packagesToScan) {
this.packagesToScan = packagesToScan;
return this;
}
/**
* The classes whose packages should be scanned for {@code @Entity} annotations.
* @param basePackageClasses the classes to use
* @return the builder for fluent usage
* @see #managedTypes(PersistenceManagedTypes)
*/
public Builder packages(Class<?>... basePackageClasses) {
Set<String> packages = new HashSet<>();
for (Class<?> type : basePackageClasses) {
packages.add(ClassUtils.getPackageName(type));
}
this.packagesToScan = StringUtils.toStringArray(packages);
return this;
}
/**
* The name of the persistence unit. If only building one EntityManagerFactory you
* can omit this, but if there are more than one in the same application you
* should give them distinct names.
* @param persistenceUnit the name of the persistence unit
* @return the builder for fluent usage
*/
public Builder persistenceUnit(String persistenceUnit) {
this.persistenceUnit = persistenceUnit;
return this;
}
/**
* Generic properties for standard JPA or vendor-specific configuration. These
* properties override any values provided in the constructor.
* @param properties the properties to use
* @return the builder for fluent usage
*/
public Builder properties(Map<String, ?> properties) {
this.properties.putAll(properties);
return this;
}
/**
* The mapping resources (equivalent to {@code <mapping-file>} entries in
* {@code persistence.xml}) for the persistence unit.
* <p>
* Note that mapping resources must be relative to the classpath root, e.g.
* "META-INF/mappings.xml" or "com/mycompany/repository/mappings.xml", so that
* they can be loaded through {@code ClassLoader.getResource()}.
* @param mappingResources the mapping resources to use
* @return the builder for fluent usage
*/
public Builder mappingResources(String... mappingResources) {
this.mappingResources = mappingResources;
return this;
}
/**
* Configure if using a JTA {@link DataSource}, i.e. if
* {@link LocalContainerEntityManagerFactoryBean#setDataSource(DataSource)
* setDataSource} or
* {@link LocalContainerEntityManagerFactoryBean#setJtaDataSource(DataSource)
* setJtaDataSource} should be called on the
* {@link LocalContainerEntityManagerFactoryBean}.
* @param jta if the data source is JTA
* @return the builder for fluent usage
*/
public Builder jta(boolean jta) {
this.jta = jta;
return this;
}
public LocalContainerEntityManagerFactoryBean build() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
if (EntityManagerFactoryBuilder.this.persistenceUnitManager != null) {
entityManagerFactoryBean
.setPersistenceUnitManager(EntityManagerFactoryBuilder.this.persistenceUnitManager);
}
if (this.persistenceUnit != null) {
entityManagerFactoryBean.setPersistenceUnitName(this.persistenceUnit);
}
entityManagerFactoryBean.setJpaVendorAdapter(EntityManagerFactoryBuilder.this.jpaVendorAdapter);
if (this.jta) {
entityManagerFactoryBean.setJtaDataSource(this.dataSource);
}
else {
entityManagerFactoryBean.setDataSource(this.dataSource);
}
if (this.managedTypes != null) {
entityManagerFactoryBean.setManagedTypes(this.managedTypes);
}
else {
entityManagerFactoryBean.setPackagesToScan(this.packagesToScan);
}
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);
}
URL rootLocation = EntityManagerFactoryBuilder.this.persistenceUnitRootLocation;
if (rootLocation != null) {
entityManagerFactoryBean.setPersistenceUnitRootLocation(rootLocation.toString());
}
if (EntityManagerFactoryBuilder.this.bootstrapExecutor != null) {
entityManagerFactoryBean.setBootstrapExecutor(EntityManagerFactoryBuilder.this.bootstrapExecutor);
}
if (EntityManagerFactoryBuilder.this.persistenceUnitPostProcessors != null) {
entityManagerFactoryBean
.setPersistenceUnitPostProcessors(EntityManagerFactoryBuilder.this.persistenceUnitPostProcessors);
}
return entityManagerFactoryBean;
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import jakarta.persistence.EntityManagerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.sql.init.dependency.AbstractBeansOfTypeDatabaseInitializerDetector;
import org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
/**
* A {@link DatabaseInitializerDetector} for JPA.
*
* @author Andy Wilkinson
*/
class JpaDatabaseInitializerDetector extends AbstractBeansOfTypeDatabaseInitializerDetector {
private final Environment environment;
JpaDatabaseInitializerDetector(Environment environment) {
this.environment = environment;
}
@Override
protected Set<Class<?>> getDatabaseInitializerBeanTypes() {
boolean deferred = this.environment.getProperty("spring.jpa.defer-datasource-initialization", boolean.class,
false);
return deferred ? Collections.singleton(EntityManagerFactory.class) : Collections.emptySet();
}
@Override
public void detectionComplete(ConfigurableListableBeanFactory beanFactory, Set<String> dataSourceInitializerNames) {
configureOtherInitializersToDependOnJpaInitializers(beanFactory, dataSourceInitializerNames);
}
private void configureOtherInitializersToDependOnJpaInitializers(ConfigurableListableBeanFactory beanFactory,
Set<String> dataSourceInitializerNames) {
Set<String> jpaInitializers = new HashSet<>();
Set<String> otherInitializers = new HashSet<>(dataSourceInitializerNames);
Iterator<String> iterator = otherInitializers.iterator();
while (iterator.hasNext()) {
String initializerName = iterator.next();
BeanDefinition initializerDefinition = beanFactory.getBeanDefinition(initializerName);
if (JpaDatabaseInitializerDetector.class.getName()
.equals(initializerDefinition.getAttribute(DatabaseInitializerDetector.class.getName()))) {
iterator.remove();
jpaInitializers.add(initializerName);
}
}
for (String otherInitializerName : otherInitializers) {
BeanDefinition definition = beanFactory.getBeanDefinition(otherInitializerName);
String[] dependencies = definition.getDependsOn();
for (String dependencyName : jpaInitializers) {
dependencies = StringUtils.addStringToArray(dependencies, dependencyName);
}
definition.setDependsOn(dependencies);
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import jakarta.persistence.EntityManagerFactory;
import org.springframework.boot.sql.init.dependency.AbstractBeansOfTypeDependsOnDatabaseInitializationDetector;
import org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector;
import org.springframework.core.env.Environment;
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
/**
* {@link DependsOnDatabaseInitializationDetector} for JPA.
*
* @author Andy Wilkinson
*/
class JpaDependsOnDatabaseInitializationDetector extends AbstractBeansOfTypeDependsOnDatabaseInitializationDetector {
private final Environment environment;
JpaDependsOnDatabaseInitializationDetector(Environment environment) {
this.environment = environment;
}
@Override
protected Set<Class<?>> getDependsOnDatabaseInitializationBeanTypes() {
boolean postpone = this.environment.getProperty("spring.jpa.defer-datasource-initialization", boolean.class,
false);
return postpone ? Collections.emptySet()
: new HashSet<>(Arrays.asList(EntityManagerFactory.class, AbstractEntityManagerFactoryBean.class));
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure;
import org.springframework.boot.jpa.EntityManagerFactoryBuilder;
/**
* Callback interface that can be used to customize the auto-configured
* {@link EntityManagerFactoryBuilder}.
*
* @author Andy Wilkinson
* @since 4.0.0
*/
@FunctionalInterface
public interface EntityManagerFactoryBuilderCustomizer {
/**
* Customize the given {@code builder}.
* @param builder the builder to customize
*/
void customize(EntityManagerFactoryBuilder builder);
}

View File

@@ -0,0 +1,58 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure;
import jakarta.persistence.EntityManagerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.AbstractDependsOnBeanFactoryPostProcessor;
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
/**
* {@link BeanFactoryPostProcessor} that can be used to dynamically declare that all
* {@link EntityManagerFactory} beans should "depend on" one or more specific beans.
*
* @author Marcel Overdijk
* @author Dave Syer
* @author Phillip Webb
* @author Andy Wilkinson
* @author Andrii Hrytsiuk
* @since 4.0.0
* @see BeanDefinition#setDependsOn(String[])
*/
public class EntityManagerFactoryDependsOnPostProcessor extends AbstractDependsOnBeanFactoryPostProcessor {
/**
* Creates a new {@code EntityManagerFactoryDependsOnPostProcessor} that will set up
* dependencies upon beans with the given names.
* @param dependsOn names of the beans to depend upon
*/
public EntityManagerFactoryDependsOnPostProcessor(String... dependsOn) {
super(EntityManagerFactory.class, AbstractEntityManagerFactoryBean.class, dependsOn);
}
/**
* Creates a new {@code EntityManagerFactoryDependsOnPostProcessor} that will set up
* dependencies upon beans with the given types.
* @param dependsOn types of the beans to depend upon
*/
public EntityManagerFactoryDependsOnPostProcessor(Class<?>... dependsOn) {
super(EntityManagerFactory.class, AbstractEntityManagerFactoryBean.class, dependsOn);
}
}

View File

@@ -0,0 +1,280 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import jakarta.persistence.EntityManagerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingFilterBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.domain.EntityScanPackages;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jpa.EntityManagerFactoryBuilder;
import org.springframework.boot.transaction.autoconfigure.TransactionManagerCustomizers;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ResourceLoader;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.persistenceunit.ManagedClassNameFilter;
import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes;
import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypesScanner;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.jta.JtaTransactionManager;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Base {@link EnableAutoConfiguration Auto-configuration} for JPA.
*
* @author Phillip Webb
* @author Dave Syer
* @author Oliver Gierke
* @author Andy Wilkinson
* @author Kazuki Shimizu
* @author Eddú Meléndez
* @author Yanming Zhou
* @since 4.0.0
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(JpaProperties.class)
public abstract class JpaBaseConfiguration {
private final DataSource dataSource;
private final JpaProperties properties;
private final JtaTransactionManager jtaTransactionManager;
protected JpaBaseConfiguration(DataSource dataSource, JpaProperties properties,
ObjectProvider<JtaTransactionManager> jtaTransactionManager) {
this.dataSource = dataSource;
this.properties = properties;
this.jtaTransactionManager = jtaTransactionManager.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean(TransactionManager.class)
public PlatformTransactionManager transactionManager(
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
return transactionManager;
}
@Bean
@ConditionalOnMissingBean
public JpaVendorAdapter jpaVendorAdapter() {
AbstractJpaVendorAdapter adapter = createJpaVendorAdapter();
adapter.setShowSql(this.properties.isShowSql());
if (this.properties.getDatabase() != null) {
adapter.setDatabase(this.properties.getDatabase());
}
if (this.properties.getDatabasePlatform() != null) {
adapter.setDatabasePlatform(this.properties.getDatabasePlatform());
}
adapter.setGenerateDdl(this.properties.isGenerateDdl());
return adapter;
}
@Bean
@ConditionalOnMissingBean
public EntityManagerFactoryBuilder entityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter,
ObjectProvider<PersistenceUnitManager> persistenceUnitManager,
ObjectProvider<EntityManagerFactoryBuilderCustomizer> customizers) {
EntityManagerFactoryBuilder builder = new EntityManagerFactoryBuilder(jpaVendorAdapter,
this::buildJpaProperties, persistenceUnitManager.getIfAvailable());
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder;
}
private Map<String, ?> buildJpaProperties(DataSource dataSource) {
Map<String, Object> properties = new HashMap<>(this.properties.getProperties());
Map<String, Object> vendorProperties = getVendorProperties(dataSource);
customizeVendorProperties(vendorProperties);
properties.putAll(vendorProperties);
return properties;
}
@Bean
@Primary
@ConditionalOnMissingBean({ LocalContainerEntityManagerFactoryBean.class, EntityManagerFactory.class })
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder factoryBuilder,
PersistenceManagedTypes persistenceManagedTypes) {
return factoryBuilder.dataSource(this.dataSource)
.managedTypes(persistenceManagedTypes)
.mappingResources(getMappingResources())
.jta(isJta())
.build();
}
protected abstract AbstractJpaVendorAdapter createJpaVendorAdapter();
/**
* 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 4.0.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
* example to configure JTA specific settings).
* @param vendorProperties the vendor properties to customize
*/
protected void customizeVendorProperties(Map<String, Object> vendorProperties) {
}
private String[] getMappingResources() {
List<String> mappingResources = this.properties.getMappingResources();
return (!ObjectUtils.isEmpty(mappingResources) ? StringUtils.toStringArray(mappingResources) : null);
}
/**
* Return the JTA transaction manager.
* @return the transaction manager or {@code null}
*/
protected JtaTransactionManager getJtaTransactionManager() {
return this.jtaTransactionManager;
}
/**
* Returns if a JTA {@link PlatformTransactionManager} is being used.
* @return if a JTA transaction manager is being used
*/
protected final boolean isJta() {
return (this.jtaTransactionManager != null);
}
/**
* Return the {@link JpaProperties}.
* @return the properties
*/
protected final JpaProperties getProperties() {
return this.properties;
}
/**
* Return the {@link DataSource}.
* @return the data source
*/
protected final DataSource getDataSource() {
return this.dataSource;
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean({ LocalContainerEntityManagerFactoryBean.class, EntityManagerFactory.class })
static class PersistenceManagedTypesConfiguration {
@Bean
@Primary
@ConditionalOnMissingBean
static PersistenceManagedTypes persistenceManagedTypes(BeanFactory beanFactory, ResourceLoader resourceLoader,
ObjectProvider<ManagedClassNameFilter> managedClassNameFilter) {
String[] packagesToScan = getPackagesToScan(beanFactory);
return new PersistenceManagedTypesScanner(resourceLoader, managedClassNameFilter.getIfAvailable())
.scan(packagesToScan);
}
private static String[] getPackagesToScan(BeanFactory beanFactory) {
List<String> packages = EntityScanPackages.get(beanFactory).getPackageNames();
if (packages.isEmpty() && AutoConfigurationPackages.has(beanFactory)) {
packages = AutoConfigurationPackages.get(beanFactory);
}
return StringUtils.toStringArray(packages);
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(WebMvcConfigurer.class)
@ConditionalOnMissingBean({ OpenEntityManagerInViewInterceptor.class, OpenEntityManagerInViewFilter.class })
@ConditionalOnMissingFilterBean(OpenEntityManagerInViewFilter.class)
@ConditionalOnBooleanProperty(name = "spring.jpa.open-in-view", matchIfMissing = true)
protected static class JpaWebConfiguration {
private static final Log logger = LogFactory.getLog(JpaWebConfiguration.class);
private final JpaProperties jpaProperties;
protected JpaWebConfiguration(JpaProperties jpaProperties) {
this.jpaProperties = jpaProperties;
}
@Bean
public OpenEntityManagerInViewInterceptor openEntityManagerInViewInterceptor() {
if (this.jpaProperties.getOpenInView() == null) {
logger.warn("spring.jpa.open-in-view is enabled by default. "
+ "Therefore, database queries may be performed during view "
+ "rendering. Explicitly configure spring.jpa.open-in-view to disable this warning");
}
return new OpenEntityManagerInViewInterceptor();
}
@Bean
public WebMvcConfigurer openEntityManagerInViewInterceptorConfigurer(
OpenEntityManagerInViewInterceptor interceptor) {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addWebRequestInterceptor(interceptor);
}
};
}
}
}

View File

@@ -0,0 +1,130 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.orm.jpa.vendor.Database;
/**
* External configuration properties for a JPA EntityManagerFactory created by Spring.
*
* @author Dave Syer
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Eddú Meléndez
* @author Madhura Bhave
* @since 4.0.0
*/
@ConfigurationProperties("spring.jpa")
public class JpaProperties {
/**
* Additional native properties to set on the JPA provider.
*/
private Map<String, String> properties = new HashMap<>();
/**
* Mapping resources (equivalent to "mapping-file" entries in persistence.xml).
*/
private final List<String> mappingResources = new ArrayList<>();
/**
* Name of the target database to operate on, auto-detected by default. Can be
* alternatively set using the "Database" enum.
*/
private String databasePlatform;
/**
* Target database to operate on, auto-detected by default. Can be alternatively set
* using the "databasePlatform" property.
*/
private Database database;
/**
* Whether to initialize the schema on startup.
*/
private boolean generateDdl = false;
/**
* Whether to enable logging of SQL statements.
*/
private boolean showSql = false;
/**
* Register OpenEntityManagerInViewInterceptor. Binds a JPA EntityManager to the
* thread for the entire processing of the request.
*/
private Boolean openInView;
public Map<String, String> getProperties() {
return this.properties;
}
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
public List<String> getMappingResources() {
return this.mappingResources;
}
public String getDatabasePlatform() {
return this.databasePlatform;
}
public void setDatabasePlatform(String databasePlatform) {
this.databasePlatform = databasePlatform;
}
public Database getDatabase() {
return this.database;
}
public void setDatabase(Database database) {
this.database = database;
}
public boolean isGenerateDdl() {
return this.generateDdl;
}
public void setGenerateDdl(boolean generateDdl) {
this.generateDdl = generateDdl;
}
public boolean isShowSql() {
return this.showSql;
}
public void setShowSql(boolean showSql) {
this.showSql = showSql;
}
public Boolean getOpenInView() {
return this.openInView;
}
public void setOpenInView(Boolean openInView) {
this.openInView = openInView;
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure.hibernate;
import java.util.stream.StreamSupport;
import javax.sql.DataSource;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.boot.jdbc.SchemaManagement;
import org.springframework.boot.jdbc.SchemaManagementProvider;
/**
* A {@link SchemaManagementProvider} that invokes a configurable number of
* {@link SchemaManagementProvider} instances for embedded data sources only.
*
* @author Stephane Nicoll
*/
class HibernateDefaultDdlAutoProvider implements SchemaManagementProvider {
private final Iterable<SchemaManagementProvider> providers;
HibernateDefaultDdlAutoProvider(Iterable<SchemaManagementProvider> providers) {
this.providers = providers;
}
String getDefaultDdlAuto(DataSource dataSource) {
if (!EmbeddedDatabaseConnection.isEmbedded(dataSource)) {
return "none";
}
SchemaManagement schemaManagement = getSchemaManagement(dataSource);
if (SchemaManagement.MANAGED.equals(schemaManagement)) {
return "none";
}
return "create-drop";
}
@Override
public SchemaManagement getSchemaManagement(DataSource dataSource) {
return StreamSupport.stream(this.providers.spliterator(), false)
.map((provider) -> provider.getSchemaManagement(dataSource))
.filter(SchemaManagement.MANAGED::equals)
.findFirst()
.orElse(SchemaManagement.UNMANAGED);
}
}

View File

@@ -0,0 +1,53 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure.hibernate;
import jakarta.persistence.EntityManager;
import org.hibernate.engine.spi.SessionImplementor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.jpa.autoconfigure.JpaProperties;
import org.springframework.boot.transaction.autoconfigure.TransactionAutoConfiguration;
import org.springframework.boot.transaction.autoconfigure.TransactionManagerCustomizationAutoConfiguration;
import org.springframework.boot.transaction.jta.autoconfigure.JtaAutoConfiguration;
import org.springframework.context.annotation.Import;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Hibernate JPA.
*
* @author Phillip Webb
* @author Josh Long
* @author Manuel Doninger
* @author Andy Wilkinson
* @since 4.0.0
*/
@AutoConfiguration(
after = { DataSourceAutoConfiguration.class, JtaAutoConfiguration.class,
TransactionManagerCustomizationAutoConfiguration.class },
before = { TransactionAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class })
@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class, EntityManager.class, SessionImplementor.class })
@EnableConfigurationProperties(JpaProperties.class)
@Import(HibernateJpaConfiguration.class)
public class HibernateJpaAutoConfiguration {
}

View File

@@ -0,0 +1,278 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure.hibernate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.boot.model.naming.PhysicalNamingStrategySnakeCaseImpl;
import org.hibernate.cfg.ManagedBeanSettings;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeHint;
import org.springframework.aot.hint.TypeHint.Builder;
import org.springframework.aot.hint.TypeReference;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jdbc.SchemaManagementProvider;
import org.springframework.boot.jdbc.metadata.CompositeDataSourcePoolMetadataProvider;
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadata;
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.boot.jpa.autoconfigure.JpaBaseConfiguration;
import org.springframework.boot.jpa.autoconfigure.JpaProperties;
import org.springframework.boot.jpa.autoconfigure.hibernate.HibernateJpaConfiguration.HibernateRuntimeHints;
import org.springframework.boot.jpa.hibernate.SpringImplicitNamingStrategy;
import org.springframework.boot.jpa.hibernate.SpringJtaPlatform;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.jndi.JndiLocatorDelegate;
import org.springframework.orm.hibernate5.SpringBeanContainer;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.jta.JtaTransactionManager;
import org.springframework.util.ClassUtils;
/**
* {@link JpaBaseConfiguration} implementation for Hibernate.
*
* @author Phillip Webb
* @author Josh Long
* @author Manuel Doninger
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Moritz Halbritter
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(HibernateProperties.class)
@ConditionalOnSingleCandidate(DataSource.class)
@ImportRuntimeHints(HibernateRuntimeHints.class)
class HibernateJpaConfiguration extends JpaBaseConfiguration {
private static final Log logger = LogFactory.getLog(HibernateJpaConfiguration.class);
private static final String JTA_PLATFORM = "hibernate.transaction.jta.platform";
private static final String PROVIDER_DISABLES_AUTOCOMMIT = "hibernate.connection.provider_disables_autocommit";
/**
* {@code NoJtaPlatform} implementations for various Hibernate versions.
*/
private static final String[] NO_JTA_PLATFORM_CLASSES = {
"org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform",
"org.hibernate.service.jta.platform.internal.NoJtaPlatform" };
private final HibernateProperties hibernateProperties;
private final HibernateDefaultDdlAutoProvider defaultDdlAutoProvider;
private final DataSourcePoolMetadataProvider poolMetadataProvider;
private final ObjectProvider<SQLExceptionTranslator> sqlExceptionTranslator;
private final List<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers;
HibernateJpaConfiguration(DataSource dataSource, JpaProperties jpaProperties,
ConfigurableListableBeanFactory beanFactory, ObjectProvider<JtaTransactionManager> jtaTransactionManager,
HibernateProperties hibernateProperties,
ObjectProvider<Collection<DataSourcePoolMetadataProvider>> metadataProviders,
ObjectProvider<SchemaManagementProvider> providers,
ObjectProvider<PhysicalNamingStrategy> physicalNamingStrategy,
ObjectProvider<ImplicitNamingStrategy> implicitNamingStrategy,
ObjectProvider<SQLExceptionTranslator> sqlExceptionTranslator,
ObjectProvider<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers) {
super(dataSource, jpaProperties, jtaTransactionManager);
this.hibernateProperties = hibernateProperties;
this.defaultDdlAutoProvider = new HibernateDefaultDdlAutoProvider(providers);
this.poolMetadataProvider = new CompositeDataSourcePoolMetadataProvider(metadataProviders.getIfAvailable());
this.sqlExceptionTranslator = sqlExceptionTranslator;
this.hibernatePropertiesCustomizers = determineHibernatePropertiesCustomizers(
physicalNamingStrategy.getIfAvailable(), implicitNamingStrategy.getIfAvailable(), beanFactory,
hibernatePropertiesCustomizers.orderedStream().toList());
}
private List<HibernatePropertiesCustomizer> determineHibernatePropertiesCustomizers(
PhysicalNamingStrategy physicalNamingStrategy, ImplicitNamingStrategy implicitNamingStrategy,
ConfigurableListableBeanFactory beanFactory,
List<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers) {
List<HibernatePropertiesCustomizer> customizers = new ArrayList<>();
if (ClassUtils.isPresent("org.hibernate.resource.beans.container.spi.BeanContainer",
getClass().getClassLoader())) {
customizers.add((properties) -> properties.put(ManagedBeanSettings.BEAN_CONTAINER,
new SpringBeanContainer(beanFactory)));
}
if (physicalNamingStrategy != null || implicitNamingStrategy != null) {
customizers
.add(new NamingStrategiesHibernatePropertiesCustomizer(physicalNamingStrategy, implicitNamingStrategy));
}
customizers.addAll(hibernatePropertiesCustomizers);
return customizers;
}
@Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
this.sqlExceptionTranslator.ifUnique(adapter.getJpaDialect()::setJdbcExceptionTranslator);
return adapter;
}
@Override
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)));
}
@Override
protected void customizeVendorProperties(Map<String, Object> vendorProperties) {
super.customizeVendorProperties(vendorProperties);
if (!vendorProperties.containsKey(JTA_PLATFORM)) {
configureJtaPlatform(vendorProperties);
}
if (!vendorProperties.containsKey(PROVIDER_DISABLES_AUTOCOMMIT)) {
configureProviderDisablesAutocommit(vendorProperties);
}
}
private void configureJtaPlatform(Map<String, Object> vendorProperties) throws LinkageError {
JtaTransactionManager jtaTransactionManager = getJtaTransactionManager();
// Make sure Hibernate doesn't attempt to auto-detect a JTA platform
if (jtaTransactionManager == null) {
vendorProperties.put(JTA_PLATFORM, getNoJtaPlatformManager());
}
// As of Hibernate 5.2, Hibernate can fully integrate with the WebSphere
// transaction manager on its own.
else if (!runningOnWebSphere()) {
configureSpringJtaPlatform(vendorProperties, jtaTransactionManager);
}
}
private void configureProviderDisablesAutocommit(Map<String, Object> vendorProperties) {
if (isDataSourceAutoCommitDisabled() && !isJta()) {
vendorProperties.put(PROVIDER_DISABLES_AUTOCOMMIT, "true");
}
}
private boolean isDataSourceAutoCommitDisabled() {
DataSourcePoolMetadata poolMetadata = this.poolMetadataProvider.getDataSourcePoolMetadata(getDataSource());
return poolMetadata != null && Boolean.FALSE.equals(poolMetadata.getDefaultAutoCommit());
}
private boolean runningOnWebSphere() {
return ClassUtils.isPresent("com.ibm.websphere.jtaextensions.ExtendedJTATransaction",
getClass().getClassLoader());
}
private void configureSpringJtaPlatform(Map<String, Object> vendorProperties,
JtaTransactionManager jtaTransactionManager) {
try {
vendorProperties.put(JTA_PLATFORM, new SpringJtaPlatform(jtaTransactionManager));
}
catch (LinkageError ex) {
// NoClassDefFoundError can happen if Hibernate 4.2 is used and some
// containers (e.g. JBoss EAP 6) wrap it in the superclass LinkageError
if (!isUsingJndi()) {
throw new IllegalStateException(
"Unable to set Hibernate JTA platform, are you using the correct version of Hibernate?", ex);
}
// Assume that Hibernate will use JNDI
if (logger.isDebugEnabled()) {
logger.debug("Unable to set Hibernate JTA platform : " + ex.getMessage());
}
}
}
private boolean isUsingJndi() {
try {
return JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable();
}
catch (Error ex) {
return false;
}
}
private Object getNoJtaPlatformManager() {
for (String candidate : NO_JTA_PLATFORM_CLASSES) {
try {
return Class.forName(candidate).getDeclaredConstructor().newInstance();
}
catch (Exception ex) {
// Continue searching
}
}
throw new IllegalStateException(
"No available JtaPlatform candidates amongst " + Arrays.toString(NO_JTA_PLATFORM_CLASSES));
}
private static class NamingStrategiesHibernatePropertiesCustomizer implements HibernatePropertiesCustomizer {
private final PhysicalNamingStrategy physicalNamingStrategy;
private final ImplicitNamingStrategy implicitNamingStrategy;
NamingStrategiesHibernatePropertiesCustomizer(PhysicalNamingStrategy physicalNamingStrategy,
ImplicitNamingStrategy implicitNamingStrategy) {
this.physicalNamingStrategy = physicalNamingStrategy;
this.implicitNamingStrategy = implicitNamingStrategy;
}
@Override
public void customize(Map<String, Object> hibernateProperties) {
if (this.physicalNamingStrategy != null) {
hibernateProperties.put("hibernate.physical_naming_strategy", this.physicalNamingStrategy);
}
if (this.implicitNamingStrategy != null) {
hibernateProperties.put("hibernate.implicit_naming_strategy", this.implicitNamingStrategy);
}
}
}
static class HibernateRuntimeHints implements RuntimeHintsRegistrar {
private static final Consumer<Builder> INVOKE_DECLARED_CONSTRUCTORS = TypeHint
.builtWith(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
for (String noJtaPlatformClass : NO_JTA_PLATFORM_CLASSES) {
hints.reflection().registerType(TypeReference.of(noJtaPlatformClass), INVOKE_DECLARED_CONSTRUCTORS);
}
hints.reflection().registerType(SpringImplicitNamingStrategy.class, INVOKE_DECLARED_CONSTRUCTORS);
hints.reflection().registerType(PhysicalNamingStrategySnakeCaseImpl.class, INVOKE_DECLARED_CONSTRUCTORS);
}
}
}

View File

@@ -0,0 +1,171 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure.hibernate;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategySnakeCaseImpl;
import org.hibernate.cfg.MappingSettings;
import org.hibernate.cfg.PersistenceSettings;
import org.hibernate.cfg.SchemaToolingSettings;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jpa.autoconfigure.JpaProperties;
import org.springframework.boot.jpa.hibernate.SpringImplicitNamingStrategy;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Configuration properties for Hibernate.
*
* @author Stephane Nicoll
* @author Chris Bono
* @since 4.0.0
* @see JpaProperties
*/
@ConfigurationProperties("spring.jpa.hibernate")
public class HibernateProperties {
private static final String DISABLED_SCANNER_CLASS = "org.hibernate.boot.archive.scan.internal.DisabledScanner";
private final Naming naming = new Naming();
/**
* DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto" property.
* Defaults to "create-drop" when using an embedded database and no schema manager was
* detected. Otherwise, defaults to "none".
*/
private String ddlAuto;
public String getDdlAuto() {
return this.ddlAuto;
}
public void setDdlAuto(String ddlAuto) {
this.ddlAuto = ddlAuto;
}
public Naming getNaming() {
return this.naming;
}
/**
* Determine the configuration properties for the initialization of the main Hibernate
* EntityManagerFactory based on standard JPA properties and
* {@link HibernateSettings}.
* @param jpaProperties standard JPA properties
* @param settings the settings to apply when determining the configuration properties
* @return the Hibernate properties to use
*/
public Map<String, Object> determineHibernateProperties(Map<String, String> jpaProperties,
HibernateSettings settings) {
Assert.notNull(jpaProperties, "'jpaProperties' must not be null");
Assert.notNull(settings, "'settings' must not be null");
return getAdditionalProperties(jpaProperties, settings);
}
private Map<String, Object> getAdditionalProperties(Map<String, String> existing, HibernateSettings settings) {
Map<String, Object> result = new HashMap<>(existing);
applyScanner(result);
getNaming().applyNamingStrategies(result);
String ddlAuto = determineDdlAuto(existing, settings::getDdlAuto);
if (StringUtils.hasText(ddlAuto) && !"none".equals(ddlAuto)) {
result.put(SchemaToolingSettings.HBM2DDL_AUTO, ddlAuto);
}
else {
result.remove(SchemaToolingSettings.HBM2DDL_AUTO);
}
Collection<HibernatePropertiesCustomizer> customizers = settings.getHibernatePropertiesCustomizers();
if (!ObjectUtils.isEmpty(customizers)) {
customizers.forEach((customizer) -> customizer.customize(result));
}
return result;
}
private void applyScanner(Map<String, Object> result) {
if (!result.containsKey(PersistenceSettings.SCANNER) && ClassUtils.isPresent(DISABLED_SCANNER_CLASS, null)) {
result.put(PersistenceSettings.SCANNER, DISABLED_SCANNER_CLASS);
}
}
private String determineDdlAuto(Map<String, String> existing, Supplier<String> defaultDdlAuto) {
String ddlAuto = existing.get(SchemaToolingSettings.HBM2DDL_AUTO);
if (ddlAuto != null) {
return ddlAuto;
}
if (this.ddlAuto != null) {
return this.ddlAuto;
}
if (existing.get(SchemaToolingSettings.JAKARTA_HBM2DDL_DATABASE_ACTION) != null) {
return null;
}
return defaultDdlAuto.get();
}
public static class Naming {
/**
* Fully qualified name of the implicit naming strategy.
*/
private String implicitStrategy;
/**
* Fully qualified name of the physical naming strategy.
*/
private String physicalStrategy;
public String getImplicitStrategy() {
return this.implicitStrategy;
}
public void setImplicitStrategy(String implicitStrategy) {
this.implicitStrategy = implicitStrategy;
}
public String getPhysicalStrategy() {
return this.physicalStrategy;
}
public void setPhysicalStrategy(String physicalStrategy) {
this.physicalStrategy = physicalStrategy;
}
private void applyNamingStrategies(Map<String, Object> properties) {
applyNamingStrategy(properties, MappingSettings.IMPLICIT_NAMING_STRATEGY, this.implicitStrategy,
SpringImplicitNamingStrategy.class::getName);
applyNamingStrategy(properties, MappingSettings.PHYSICAL_NAMING_STRATEGY, this.physicalStrategy,
PhysicalNamingStrategySnakeCaseImpl.class::getName);
}
private void applyNamingStrategy(Map<String, Object> properties, String key, Object strategy,
Supplier<String> defaultStrategy) {
if (strategy != null) {
properties.put(key, strategy);
}
else {
properties.computeIfAbsent(key, (k) -> defaultStrategy.get());
}
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure.hibernate;
import java.util.Map;
/**
* Callback interface that can be implemented by beans wishing to customize the Hibernate
* properties before it is used by an auto-configured {@code EntityManagerFactory}.
*
* @author Stephane Nicoll
* @since 4.0.0
*/
@FunctionalInterface
public interface HibernatePropertiesCustomizer {
/**
* Customize the specified JPA vendor properties.
* @param hibernateProperties the JPA vendor properties to customize
*/
void customize(Map<String, Object> hibernateProperties);
}

View File

@@ -0,0 +1,54 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure.hibernate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Supplier;
/**
* Settings to apply when configuring Hibernate.
*
* @author Andy Wilkinson
* @since 4.0.0
*/
public class HibernateSettings {
private Supplier<String> ddlAuto;
private Collection<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers;
public HibernateSettings ddlAuto(Supplier<String> ddlAuto) {
this.ddlAuto = ddlAuto;
return this;
}
public String getDdlAuto() {
return (this.ddlAuto != null) ? this.ddlAuto.get() : null;
}
public HibernateSettings hibernatePropertiesCustomizers(
Collection<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers) {
this.hibernatePropertiesCustomizers = new ArrayList<>(hibernatePropertiesCustomizers);
return this;
}
public Collection<HibernatePropertiesCustomizer> getHibernatePropertiesCustomizers() {
return this.hibernatePropertiesCustomizers;
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2012-2019 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Auto-configuration for JPA and Spring ORM.
*/
package org.springframework.boot.jpa.autoconfigure.hibernate;

View File

@@ -0,0 +1,20 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Base Auto-configuration for JPA and Spring ORM.
*/
package org.springframework.boot.jpa.autoconfigure;

View File

@@ -0,0 +1,43 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.hibernate;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitJoinTableNameSource;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl;
/**
* Hibernate {@link ImplicitNamingStrategy} that follows Spring recommended naming
* conventions. Naming conventions implemented here are identical to
* {@link ImplicitNamingStrategyJpaCompliantImpl} with the exception that join table names
* are of the form
* <code>{owning_physical_table_name}_{association_owning_property_name}</code>.
*
* @author Andy Wilkinson
* @since 4.0.0
*/
public class SpringImplicitNamingStrategy extends ImplicitNamingStrategyJpaCompliantImpl {
@Override
public Identifier determineJoinTableName(ImplicitJoinTableNameSource source) {
String name = source.getOwningPhysicalTableName() + "_"
+ source.getAssociationOwningAttributePath().getProperty();
return toIdentifier(name, source.getBuildingContext());
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.hibernate;
import jakarta.transaction.TransactionManager;
import jakarta.transaction.UserTransaction;
import org.hibernate.engine.transaction.jta.platform.internal.AbstractJtaPlatform;
import org.springframework.transaction.jta.JtaTransactionManager;
import org.springframework.util.Assert;
/**
* Generic Hibernate {@link AbstractJtaPlatform} implementation that simply resolves the
* JTA {@link UserTransaction} and {@link TransactionManager} from the Spring-configured
* {@link JtaTransactionManager} implementation.
*
* @author Josh Long
* @author Phillip Webb
* @since 4.0.0
*/
public class SpringJtaPlatform extends AbstractJtaPlatform {
private static final long serialVersionUID = 1L;
private final JtaTransactionManager transactionManager;
public SpringJtaPlatform(JtaTransactionManager transactionManager) {
Assert.notNull(transactionManager, "'transactionManager' must not be null");
this.transactionManager = transactionManager;
}
@Override
protected TransactionManager locateTransactionManager() {
return this.transactionManager.getTransactionManager();
}
@Override
protected UserTransaction locateUserTransaction() {
return this.transactionManager.getUserTransaction();
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2012-2019 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Hibernate Support classes.
*/
package org.springframework.boot.jpa.hibernate;

View File

@@ -0,0 +1,20 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* JPA Support classes.
*/
package org.springframework.boot.jpa;

View File

@@ -0,0 +1,79 @@
{
"groups": [],
"properties": [
{
"name": "spring.jpa.hibernate.use-new-id-generator-mappings",
"type": "java.lang.Boolean",
"description": "Whether to use Hibernate's newer IdentifierGenerator for AUTO, TABLE and SEQUENCE. This is actually a shortcut for the \"hibernate.id.new_generator_mappings\" property. When not specified will default to \"true\".",
"deprecation": {
"level": "error",
"reason": "Hibernate no longer supports disabling the use of new ID generator mappings."
}
},
{
"name": "spring.jpa.open-in-view",
"defaultValue": true
}
],
"hints": [
{
"name": "spring.jpa.hibernate.ddl-auto",
"values": [
{
"value": "create",
"description": "Create the schema and destroy previous data."
},
{
"value": "create-drop",
"description": "Create and then destroy the schema at the end of the session."
},
{
"value": "create-only",
"description": "Create the schema."
},
{
"value": "drop",
"description": "Drop the schema."
},
{
"value": "none",
"description": "Disable DDL handling."
},
{
"value": "truncate",
"description": "Truncate the tables in the schema."
},
{
"value": "update",
"description": "Update the schema if necessary."
},
{
"value": "validate",
"description": "Validate the schema, make no changes to the database."
}
]
},
{
"name": "spring.jpa.hibernate.naming.implicit-strategy",
"providers": [
{
"name": "class-reference",
"parameters": {
"target": "org.hibernate.boot.model.naming.ImplicitNamingStrategy"
}
}
]
},
{
"name": "spring.jpa.hibernate.naming.physical-strategy",
"providers": [
{
"name": "class-reference",
"parameters": {
"target": "org.hibernate.boot.model.naming.PhysicalNamingStrategy"
}
}
]
}
]
}

View File

@@ -0,0 +1,7 @@
# Database Initializer Detectors
org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector=\
org.springframework.boot.jpa.JpaDatabaseInitializerDetector
# Depends On Database Initialization Detectors
org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector=\
org.springframework.boot.jpa.JpaDependsOnDatabaseInitializationDetector

View File

@@ -0,0 +1 @@
org.springframework.boot.jpa.autoconfigure.hibernate.HibernateJpaAutoConfiguration

View File

@@ -0,0 +1,528 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure;
import java.io.File;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.sql.DataSource;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.metamodel.ManagedType;
import jakarta.persistence.spi.PersistenceUnitInfo;
import org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.jdbc.autoconfigure.DataSourceInitializationAutoConfiguration;
import org.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.jpa.EntityManagerFactoryBuilder;
import org.springframework.boot.jpa.autoconfigure.test.city.City;
import org.springframework.boot.jpa.autoconfigure.test.country.Country;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.testsupport.BuildOutput;
import org.springframework.boot.testsupport.classpath.resources.WithResource;
import org.springframework.boot.transaction.autoconfigure.TransactionAutoConfiguration;
import org.springframework.boot.transaction.autoconfigure.TransactionManagerCustomizationAutoConfiguration;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager;
import org.springframework.orm.jpa.persistenceunit.ManagedClassNameFilter;
import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionManager;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Base for JPA tests and tests for {@link JpaBaseConfiguration}.
*
* @author Phillip Webb
* @author Dave Syer
* @author Stephane Nicoll
* @author Yanming Zhou
*/
public abstract class AbstractJpaAutoConfigurationTests {
private final Class<?> autoConfiguredClass;
private final ApplicationContextRunner contextRunner;
protected AbstractJpaAutoConfigurationTests(Class<?> autoConfiguredClass) {
this.autoConfiguredClass = autoConfiguredClass;
this.contextRunner = new ApplicationContextRunner()
.withPropertyValues("spring.datasource.generate-unique-name=true",
"spring.jta.log-dir=" + new File(new BuildOutput(getClass()).getRootLocation(), "transaction-logs"))
.withUserConfiguration(TestConfiguration.class)
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TransactionAutoConfiguration.class, TransactionManagerCustomizationAutoConfiguration.class,
DataSourceInitializationAutoConfiguration.class, autoConfiguredClass));
}
protected ApplicationContextRunner contextRunner() {
return this.contextRunner;
}
@Test
void notConfiguredIfDataSourceIsNotAvailable() {
new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(this.autoConfiguredClass))
.run(assertJpaIsNotAutoConfigured());
}
@Test
void notConfiguredIfNoSingleDataSourceCandidateIsAvailable() {
new ApplicationContextRunner().withUserConfiguration(TestTwoDataSourcesConfiguration.class)
.withConfiguration(AutoConfigurations.of(this.autoConfiguredClass))
.run(assertJpaIsNotAutoConfigured());
}
protected ContextConsumer<AssertableApplicationContext> assertJpaIsNotAutoConfigured() {
return (context) -> {
assertThat(context).hasNotFailed();
assertThat(context).hasSingleBean(JpaProperties.class);
assertThat(context).doesNotHaveBean(TransactionManager.class);
assertThat(context).doesNotHaveBean(EntityManagerFactory.class);
};
}
@Test
void configuredWithAutoConfiguredDataSource() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(DataSource.class);
assertThat(context).hasSingleBean(JpaTransactionManager.class);
assertThat(context).hasSingleBean(EntityManagerFactory.class);
assertThat(context).hasSingleBean(PersistenceManagedTypes.class);
});
}
@Test
void configuredWithSingleCandidateDataSource() {
this.contextRunner.withUserConfiguration(TestTwoDataSourcesAndPrimaryConfiguration.class).run((context) -> {
assertThat(context).getBeans(DataSource.class).hasSize(2);
assertThat(context).hasSingleBean(JpaTransactionManager.class);
assertThat(context).hasSingleBean(EntityManagerFactory.class);
assertThat(context).hasSingleBean(PersistenceManagedTypes.class);
});
}
@Test
void jpaTransactionManagerTakesPrecedenceOverSimpleDataSourceOne() {
this.contextRunner.withConfiguration(AutoConfigurations.of(DataSourceTransactionManagerAutoConfiguration.class))
.run((context) -> {
assertThat(context).hasSingleBean(DataSource.class);
assertThat(context).hasSingleBean(JpaTransactionManager.class);
assertThat(context).getBean("transactionManager").isInstanceOf(JpaTransactionManager.class);
});
}
@Test
void openEntityManagerInViewInterceptorIsCreated() {
new WebApplicationContextRunner().withPropertyValues("spring.datasource.generate-unique-name=true")
.withUserConfiguration(TestConfiguration.class)
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TransactionAutoConfiguration.class, this.autoConfiguredClass))
.run((context) -> assertThat(context).hasSingleBean(OpenEntityManagerInViewInterceptor.class));
}
@Test
void openEntityManagerInViewInterceptorIsNotRegisteredWhenFilterPresent() {
new WebApplicationContextRunner().withPropertyValues("spring.datasource.generate-unique-name=true")
.withUserConfiguration(TestFilterConfiguration.class)
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TransactionAutoConfiguration.class, this.autoConfiguredClass))
.run((context) -> assertThat(context).doesNotHaveBean(OpenEntityManagerInViewInterceptor.class));
}
@Test
void openEntityManagerInViewInterceptorIsNotRegisteredWhenFilterRegistrationPresent() {
new WebApplicationContextRunner().withPropertyValues("spring.datasource.generate-unique-name=true")
.withUserConfiguration(TestFilterRegistrationConfiguration.class)
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TransactionAutoConfiguration.class, this.autoConfiguredClass))
.run((context) -> assertThat(context).doesNotHaveBean(OpenEntityManagerInViewInterceptor.class));
}
@Test
void openEntityManagerInViewInterceptorAutoConfigurationBacksOffWhenManuallyRegistered() {
new WebApplicationContextRunner().withPropertyValues("spring.datasource.generate-unique-name=true")
.withUserConfiguration(TestInterceptorManualConfiguration.class)
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TransactionAutoConfiguration.class, this.autoConfiguredClass))
.run((context) -> assertThat(context).getBean(OpenEntityManagerInViewInterceptor.class)
.isExactlyInstanceOf(
TestInterceptorManualConfiguration.ManualOpenEntityManagerInViewInterceptor.class));
}
@Test
void openEntityManagerInViewInterceptorIsNotRegisteredWhenExplicitlyOff() {
new WebApplicationContextRunner()
.withPropertyValues("spring.datasource.generate-unique-name=true", "spring.jpa.open-in-view=false")
.withUserConfiguration(TestConfiguration.class)
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TransactionAutoConfiguration.class, this.autoConfiguredClass))
.run((context) -> assertThat(context).doesNotHaveBean(OpenEntityManagerInViewInterceptor.class));
}
@Test
void customJpaProperties() {
this.contextRunner
.withPropertyValues("spring.jpa.properties.a:b", "spring.jpa.properties.a.b:c", "spring.jpa.properties.c:d")
.run((context) -> {
LocalContainerEntityManagerFactoryBean bean = context
.getBean(LocalContainerEntityManagerFactoryBean.class);
Map<String, Object> map = bean.getJpaPropertyMap();
assertThat(map).containsEntry("a", "b");
assertThat(map).containsEntry("c", "d");
assertThat(map).containsEntry("a.b", "c");
});
}
@Test
@WithMetaInfPersistenceXmlResource
void usesManuallyDefinedLocalContainerEntityManagerFactoryBeanUsingBuilder() {
this.contextRunner.withPropertyValues("spring.jpa.properties.a=b")
.withUserConfiguration(TestConfigurationWithEntityManagerFactoryBuilder.class)
.run((context) -> {
LocalContainerEntityManagerFactoryBean factoryBean = context
.getBean(LocalContainerEntityManagerFactoryBean.class);
Map<String, Object> map = factoryBean.getJpaPropertyMap();
assertThat(map).containsEntry("configured", "manually").containsEntry("a", "b");
});
}
@Test
@WithMetaInfPersistenceXmlResource
void usesManuallyDefinedLocalContainerEntityManagerFactoryBeanIfAvailable() {
this.contextRunner.withUserConfiguration(TestConfigurationWithLocalContainerEntityManagerFactoryBean.class)
.run((context) -> {
LocalContainerEntityManagerFactoryBean factoryBean = context
.getBean(LocalContainerEntityManagerFactoryBean.class);
Map<String, Object> map = factoryBean.getJpaPropertyMap();
assertThat(map).containsEntry("configured", "manually");
});
}
@Test
@WithMetaInfPersistenceXmlResource
void usesManuallyDefinedEntityManagerFactoryIfAvailable() {
this.contextRunner.withUserConfiguration(TestConfigurationWithLocalContainerEntityManagerFactoryBean.class)
.run((context) -> {
EntityManagerFactory factoryBean = context.getBean(EntityManagerFactory.class);
Map<String, Object> map = factoryBean.getProperties();
assertThat(map).containsEntry("configured", "manually");
});
}
@Test
void usesManuallyDefinedTransactionManagerBeanIfAvailable() {
this.contextRunner.withUserConfiguration(TestConfigurationWithTransactionManager.class).run((context) -> {
assertThat(context).hasSingleBean(TransactionManager.class);
TransactionManager txManager = context.getBean(TransactionManager.class);
assertThat(txManager).isInstanceOf(CustomJpaTransactionManager.class);
});
}
@Test
void defaultPersistenceManagedTypes() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(PersistenceManagedTypes.class);
EntityManager entityManager = context.getBean(EntityManagerFactory.class).createEntityManager();
assertThat(getManagedJavaTypes(entityManager)).contains(City.class).doesNotContain(Country.class);
});
}
@Test
void customPersistenceManagedTypes() {
this.contextRunner
.withBean(PersistenceManagedTypes.class, () -> PersistenceManagedTypes.of(Country.class.getName()))
.run((context) -> {
assertThat(context).hasSingleBean(PersistenceManagedTypes.class);
EntityManager entityManager = context.getBean(EntityManagerFactory.class).createEntityManager();
assertThat(getManagedJavaTypes(entityManager)).contains(Country.class).doesNotContain(City.class);
});
}
@Test
void customPersistenceUnitManager() {
this.contextRunner.withUserConfiguration(TestConfigurationWithCustomPersistenceUnitManager.class)
.run((context) -> {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = context
.getBean(LocalContainerEntityManagerFactoryBean.class);
assertThat(entityManagerFactoryBean).hasFieldOrPropertyWithValue("persistenceUnitManager",
context.getBean(PersistenceUnitManager.class));
});
}
@Test
void customPersistenceUnitPostProcessors() {
this.contextRunner.withUserConfiguration(TestConfigurationWithCustomPersistenceUnitPostProcessors.class)
.run((context) -> {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = context
.getBean(LocalContainerEntityManagerFactoryBean.class);
PersistenceUnitInfo persistenceUnitInfo = entityManagerFactoryBean.getPersistenceUnitInfo();
assertThat(persistenceUnitInfo).isNotNull();
assertThat(persistenceUnitInfo.getManagedClassNames())
.contains("customized.attribute.converter.class.name");
});
}
@Test
void customManagedClassNameFilter() {
this.contextRunner.withBean(ManagedClassNameFilter.class, () -> (s) -> !s.endsWith("City"))
.withUserConfiguration(AutoConfigurePackageForCountry.class)
.run((context) -> {
EntityManager entityManager = context.getBean(EntityManagerFactory.class).createEntityManager();
assertThat(getManagedJavaTypes(entityManager)).contains(Country.class).doesNotContain(City.class);
});
}
private Class<?>[] getManagedJavaTypes(EntityManager entityManager) {
Set<ManagedType<?>> managedTypes = entityManager.getMetamodel().getManagedTypes();
return managedTypes.stream().map(ManagedType::getJavaType).toArray(Class<?>[]::new);
}
@Configuration(proxyBeanMethods = false)
protected static class TestTwoDataSourcesConfiguration {
@Bean
DataSource firstDataSource() {
return createRandomDataSource();
}
@Bean
DataSource secondDataSource() {
return createRandomDataSource();
}
private DataSource createRandomDataSource() {
String url = "jdbc:h2:mem:init-" + UUID.randomUUID();
return DataSourceBuilder.create().url(url).build();
}
}
@Configuration(proxyBeanMethods = false)
static class TestTwoDataSourcesAndPrimaryConfiguration {
@Bean
@Primary
DataSource firstDataSource() {
return createRandomDataSource();
}
@Bean
DataSource secondDataSource() {
return createRandomDataSource();
}
private DataSource createRandomDataSource() {
String url = "jdbc:h2:mem:init-" + UUID.randomUUID();
return DataSourceBuilder.create().url(url).build();
}
}
@Configuration(proxyBeanMethods = false)
@TestAutoConfigurationPackage(City.class)
protected static class TestConfiguration {
}
@Configuration(proxyBeanMethods = false)
@TestAutoConfigurationPackage(City.class)
static class TestFilterConfiguration {
@Bean
OpenEntityManagerInViewFilter openEntityManagerInViewFilter() {
return new OpenEntityManagerInViewFilter();
}
}
@Configuration(proxyBeanMethods = false)
@TestAutoConfigurationPackage(City.class)
static class TestFilterRegistrationConfiguration {
@Bean
FilterRegistrationBean<OpenEntityManagerInViewFilter> openEntityManagerInViewFilterFilterRegistrationBean() {
return new FilterRegistrationBean<>();
}
}
@Configuration(proxyBeanMethods = false)
@TestAutoConfigurationPackage(City.class)
static class TestInterceptorManualConfiguration {
@Bean
OpenEntityManagerInViewInterceptor openEntityManagerInViewInterceptor() {
return new ManualOpenEntityManagerInViewInterceptor();
}
static class ManualOpenEntityManagerInViewInterceptor extends OpenEntityManagerInViewInterceptor {
}
}
@Configuration(proxyBeanMethods = false)
static class TestConfigurationWithEntityManagerFactoryBuilder extends TestConfiguration {
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(EntityManagerFactoryBuilder builder,
DataSource dataSource) {
return builder.dataSource(dataSource).properties(Map.of("configured", "manually")).build();
}
}
@Configuration(proxyBeanMethods = false)
static class TestConfigurationWithLocalContainerEntityManagerFactoryBean extends TestConfiguration {
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, JpaVendorAdapter adapter) {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setJpaVendorAdapter(adapter);
factoryBean.setDataSource(dataSource);
factoryBean.setPersistenceUnitName("manually-configured");
Map<String, Object> properties = new HashMap<>();
properties.put("configured", "manually");
properties.put("hibernate.transaction.jta.platform", NoJtaPlatform.INSTANCE);
factoryBean.setJpaPropertyMap(properties);
return factoryBean;
}
}
@Configuration(proxyBeanMethods = false)
static class TestConfigurationWithEntityManagerFactory extends TestConfiguration {
@Bean
EntityManagerFactory entityManagerFactory(DataSource dataSource, JpaVendorAdapter adapter) {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setJpaVendorAdapter(adapter);
factoryBean.setDataSource(dataSource);
factoryBean.setPersistenceUnitName("manually-configured");
Map<String, Object> properties = new HashMap<>();
properties.put("configured", "manually");
properties.put("hibernate.transaction.jta.platform", NoJtaPlatform.INSTANCE);
factoryBean.setJpaPropertyMap(properties);
factoryBean.afterPropertiesSet();
return factoryBean.getObject();
}
@Bean
PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
}
@Configuration(proxyBeanMethods = false)
@TestAutoConfigurationPackage(City.class)
static class TestConfigurationWithTransactionManager {
@Bean
TransactionManager testTransactionManager() {
return new CustomJpaTransactionManager();
}
}
@Configuration(proxyBeanMethods = false)
@TestAutoConfigurationPackage(Country.class)
static class AutoConfigurePackageForCountry {
}
@Configuration(proxyBeanMethods = false)
@TestAutoConfigurationPackage(AbstractJpaAutoConfigurationTests.class)
static class TestConfigurationWithCustomPersistenceUnitManager {
private final DataSource dataSource;
TestConfigurationWithCustomPersistenceUnitManager(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
PersistenceUnitManager persistenceUnitManager() {
DefaultPersistenceUnitManager persistenceUnitManager = new DefaultPersistenceUnitManager();
persistenceUnitManager.setDefaultDataSource(this.dataSource);
persistenceUnitManager.setPackagesToScan(City.class.getPackage().getName());
return persistenceUnitManager;
}
}
@Configuration(proxyBeanMethods = false)
@TestAutoConfigurationPackage(AbstractJpaAutoConfigurationTests.class)
static class TestConfigurationWithCustomPersistenceUnitPostProcessors {
@Bean
EntityManagerFactoryBuilderCustomizer entityManagerFactoryBuilderCustomizer() {
return (builder) -> builder.setPersistenceUnitPostProcessors(
(pui) -> pui.addManagedClassName("customized.attribute.converter.class.name"));
}
}
static class CustomJpaTransactionManager extends JpaTransactionManager {
}
@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.jpa.autoconfigure.test.city.City</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
</persistence>
""")
protected @interface WithMetaInfPersistenceXmlResource {
}
}

View File

@@ -0,0 +1,173 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure.hibernate;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Map;
import javax.sql.DataSource;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.jpa.autoconfigure.JpaProperties;
import org.springframework.boot.jpa.autoconfigure.test.city.City;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Additional tests for {@link HibernateJpaAutoConfiguration}.
*
* @author Dave Syer
* @author Phillip Webb
* @author Eddú Meléndez
* @author Stephane Nicoll
*/
class CustomHibernateJpaAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withPropertyValues("spring.datasource.generate-unique-name=true")
.withUserConfiguration(TestConfiguration.class)
.withConfiguration(
AutoConfigurations.of(DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class));
@Test
void namingStrategyDelegatorTakesPrecedence() {
this.contextRunner
.withPropertyValues("spring.jpa.properties.hibernate.ejb.naming_strategy_delegator:"
+ "org.hibernate.cfg.naming.ImprovedNamingStrategyDelegator")
.run((context) -> {
JpaProperties jpaProperties = context.getBean(JpaProperties.class);
HibernateProperties hibernateProperties = context.getBean(HibernateProperties.class);
Map<String, Object> properties = hibernateProperties
.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
assertThat(properties).doesNotContainKey("hibernate.ejb.naming_strategy");
});
}
@Test
void namingStrategyBeansAreUsed() {
this.contextRunner.withUserConfiguration(NamingStrategyConfiguration.class)
.withPropertyValues("spring.datasource.url:jdbc:h2:mem:naming-strategy-beans")
.run((context) -> {
HibernateJpaConfiguration jpaConfiguration = context.getBean(HibernateJpaConfiguration.class);
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",
NamingStrategyConfiguration.physicalNamingStrategy);
});
}
@Test
void hibernatePropertiesCustomizersAreAppliedInOrder() {
this.contextRunner.withUserConfiguration(HibernatePropertiesCustomizerConfiguration.class).run((context) -> {
HibernateJpaConfiguration jpaConfiguration = context.getBean(HibernateJpaConfiguration.class);
Map<String, Object> hibernateProperties = jpaConfiguration
.getVendorProperties(context.getBean(DataSource.class));
assertThat(hibernateProperties).containsEntry("test.counter", 2);
});
}
@Test
void defaultDatabaseIsSet() {
this.contextRunner.withPropertyValues("spring.datasource.url:jdbc:h2:mem:testdb").run((context) -> {
HibernateJpaVendorAdapter bean = context.getBean(HibernateJpaVendorAdapter.class);
Database database = (Database) ReflectionTestUtils.getField(bean, "database");
assertThat(database).isEqualTo(Database.DEFAULT);
});
}
@Configuration(proxyBeanMethods = false)
@TestAutoConfigurationPackage(City.class)
static class TestConfiguration {
}
@Configuration(proxyBeanMethods = false)
static class MockDataSourceConfiguration {
@Bean
DataSource dataSource() {
DataSource dataSource = mock(DataSource.class);
try {
given(dataSource.getConnection()).willReturn(mock(Connection.class));
given(dataSource.getConnection().getMetaData()).willReturn(mock(DatabaseMetaData.class));
}
catch (SQLException ex) {
// Do nothing
}
return dataSource;
}
}
@Configuration(proxyBeanMethods = false)
static class NamingStrategyConfiguration {
static final ImplicitNamingStrategy implicitNamingStrategy = new ImplicitNamingStrategyJpaCompliantImpl();
static final PhysicalNamingStrategy physicalNamingStrategy = new PhysicalNamingStrategyStandardImpl();
@Bean
ImplicitNamingStrategy implicitNamingStrategy() {
return implicitNamingStrategy;
}
@Bean
PhysicalNamingStrategy physicalNamingStrategy() {
return physicalNamingStrategy;
}
}
@Configuration(proxyBeanMethods = false)
static class HibernatePropertiesCustomizerConfiguration {
@Bean
@Order(2)
HibernatePropertiesCustomizer sampleCustomizer() {
return ((hibernateProperties) -> hibernateProperties.put("test.counter", 2));
}
@Bean
@Order(1)
HibernatePropertiesCustomizer anotherCustomizer() {
return ((hibernateProperties) -> hibernateProperties.put("test.counter", 1));
}
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure.hibernate;
import javax.cache.CacheManager;
import javax.cache.Caching;
import org.ehcache.jsr107.EhcacheCachingProvider;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for Hibernate 2nd level cache with jcache.
*
* @author Stephane Nicoll
*/
class Hibernate2ndLevelCacheIntegrationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class))
.withUserConfiguration(TestConfiguration.class);
@Test
void hibernate2ndLevelCacheWithJCacheAndEhCache() {
String cachingProviderFqn = EhcacheCachingProvider.class.getName();
this.contextRunner
.withPropertyValues("spring.cache.type=jcache", "spring.cache.jcache.provider=" + cachingProviderFqn,
"spring.jpa.properties.hibernate.cache.region.factory_class=jcache",
"spring.jpa.properties.hibernate.cache.provider=" + cachingProviderFqn)
.run((context) -> assertThat(context).hasNotFailed());
}
@Configuration(proxyBeanMethods = false)
@EnableCaching
static class TestConfiguration {
@Bean
CacheManager cacheManager() {
return Caching.getCachingProvider(EhcacheCachingProvider.class.getName()).getCacheManager();
}
@Bean
JCacheCacheManager jcacheCacheManager(CacheManager cacheManager) {
return new JCacheCacheManager(cacheManager);
}
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure.hibernate;
import java.util.Collections;
import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.jdbc.SchemaManagement;
import org.springframework.boot.jdbc.SchemaManagementProvider;
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link HibernateDefaultDdlAutoProvider}.
*
* @author Stephane Nicoll
*/
class HibernateDefaultDdlAutoProviderTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class))
.withPropertyValues("spring.sql.init.mode:never");
@Test
void defaultDDlAutoForEmbedded() {
this.contextRunner.run((context) -> {
HibernateDefaultDdlAutoProvider ddlAutoProvider = new HibernateDefaultDdlAutoProvider(
Collections.emptyList());
assertThat(ddlAutoProvider.getDefaultDdlAuto(context.getBean(DataSource.class))).isEqualTo("create-drop");
});
}
@Test
void defaultDDlAutoForEmbeddedWithPositiveContributor() {
this.contextRunner.run((context) -> {
DataSource dataSource = context.getBean(DataSource.class);
SchemaManagementProvider provider = mock(SchemaManagementProvider.class);
given(provider.getSchemaManagement(dataSource)).willReturn(SchemaManagement.MANAGED);
HibernateDefaultDdlAutoProvider ddlAutoProvider = new HibernateDefaultDdlAutoProvider(
Collections.singletonList(provider));
assertThat(ddlAutoProvider.getDefaultDdlAuto(dataSource)).isEqualTo("none");
});
}
@Test
void defaultDDlAutoForEmbeddedWithNegativeContributor() {
this.contextRunner.run((context) -> {
DataSource dataSource = context.getBean(DataSource.class);
SchemaManagementProvider provider = mock(SchemaManagementProvider.class);
given(provider.getSchemaManagement(dataSource)).willReturn(SchemaManagement.UNMANAGED);
HibernateDefaultDdlAutoProvider ddlAutoProvider = new HibernateDefaultDdlAutoProvider(
Collections.singletonList(provider));
assertThat(ddlAutoProvider.getDefaultDdlAuto(dataSource)).isEqualTo("create-drop");
});
}
}

View File

@@ -0,0 +1,898 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure.hibernate;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.transaction.Synchronization;
import jakarta.transaction.Transaction;
import jakarta.transaction.TransactionManager;
import jakarta.transaction.UserTransaction;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.boot.model.naming.PhysicalNamingStrategySnakeCaseImpl;
import org.hibernate.cfg.ManagedBeanSettings;
import org.hibernate.cfg.SchemaToolingSettings;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.TypeReference;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
import org.springframework.boot.flyway.autoconfigure.FlywayAutoConfiguration;
import org.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.jdbc.autoconfigure.XADataSourceAutoConfiguration;
import org.springframework.boot.jpa.autoconfigure.AbstractJpaAutoConfigurationTests;
import org.springframework.boot.jpa.autoconfigure.EntityManagerFactoryBuilderCustomizer;
import org.springframework.boot.jpa.autoconfigure.hibernate.HibernateJpaAutoConfigurationTests.JpaUsingApplicationListenerConfiguration.EventCapturingApplicationListener;
import org.springframework.boot.jpa.autoconfigure.hibernate.HibernateJpaConfiguration.HibernateRuntimeHints;
import org.springframework.boot.jpa.autoconfigure.hibernate.mapping.NonAnnotatedEntity;
import org.springframework.boot.jpa.autoconfigure.test.city.City;
import org.springframework.boot.jpa.hibernate.SpringImplicitNamingStrategy;
import org.springframework.boot.jpa.hibernate.SpringJtaPlatform;
import org.springframework.boot.liquibase.autoconfigure.LiquibaseAutoConfiguration;
import org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitialization;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.boot.testsupport.classpath.resources.WithResource;
import org.springframework.boot.transaction.jta.autoconfigure.JtaAutoConfiguration;
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;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.transaction.jta.JtaTransactionManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.assertj.core.api.Assertions.entry;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link HibernateJpaAutoConfiguration}.
*
* @author Dave Syer
* @author Phillip Webb
* @author Andy Wilkinson
* @author Kazuki Shimizu
* @author Stephane Nicoll
* @author Chris Bono
* @author Moritz Halbritter
*/
class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTests {
HibernateJpaAutoConfigurationTests() {
super(HibernateJpaAutoConfiguration.class);
}
@Test
void testDmlScriptWithMissingDdl() {
contextRunner().withPropertyValues("spring.sql.init.data-locations:classpath:/city.sql",
// Missing:
"spring.sql.init.schema-locations:classpath:/ddl.sql")
.run((context) -> {
assertThat(context).hasFailed();
assertThat(context.getStartupFailure()).hasMessageContaining("ddl.sql");
});
}
@Test
void testDmlScript() {
// This can't succeed because the data SQL is executed immediately after the
// schema and Hibernate hasn't initialized yet at that point
contextRunner().withPropertyValues("spring.sql.init.data-locations:/city.sql").run((context) -> {
assertThat(context).hasFailed();
assertThat(context.getStartupFailure()).isInstanceOf(BeanCreationException.class);
});
}
@Test
@WithResource(name = "city.sql",
content = "INSERT INTO CITY (ID, NAME, STATE, COUNTRY, MAP) values (2000, 'Washington', 'DC', 'US', 'Google')")
void testDmlScriptRunsEarly() {
contextRunner().withUserConfiguration(TestInitializedJpaConfiguration.class)
.withClassLoader(new HideDataScriptClassLoader())
.withPropertyValues("spring.jpa.show-sql=true", "spring.jpa.properties.hibernate.format_sql=true",
"spring.jpa.properties.hibernate.highlight_sql=true", "spring.jpa.hibernate.ddl-auto:create-drop",
"spring.sql.init.data-locations:/city.sql", "spring.jpa.defer-datasource-initialization=true")
.run((context) -> assertThat(context.getBean(TestInitializedJpaConfiguration.class).called).isTrue());
}
@Test
@WithResource(name = "db/city/V1__init.sql", content = """
CREATE SEQUENCE city_seq INCREMENT BY 50;
CREATE TABLE CITY (
id BIGINT GENERATED BY DEFAULT AS IDENTITY,
name VARCHAR(30),
state VARCHAR(30),
country VARCHAR(30),
map VARCHAR(30)
);
""")
void testFlywaySwitchOffDdlAuto() {
contextRunner().withPropertyValues("spring.sql.init.mode:never", "spring.flyway.locations:classpath:db/city")
.withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class))
.run((context) -> assertThat(context).hasNotFailed());
}
@Test
@WithResource(name = "db/city/V1__init.sql", content = """
CREATE SEQUENCE city_seq INCREMENT BY 50;
CREATE TABLE CITY (
id BIGINT GENERATED BY DEFAULT AS IDENTITY,
name VARCHAR(30),
state VARCHAR(30),
country VARCHAR(30),
map VARCHAR(30)
);
""")
void testFlywayPlusValidation() {
contextRunner()
.withPropertyValues("spring.sql.init.mode:never", "spring.flyway.locations:classpath:db/city",
"spring.jpa.hibernate.ddl-auto:validate")
.withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class))
.run((context) -> assertThat(context).hasNotFailed());
}
@Test
@WithResource(name = "db/changelog/db.changelog-city.yaml", content = """
databaseChangeLog:
- changeSet:
id: 1
author: dsyer
changes:
- createSequence:
sequenceName: city_seq
incrementBy: 50
- createTable:
tableName: city
columns:
- column:
name: id
type: bigint
autoIncrement: true
constraints:
primaryKey: true
nullable: false
- column:
name: name
type: varchar(50)
constraints:
nullable: false
- column:
name: state
type: varchar(50)
constraints:
nullable: false
- column:
name: country
type: varchar(50)
constraints:
nullable: false
- column:
name: map
type: varchar(50)
constraints:
nullable: true
""")
void testLiquibasePlusValidation() {
contextRunner()
.withPropertyValues("spring.liquibase.change-log:classpath:db/changelog/db.changelog-city.yaml",
"spring.jpa.hibernate.ddl-auto:validate")
.withConfiguration(AutoConfigurations.of(LiquibaseAutoConfiguration.class))
.run((context) -> assertThat(context).hasNotFailed());
}
@Test
void hibernateDialectIsNotSetByDefault() {
contextRunner().run(assertJpaVendorAdapter(
(adapter) -> assertThat(adapter.getJpaPropertyMap()).doesNotContainKeys("hibernate.dialect")));
}
@Test
void shouldConfigureHibernateJpaDialectWithSqlExceptionTranslatorIfPresent() {
SQLStateSQLExceptionTranslator sqlExceptionTranslator = new SQLStateSQLExceptionTranslator();
contextRunner().withBean(SQLStateSQLExceptionTranslator.class, () -> sqlExceptionTranslator)
.run(assertJpaVendorAdapter((adapter) -> assertThat(adapter.getJpaDialect())
.hasFieldOrPropertyWithValue("jdbcExceptionTranslator", sqlExceptionTranslator)));
}
@Test
void shouldNotConfigureHibernateJpaDialectWithSqlExceptionTranslatorIfNotUnique() {
SQLStateSQLExceptionTranslator sqlExceptionTranslator1 = new SQLStateSQLExceptionTranslator();
SQLStateSQLExceptionTranslator sqlExceptionTranslator2 = new SQLStateSQLExceptionTranslator();
contextRunner().withBean("sqlExceptionTranslator1", SQLExceptionTranslator.class, () -> sqlExceptionTranslator1)
.withBean("sqlExceptionTranslator2", SQLExceptionTranslator.class, () -> sqlExceptionTranslator2)
.run(assertJpaVendorAdapter((adapter) -> assertThat(adapter.getJpaDialect())
.hasFieldOrPropertyWithValue("jdbcExceptionTranslator", null)));
}
@Test
void hibernateDialectIsSetWhenDatabaseIsSet() {
contextRunner().withPropertyValues("spring.jpa.database=H2")
.run(assertJpaVendorAdapter((adapter) -> assertThat(adapter.getJpaPropertyMap())
.contains(entry("hibernate.dialect", H2Dialect.class.getName()))));
}
@Test
void hibernateDialectIsSetWhenDatabasePlatformIsSet() {
String databasePlatform = TestH2Dialect.class.getName();
contextRunner().withPropertyValues("spring.jpa.database-platform=" + databasePlatform)
.run(assertJpaVendorAdapter((adapter) -> assertThat(adapter.getJpaPropertyMap())
.contains(entry("hibernate.dialect", databasePlatform))));
}
private ContextConsumer<AssertableApplicationContext> assertJpaVendorAdapter(
Consumer<HibernateJpaVendorAdapter> adapter) {
return (context) -> {
assertThat(context).hasSingleBean(JpaVendorAdapter.class);
assertThat(context).hasSingleBean(HibernateJpaVendorAdapter.class);
adapter.accept(context.getBean(HibernateJpaVendorAdapter.class));
};
}
@Test
void jtaDefaultPlatform() {
contextRunner().withUserConfiguration(JtaTransactionManagerConfiguration.class)
.run(assertJtaPlatform(SpringJtaPlatform.class));
}
@Test
void jtaCustomPlatform() {
contextRunner()
.withPropertyValues(
"spring.jpa.properties.hibernate.transaction.jta.platform:" + TestJtaPlatform.class.getName())
.withConfiguration(AutoConfigurations.of(JtaAutoConfiguration.class))
.run(assertJtaPlatform(TestJtaPlatform.class));
}
@Test
void jtaNotUsedByTheApplication() {
contextRunner().run(assertJtaPlatform(NoJtaPlatform.class));
}
private ContextConsumer<AssertableApplicationContext> assertJtaPlatform(Class<? extends JtaPlatform> expectedType) {
return (context) -> {
SessionFactoryImpl sessionFactory = context.getBean(LocalContainerEntityManagerFactoryBean.class)
.getNativeEntityManagerFactory()
.unwrap(SessionFactoryImpl.class);
assertThat(sessionFactory.getServiceRegistry().getService(JtaPlatform.class)).isInstanceOf(expectedType);
};
}
@Test
void jtaCustomTransactionManagerUsingProperties() {
contextRunner()
.withPropertyValues("spring.transaction.default-timeout:30",
"spring.transaction.rollback-on-commit-failure:true")
.run((context) -> {
JpaTransactionManager transactionManager = context.getBean(JpaTransactionManager.class);
assertThat(transactionManager.getDefaultTimeout()).isEqualTo(30);
assertThat(transactionManager.isRollbackOnCommitFailure()).isTrue();
});
}
@Test
void autoConfigurationBacksOffWithSeveralDataSources() {
contextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceTransactionManagerAutoConfiguration.class,
XADataSourceAutoConfiguration.class, JtaAutoConfiguration.class))
.withUserConfiguration(TestTwoDataSourcesConfiguration.class)
.run((context) -> {
assertThat(context).hasNotFailed();
assertThat(context).doesNotHaveBean(EntityManagerFactory.class);
});
}
@Test
void providerDisablesAutoCommitIsConfigured() {
contextRunner()
.withPropertyValues("spring.datasource.type:" + HikariDataSource.class.getName(),
"spring.datasource.hikari.auto-commit:false")
.run((context) -> {
Map<String, Object> jpaProperties = context.getBean(LocalContainerEntityManagerFactoryBean.class)
.getJpaPropertyMap();
assertThat(jpaProperties).contains(entry("hibernate.connection.provider_disables_autocommit", "true"));
});
}
@Test
void providerDisablesAutoCommitIsNotConfiguredIfAutoCommitIsEnabled() {
contextRunner()
.withPropertyValues("spring.datasource.type:" + HikariDataSource.class.getName(),
"spring.datasource.hikari.auto-commit:true")
.run((context) -> {
Map<String, Object> jpaProperties = context.getBean(LocalContainerEntityManagerFactoryBean.class)
.getJpaPropertyMap();
assertThat(jpaProperties).doesNotContainKeys("hibernate.connection.provider_disables_autocommit");
});
}
@Test
void providerDisablesAutoCommitIsNotConfiguredIfPropertyIsSet() {
contextRunner()
.withPropertyValues("spring.datasource.type:" + HikariDataSource.class.getName(),
"spring.datasource.hikari.auto-commit:false",
"spring.jpa.properties.hibernate.connection.provider_disables_autocommit=false")
.run((context) -> {
Map<String, Object> jpaProperties = context.getBean(LocalContainerEntityManagerFactoryBean.class)
.getJpaPropertyMap();
assertThat(jpaProperties).contains(entry("hibernate.connection.provider_disables_autocommit", "false"));
});
}
@Test
void providerDisablesAutoCommitIsNotConfiguredWithJta() {
contextRunner().withUserConfiguration(JtaTransactionManagerConfiguration.class)
.withPropertyValues("spring.datasource.type:" + HikariDataSource.class.getName(),
"spring.datasource.hikari.auto-commit:false")
.run((context) -> {
Map<String, Object> jpaProperties = context.getBean(LocalContainerEntityManagerFactoryBean.class)
.getJpaPropertyMap();
assertThat(jpaProperties).doesNotContainKeys("hibernate.connection.provider_disables_autocommit");
});
}
@Test
@WithResource(name = "META-INF/mappings/non-annotated.xml",
content = """
<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm https://www.oracle.com/webfolder/technetwork/jsc/xml/ns/persistence/orm_2_1.xsd"
version="2.1">
<entity class="org.springframework.boot.jpa.autoconfigure.hibernate.mapping.NonAnnotatedEntity">
<table name="NON_ANNOTATED"/>
<attributes>
<id name="id">
<column name="id"/>
<generated-value strategy="IDENTITY"/>
</id>
<basic name="item">
<column name="item"/>
</basic>
</attributes>
</entity>
</entity-mappings>
""")
@WithResource(name = "non-annotated-data.sql",
content = "INSERT INTO NON_ANNOTATED (id, item) values (2000, 'Test');")
void customResourceMapping() {
contextRunner().withClassLoader(new HideDataScriptClassLoader())
.withPropertyValues("spring.sql.init.data-locations:classpath:non-annotated-data.sql",
"spring.jpa.mapping-resources=META-INF/mappings/non-annotated.xml",
"spring.jpa.defer-datasource-initialization=true")
.run((context) -> {
EntityManager em = context.getBean(EntityManagerFactory.class).createEntityManager();
NonAnnotatedEntity found = em.find(NonAnnotatedEntity.class, 2000L);
assertThat(found).isNotNull();
assertThat(found.getItem()).isEqualTo("Test");
});
}
@Test
void physicalNamingStrategyCanBeUsed() {
contextRunner().withUserConfiguration(TestPhysicalNamingStrategyConfiguration.class).run((context) -> {
Map<String, Object> hibernateProperties = getVendorProperties(context);
assertThat(hibernateProperties)
.contains(entry("hibernate.physical_naming_strategy", context.getBean("testPhysicalNamingStrategy")));
assertThat(hibernateProperties).doesNotContainKeys("hibernate.ejb.naming_strategy");
});
}
@Test
void implicitNamingStrategyCanBeUsed() {
contextRunner().withUserConfiguration(TestImplicitNamingStrategyConfiguration.class).run((context) -> {
Map<String, Object> hibernateProperties = getVendorProperties(context);
assertThat(hibernateProperties)
.contains(entry("hibernate.implicit_naming_strategy", context.getBean("testImplicitNamingStrategy")));
assertThat(hibernateProperties).doesNotContainKeys("hibernate.ejb.naming_strategy");
});
}
@Test
void namingStrategyInstancesTakePrecedenceOverNamingStrategyProperties() {
contextRunner()
.withUserConfiguration(TestPhysicalNamingStrategyConfiguration.class,
TestImplicitNamingStrategyConfiguration.class)
.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 = getVendorProperties(context);
assertThat(hibernateProperties).contains(
entry("hibernate.physical_naming_strategy", context.getBean("testPhysicalNamingStrategy")),
entry("hibernate.implicit_naming_strategy", context.getBean("testImplicitNamingStrategy")));
assertThat(hibernateProperties).doesNotContainKeys("hibernate.ejb.naming_strategy");
});
}
@Test
void hibernatePropertiesCustomizerTakesPrecedenceOverStrategyInstancesAndNamingStrategyProperties() {
contextRunner()
.withUserConfiguration(TestHibernatePropertiesCustomizerConfiguration.class,
TestPhysicalNamingStrategyConfiguration.class, TestImplicitNamingStrategyConfiguration.class)
.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 = getVendorProperties(context);
TestHibernatePropertiesCustomizerConfiguration configuration = context
.getBean(TestHibernatePropertiesCustomizerConfiguration.class);
assertThat(hibernateProperties).contains(
entry("hibernate.physical_naming_strategy", configuration.physicalNamingStrategy),
entry("hibernate.implicit_naming_strategy", configuration.implicitNamingStrategy));
assertThat(hibernateProperties).doesNotContainKeys("hibernate.ejb.naming_strategy");
});
}
@Test
@WithResource(name = "city.sql",
content = "INSERT INTO CITY (ID, NAME, STATE, COUNTRY, MAP) values (2000, 'Washington', 'DC', 'US', 'Google')")
void eventListenerCanBeRegisteredAsBeans() {
contextRunner().withUserConfiguration(TestInitializedJpaConfiguration.class)
.withClassLoader(new HideDataScriptClassLoader())
.withPropertyValues("spring.jpa.show-sql=true", "spring.jpa.hibernate.ddl-auto:create-drop",
"spring.sql.init.data-locations:classpath:/city.sql",
"spring.jpa.defer-datasource-initialization=true")
.run((context) -> {
// See CityListener
assertThat(context).hasSingleBean(City.class);
assertThat(context.getBean(City.class).getName()).isEqualTo("Washington");
});
}
@Test
void hibernatePropertiesCustomizerCanDisableBeanContainer() {
contextRunner().withUserConfiguration(DisableBeanContainerConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(City.class));
}
@Test
void vendorPropertiesWithEmbeddedDatabaseAndNoDdlProperty() {
contextRunner().run(vendorProperties((vendorProperties) -> {
assertThat(vendorProperties).doesNotContainKeys(SchemaToolingSettings.JAKARTA_HBM2DDL_DATABASE_ACTION);
assertThat(vendorProperties).containsEntry(SchemaToolingSettings.HBM2DDL_AUTO, "create-drop");
}));
}
@Test
void vendorPropertiesWhenDdlAutoPropertyIsSet() {
contextRunner().withPropertyValues("spring.jpa.hibernate.ddl-auto=update")
.run(vendorProperties((vendorProperties) -> {
assertThat(vendorProperties).doesNotContainKeys(SchemaToolingSettings.JAKARTA_HBM2DDL_DATABASE_ACTION);
assertThat(vendorProperties).containsEntry(SchemaToolingSettings.HBM2DDL_AUTO, "update");
}));
}
@Test
void vendorPropertiesWhenDdlAutoPropertyAndHibernatePropertiesAreSet() {
contextRunner()
.withPropertyValues("spring.jpa.hibernate.ddl-auto=update",
"spring.jpa.properties.hibernate.hbm2ddl.auto=create-drop")
.run(vendorProperties((vendorProperties) -> {
assertThat(vendorProperties).doesNotContainKeys(SchemaToolingSettings.JAKARTA_HBM2DDL_DATABASE_ACTION);
assertThat(vendorProperties).containsEntry(SchemaToolingSettings.HBM2DDL_AUTO, "create-drop");
}));
}
@Test
void vendorPropertiesWhenDdlAutoPropertyIsSetToNone() {
contextRunner().withPropertyValues("spring.jpa.hibernate.ddl-auto=none")
.run(vendorProperties((vendorProperties) -> assertThat(vendorProperties).doesNotContainKeys(
SchemaToolingSettings.JAKARTA_HBM2DDL_DATABASE_ACTION, SchemaToolingSettings.HBM2DDL_AUTO)));
}
@Test
void vendorPropertiesWhenJpaDdlActionIsSet() {
contextRunner()
.withPropertyValues("spring.jpa.properties.jakarta.persistence.schema-generation.database.action=create")
.run(vendorProperties((vendorProperties) -> {
assertThat(vendorProperties).containsEntry(SchemaToolingSettings.JAKARTA_HBM2DDL_DATABASE_ACTION,
"create");
assertThat(vendorProperties).doesNotContainKeys(SchemaToolingSettings.HBM2DDL_AUTO);
}));
}
@Test
void vendorPropertiesWhenBothDdlAutoPropertiesAreSet() {
contextRunner()
.withPropertyValues("spring.jpa.properties.jakarta.persistence.schema-generation.database.action=create",
"spring.jpa.hibernate.ddl-auto=create-only")
.run(vendorProperties((vendorProperties) -> {
assertThat(vendorProperties).containsEntry(SchemaToolingSettings.JAKARTA_HBM2DDL_DATABASE_ACTION,
"create");
assertThat(vendorProperties).containsEntry(SchemaToolingSettings.HBM2DDL_AUTO, "create-only");
}));
}
private ContextConsumer<AssertableApplicationContext> vendorProperties(
Consumer<Map<String, Object>> vendorProperties) {
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
void withSyncBootstrappingAnApplicationListenerThatUsesJpaDoesNotTriggerABeanCurrentlyInCreationException() {
contextRunner().withUserConfiguration(JpaUsingApplicationListenerConfiguration.class).run((context) -> {
assertThat(context).hasNotFailed();
EventCapturingApplicationListener listener = context.getBean(EventCapturingApplicationListener.class);
assertThat(listener.events).hasSize(1);
assertThat(listener.events).hasOnlyElementsOfType(ContextRefreshedEvent.class);
});
}
@Test
void withAsyncBootstrappingAnApplicationListenerThatUsesJpaDoesNotTriggerABeanCurrentlyInCreationException() {
contextRunner()
.withUserConfiguration(AsyncBootstrappingConfiguration.class,
JpaUsingApplicationListenerConfiguration.class)
.run((context) -> {
assertThat(context).hasNotFailed();
EventCapturingApplicationListener listener = context.getBean(EventCapturingApplicationListener.class);
assertThat(listener.events).hasSize(1);
assertThat(listener.events).hasOnlyElementsOfType(ContextRefreshedEvent.class);
// createEntityManager requires Hibernate bootstrapping to be complete
assertThatNoException()
.isThrownBy(() -> context.getBean(EntityManagerFactory.class).createEntityManager());
});
}
@Test
@WithMetaInfPersistenceXmlResource
void whenLocalContainerEntityManagerFactoryBeanHasNoJpaVendorAdapterAutoConfigurationSucceeds() {
contextRunner()
.withUserConfiguration(
TestConfigurationWithLocalContainerEntityManagerFactoryBeanWithNoJpaVendorAdapter.class)
.run((context) -> {
EntityManagerFactory factoryBean = context.getBean(EntityManagerFactory.class);
Map<String, Object> map = factoryBean.getProperties();
assertThat(map).containsEntry("configured", "manually");
});
}
@Test
void registersHintsForJtaClasses() {
RuntimeHints hints = new RuntimeHints();
new HibernateRuntimeHints().registerHints(hints, getClass().getClassLoader());
for (String noJtaPlatformClass : Arrays.asList(
"org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform",
"org.hibernate.service.jta.platform.internal.NoJtaPlatform")) {
assertThat(RuntimeHintsPredicates.reflection()
.onType(TypeReference.of(noJtaPlatformClass))
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(hints);
}
}
@Test
void registersHintsForNamingClasses() {
RuntimeHints hints = new RuntimeHints();
new HibernateRuntimeHints().registerHints(hints, getClass().getClassLoader());
for (Class<?> noJtaPlatformClass : Arrays.asList(SpringImplicitNamingStrategy.class,
PhysicalNamingStrategySnakeCaseImpl.class)) {
assertThat(RuntimeHintsPredicates.reflection()
.onType(noJtaPlatformClass)
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(hints);
}
}
@Test
@Disabled("gh-40177")
void whenSpringJpaGenerateDdlIsNotSetThenTableIsNotCreated() {
// spring.jpa.generated-ddl defaults to false but this test still fails because
// we're using an embedded database which means that HibernateProperties defaults
// hibernate.hbm2ddl.auto to create-drop, replacing the
// hibernate.hbm2ddl.auto=none that comes from generate-ddl being false.
contextRunner().run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY"));
}
@Test
void whenSpringJpaGenerateDdlIsTrueThenTableIsCreated() {
contextRunner().withPropertyValues("spring.jpa.generate-ddl=true")
.run((context) -> assertThat(tablesFrom(context)).contains("CITY"));
}
@Test
@Disabled("gh-40177")
void whenSpringJpaGenerateDdlIsFalseThenTableIsNotCreated() {
// This test fails because we're using an embedded database which means that
// HibernateProperties defaults hibernate.hbm2ddl.auto to create-drop, replacing
// the hibernate.hbm2ddl.auto=none that comes from setting generate-ddl to false.
contextRunner().withPropertyValues("spring.jpa.generate-ddl=false")
.run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY"));
}
@Test
void whenHbm2DdlAutoIsNoneThenTableIsNotCreated() {
contextRunner().withPropertyValues("spring.jpa.properties.hibernate.hbm2ddl.auto=none")
.run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY"));
}
@Test
void whenSpringJpaHibernateDdlAutoIsNoneThenTableIsNotCreated() {
contextRunner().withPropertyValues("spring.jpa.hibernate.ddl-auto=none")
.run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY"));
}
@Test
@Disabled("gh-40177")
void whenSpringJpaGenerateDdlIsTrueAndSpringJpaHibernateDdlAutoIsNoneThenTableIsNotCreated() {
// This test fails because when ddl-auto is set to none, we remove
// hibernate.hbm2ddl.auto from Hibernate properties. This then allows
// spring.jpa.generate-ddl to set it to create-drop
contextRunner().withPropertyValues("spring.jpa.generate-ddl=true", "spring.jpa.hibernate.ddl-auto=none")
.run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY"));
}
@Test
void whenSpringJpaGenerateDdlIsTrueAndSpringJpaHibernateDdlAutoIsDropThenTableIsNotCreated() {
contextRunner().withPropertyValues("spring.jpa.generate-ddl=true", "spring.jpa.hibernate.ddl-auto=drop")
.run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY"));
}
@Test
void whenSpringJpaGenerateDdlIsTrueAndJakartaSchemaGenerationIsNoneThenTableIsNotCreated() {
contextRunner()
.withPropertyValues("spring.jpa.generate-ddl=true",
"spring.jpa.properties.jakarta.persistence.schema-generation.database.action=none")
.run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY"));
}
@Test
void whenSpringJpaGenerateDdlIsTrueSpringJpaHibernateDdlAutoIsCreateAndJakartaSchemaGenerationIsNoneThenTableIsNotCreated() {
contextRunner()
.withPropertyValues("spring.jpa.generate-ddl=true", "spring.jpa.hibernate.ddl-auto=create",
"spring.jpa.properties.jakarta.persistence.schema-generation.database.action=none")
.run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY"));
}
private List<String> tablesFrom(AssertableApplicationContext context) {
DataSource dataSource = context.getBean(DataSource.class);
JdbcTemplate jdbc = new JdbcTemplate(dataSource);
List<String> tables = jdbc.query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES",
(results, row) -> results.getString(1));
return tables;
}
@Configuration(proxyBeanMethods = false)
@TestAutoConfigurationPackage(City.class)
@DependsOnDatabaseInitialization
static class TestInitializedJpaConfiguration {
private boolean called;
@Autowired
void validateDataSourceIsInitialized(EntityManagerFactory entityManagerFactory) {
// Inject the entity manager to validate it is initialized at the injection
// point
EntityManager entityManager = entityManagerFactory.createEntityManager();
City city = entityManager.find(City.class, 2000L);
assertThat(city).isNotNull();
assertThat(city.getName()).isEqualTo("Washington");
this.called = true;
}
}
@Configuration(proxyBeanMethods = false)
static class TestImplicitNamingStrategyConfiguration {
@Bean
ImplicitNamingStrategy testImplicitNamingStrategy() {
return new SpringImplicitNamingStrategy();
}
}
@Configuration(proxyBeanMethods = false)
static class TestPhysicalNamingStrategyConfiguration {
@Bean
PhysicalNamingStrategy testPhysicalNamingStrategy() {
return new PhysicalNamingStrategySnakeCaseImpl();
}
}
@Configuration(proxyBeanMethods = false)
static class TestHibernatePropertiesCustomizerConfiguration {
private final PhysicalNamingStrategy physicalNamingStrategy = new PhysicalNamingStrategySnakeCaseImpl();
private final ImplicitNamingStrategy implicitNamingStrategy = new SpringImplicitNamingStrategy();
@Bean
HibernatePropertiesCustomizer testHibernatePropertiesCustomizer() {
return (hibernateProperties) -> {
hibernateProperties.put("hibernate.physical_naming_strategy", this.physicalNamingStrategy);
hibernateProperties.put("hibernate.implicit_naming_strategy", this.implicitNamingStrategy);
};
}
}
@Configuration(proxyBeanMethods = false)
static class DisableBeanContainerConfiguration {
@Bean
HibernatePropertiesCustomizer disableBeanContainerHibernatePropertiesCustomizer() {
return (hibernateProperties) -> hibernateProperties.remove(ManagedBeanSettings.BEAN_CONTAINER);
}
}
public static class TestJtaPlatform implements JtaPlatform {
@Override
public TransactionManager retrieveTransactionManager() {
return mock(TransactionManager.class);
}
@Override
public UserTransaction retrieveUserTransaction() {
throw new UnsupportedOperationException();
}
@Override
public Object getTransactionIdentifier(Transaction transaction) {
throw new UnsupportedOperationException();
}
@Override
public boolean canRegisterSynchronization() {
throw new UnsupportedOperationException();
}
@Override
public void registerSynchronization(Synchronization synchronization) {
throw new UnsupportedOperationException();
}
@Override
public int getCurrentStatus() {
throw new UnsupportedOperationException();
}
}
static class HideDataScriptClassLoader extends URLClassLoader {
private static final List<String> HIDDEN_RESOURCES = Arrays.asList("schema-all.sql", "schema.sql");
HideDataScriptClassLoader() {
super(new URL[0], Thread.currentThread().getContextClassLoader());
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
if (HIDDEN_RESOURCES.contains(name)) {
return Collections.emptyEnumeration();
}
return super.getResources(name);
}
}
@org.springframework.context.annotation.Configuration(proxyBeanMethods = false)
static class JpaUsingApplicationListenerConfiguration {
@Bean
EventCapturingApplicationListener jpaUsingApplicationListener(EntityManagerFactory emf) {
return new EventCapturingApplicationListener();
}
static class EventCapturingApplicationListener implements ApplicationListener<ApplicationEvent> {
private final List<ApplicationEvent> events = new ArrayList<>();
@Override
public void onApplicationEvent(ApplicationEvent event) {
this.events.add(event);
}
}
}
@Configuration(proxyBeanMethods = false)
static class AsyncBootstrappingConfiguration {
@Bean
ThreadPoolTaskExecutor ThreadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
@Bean
EntityManagerFactoryBuilderCustomizer asyncBootstrappingCustomizer(ThreadPoolTaskExecutor executor) {
return (builder) -> builder.setBootstrapExecutor(executor);
}
}
@Configuration(proxyBeanMethods = false)
static class TestConfigurationWithLocalContainerEntityManagerFactoryBeanWithNoJpaVendorAdapter
extends TestConfiguration {
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setPersistenceUnitName("manually-configured");
factoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
Map<String, Object> properties = new HashMap<>();
properties.put("configured", "manually");
properties.put("hibernate.transaction.jta.platform", NoJtaPlatform.INSTANCE);
factoryBean.setJpaPropertyMap(properties);
return factoryBean;
}
}
public static class TestH2Dialect extends H2Dialect {
}
@Configuration(proxyBeanMethods = false)
static class JtaTransactionManagerConfiguration {
@Bean
JtaTransactionManager jtaTransactionManager() {
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
jtaTransactionManager.setUserTransaction(mock(UserTransaction.class));
jtaTransactionManager.setTransactionManager(mock(TransactionManager.class));
return jtaTransactionManager;
}
}
}

View File

@@ -0,0 +1,163 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure.hibernate;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategySnakeCaseImpl;
import org.hibernate.cfg.MappingSettings;
import org.hibernate.cfg.PersistenceSettings;
import org.hibernate.cfg.SchemaToolingSettings;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jpa.autoconfigure.JpaProperties;
import org.springframework.boot.jpa.hibernate.SpringImplicitNamingStrategy;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.never;
/**
* Tests for {@link HibernateProperties}.
*
* @author Stephane Nicoll
* @author Artsiom Yudovin
* @author Chris Bono
*/
@ExtendWith(MockitoExtension.class)
class HibernatePropertiesTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withUserConfiguration(TestConfiguration.class);
@Mock
private Supplier<String> ddlAutoSupplier;
@Test
void noCustomNamingStrategy() {
this.contextRunner.run(assertHibernateProperties((hibernateProperties) -> {
assertThat(hibernateProperties).doesNotContainKeys("hibernate.ejb.naming_strategy");
assertThat(hibernateProperties).containsEntry(MappingSettings.PHYSICAL_NAMING_STRATEGY,
PhysicalNamingStrategySnakeCaseImpl.class.getName());
assertThat(hibernateProperties).containsEntry(MappingSettings.IMPLICIT_NAMING_STRATEGY,
SpringImplicitNamingStrategy.class.getName());
}));
}
@Test
void hibernate5CustomNamingStrategies() {
this.contextRunner
.withPropertyValues("spring.jpa.hibernate.naming.implicit-strategy:com.example.Implicit",
"spring.jpa.hibernate.naming.physical-strategy:com.example.Physical")
.run(assertHibernateProperties((hibernateProperties) -> {
assertThat(hibernateProperties).contains(
entry(MappingSettings.IMPLICIT_NAMING_STRATEGY, "com.example.Implicit"),
entry(MappingSettings.PHYSICAL_NAMING_STRATEGY, "com.example.Physical"));
assertThat(hibernateProperties).doesNotContainKeys("hibernate.ejb.naming_strategy");
}));
}
@Test
void hibernate5CustomNamingStrategiesViaJpaProperties() {
this.contextRunner
.withPropertyValues("spring.jpa.properties.hibernate.implicit_naming_strategy:com.example.Implicit",
"spring.jpa.properties.hibernate.physical_naming_strategy:com.example.Physical")
.run(assertHibernateProperties((hibernateProperties) -> {
// You can override them as we don't provide any default
assertThat(hibernateProperties).contains(
entry(MappingSettings.IMPLICIT_NAMING_STRATEGY, "com.example.Implicit"),
entry(MappingSettings.PHYSICAL_NAMING_STRATEGY, "com.example.Physical"));
assertThat(hibernateProperties).doesNotContainKeys("hibernate.ejb.naming_strategy");
}));
}
@Test
void scannerUsesDisabledScannerByDefault() {
this.contextRunner.run(assertHibernateProperties((hibernateProperties) -> assertThat(hibernateProperties)
.containsEntry(PersistenceSettings.SCANNER, "org.hibernate.boot.archive.scan.internal.DisabledScanner")));
}
@Test
void scannerCanBeCustomized() {
this.contextRunner.withPropertyValues(
"spring.jpa.properties.hibernate.archive.scanner:org.hibernate.boot.archive.scan.internal.StandardScanner")
.run(assertHibernateProperties((hibernateProperties) -> assertThat(hibernateProperties).containsEntry(
PersistenceSettings.SCANNER, "org.hibernate.boot.archive.scan.internal.StandardScanner")));
}
@Test
void defaultDdlAutoIsNotInvokedIfPropertyIsSet() {
this.contextRunner.withPropertyValues("spring.jpa.hibernate.ddl-auto=validate")
.run(assertDefaultDdlAutoNotInvoked("validate"));
}
@Test
void defaultDdlAutoIsNotInvokedIfHibernateSpecificPropertyIsSet() {
this.contextRunner.withPropertyValues("spring.jpa.properties.hibernate.hbm2ddl.auto=create")
.run(assertDefaultDdlAutoNotInvoked("create"));
}
@Test
void defaultDdlAutoIsNotInvokedAndDdlAutoIsNotSetIfJpaDbActionPropertyIsSet() {
this.contextRunner
.withPropertyValues(
"spring.jpa.properties.jakarta.persistence.schema-generation.database.action=drop-and-create")
.run(assertHibernateProperties((hibernateProperties) -> {
assertThat(hibernateProperties).doesNotContainKey(SchemaToolingSettings.HBM2DDL_AUTO);
assertThat(hibernateProperties).containsEntry(SchemaToolingSettings.JAKARTA_HBM2DDL_DATABASE_ACTION,
"drop-and-create");
then(this.ddlAutoSupplier).should(never()).get();
}));
}
private ContextConsumer<AssertableApplicationContext> assertDefaultDdlAutoNotInvoked(String expectedDdlAuto) {
return assertHibernateProperties((hibernateProperties) -> {
assertThat(hibernateProperties).containsEntry(SchemaToolingSettings.HBM2DDL_AUTO, expectedDdlAuto);
then(this.ddlAutoSupplier).should(never()).get();
});
}
private ContextConsumer<AssertableApplicationContext> assertHibernateProperties(
Consumer<Map<String, Object>> consumer) {
return (context) -> {
assertThat(context).hasSingleBean(JpaProperties.class);
assertThat(context).hasSingleBean(HibernateProperties.class);
Map<String, Object> hibernateProperties = context.getBean(HibernateProperties.class)
.determineHibernateProperties(context.getBean(JpaProperties.class).getProperties(),
new HibernateSettings().ddlAuto(this.ddlAutoSupplier));
consumer.accept(hibernateProperties);
};
}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({ JpaProperties.class, HibernateProperties.class })
static class TestConfiguration {
}
}

View File

@@ -0,0 +1,53 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure.hibernate.mapping;
/**
* A non annotated entity that is handled by a custom "mapping-file".
*
* @author Stephane Nicoll
*/
public class NonAnnotatedEntity {
private Long id;
private String item;
protected NonAnnotatedEntity() {
}
public NonAnnotatedEntity(String item) {
this.item = item;
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getItem() {
return this.item;
}
public void setItem(String value) {
this.item = value;
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure.test.city;
import java.io.Serializable;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
@Entity
@EntityListeners(CityListener.class)
public 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() {
}
public 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

@@ -0,0 +1,43 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure.test.city;
import jakarta.persistence.PostLoad;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
public class CityListener {
private ConfigurableBeanFactory beanFactory;
public CityListener() {
}
@Autowired
public CityListener(ConfigurableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@PostLoad
public void postLoad(City city) {
if (this.beanFactory != null) {
this.beanFactory.registerSingleton(City.class.getName(), city);
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jpa.autoconfigure.test.country;
import java.io.Serializable;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import org.hibernate.envers.Audited;
@Entity
public class Country implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private Long id;
@Audited
@Column
private String name;
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}