Create spring-boot-jpa module
This commit is contained in:
committed by
Phillip Webb
parent
81d4528eee
commit
079713039d
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.boot.jpa.autoconfigure.hibernate.HibernateJpaAutoConfiguration
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user