diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBean.java b/spring-orm/src/main/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBean.java
index 05c6bccf726..83b72032644 100644
--- a/spring-orm/src/main/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBean.java
+++ b/spring-orm/src/main/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBean.java
@@ -71,7 +71,9 @@ import org.springframework.util.CollectionUtils;
* making {@code EntityManager} available for dependency injection as well.
*
*
Encapsulates the common functionality between the different JPA bootstrap
- * contracts (standalone as well as container).
+ * contracts: standalone as well as container. Note that as of 7.0, the JPA 3.2
+ * {@link LocalEntityManagerFactoryBean#setPersistenceConfiguration PersistenceConfiguration}
+ * mechanism is supported as well, allowing for much richer standalone bootstrap options.
*
*
Implements support for standard JPA configuration conventions as well as
* Spring's customizable {@link JpaVendorAdapter} mechanism, and controls the
diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.java b/spring-orm/src/main/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.java
index 44cd372cb18..8b080269b49 100644
--- a/spring-orm/src/main/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.java
+++ b/spring-orm/src/main/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.java
@@ -21,6 +21,7 @@ import java.util.List;
import javax.sql.DataSource;
import jakarta.persistence.EntityManagerFactory;
+import jakarta.persistence.PersistenceConfiguration;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.SharedCacheMode;
import jakarta.persistence.ValidationMode;
@@ -166,6 +167,22 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
this.internalPersistenceUnitManager.setDefaultPersistenceUnitRootLocation(defaultPersistenceUnitRootLocation);
}
+ /**
+ * Set a local JPA 3.2 {@link PersistenceConfiguration} to use for this
+ * persistence unit.
+ *
Note: {@link PersistenceConfiguration} includes a persistence unit name,
+ * so this effectively overrides the {@link #setPersistenceUnitName} method.
+ * In contrast, all other settings will be merged with the settings in the
+ * {@code PersistenceConfiguration} instance.
+ * @since 7.0
+ * @see DefaultPersistenceUnitManager#setPersistenceConfiguration
+ */
+ public void setPersistenceConfiguration(PersistenceConfiguration configuration) {
+ Assert.notNull(configuration, "PersistenceConfiguration must not be null");
+ super.setPersistenceUnitName(configuration.name());
+ this.internalPersistenceUnitManager.setPersistenceConfiguration(configuration);
+ }
+
/**
* Set the {@link PersistenceManagedTypes} to use to build the list of managed types
* as an alternative to entity scanning.
@@ -424,15 +441,16 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
* Determine the PersistenceUnitInfo to use for the EntityManagerFactory
* created by this bean.
*
The default implementation reads in all persistence unit infos from
- * {@code persistence.xml}, as defined in the JPA specification.
- * If no entity manager name was specified, it takes the first info in the
- * array as returned by the reader. Otherwise, it checks for a matching name.
+ * {@code persistence.xml}, as defined in the JPA specification, selecting a unit
+ * by name. If no persistence unit name was specified, it takes the default one
+ * if configured, or otherwise the first persistence unit as found by the reader.
* @param persistenceUnitManager the PersistenceUnitManager to obtain from
* @return the chosen PersistenceUnitInfo
*/
protected PersistenceUnitInfo determinePersistenceUnitInfo(PersistenceUnitManager persistenceUnitManager) {
- if (getPersistenceUnitName() != null) {
- return persistenceUnitManager.obtainPersistenceUnitInfo(getPersistenceUnitName());
+ String persistenceUnitName = getPersistenceUnitName();
+ if (persistenceUnitName != null) {
+ return persistenceUnitManager.obtainPersistenceUnitInfo(persistenceUnitName);
}
else {
return persistenceUnitManager.obtainDefaultPersistenceUnitInfo();
diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBean.java b/spring-orm/src/main/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBean.java
index 39d7490d35d..0eeb261730e 100644
--- a/spring-orm/src/main/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBean.java
+++ b/spring-orm/src/main/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBean.java
@@ -20,10 +20,14 @@ import javax.sql.DataSource;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;
+import jakarta.persistence.PersistenceConfiguration;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.spi.PersistenceProvider;
import org.jspecify.annotations.Nullable;
+import org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager;
+import org.springframework.util.Assert;
+
/**
* {@link org.springframework.beans.factory.FactoryBean} that creates a JPA
* {@link jakarta.persistence.EntityManagerFactory} according to JPA's standard
@@ -62,6 +66,80 @@ public class LocalEntityManagerFactoryBean extends AbstractEntityManagerFactoryB
private static final String DATASOURCE_PROPERTY = "jakarta.persistence.dataSource";
+ private @Nullable PersistenceConfiguration configuration;
+
+
+ /**
+ * Create a {@code LocalEntityManagerFactoryBean}.
+ *
As of 7.0, This uses "default" for the default persistence unit name.
+ * @see #setPersistenceUnitName
+ * @see #setPersistenceConfiguration
+ */
+ public LocalEntityManagerFactoryBean() {
+ setPersistenceUnitName(DefaultPersistenceUnitManager.ORIGINAL_DEFAULT_PERSISTENCE_UNIT_NAME);
+ }
+
+ /**
+ * Create a {@code LocalEntityManagerFactoryBean} for the given persistence unit.
+ * @param persistenceUnitName the name of the persistence unit
+ * @since 7.0
+ */
+ public LocalEntityManagerFactoryBean(String persistenceUnitName) {
+ setPersistenceUnitName(persistenceUnitName);
+ }
+
+ /**
+ * Create a {@code LocalEntityManagerFactoryBean} for the given persistence unit.
+ * @param configuration the configuration for the persistence unit
+ * @since 7.0
+ */
+ public LocalEntityManagerFactoryBean(PersistenceConfiguration configuration) {
+ setPersistenceConfiguration(configuration);
+ }
+
+
+ /**
+ * Set a local JPA 3.2 {@link PersistenceConfiguration} to use for creating
+ * the EntityManagerFactory. This can be a provider-specific subclass such as
+ * {@link org.hibernate.jpa.HibernatePersistenceConfiguration}, exposing a
+ * complete programmatic persistence unit configuration which replaces
+ * {@code persistence.xml} (including provider-specific classpath scanning).
+ *
Note: {@link PersistenceConfiguration} includes a persistence unit name,
+ * so this effectively overrides the {@link #setPersistenceUnitName} method.
+ * In contrast, locally specified JPA properties ({@link #setJpaProperties})
+ * will get merged into the given {@code PersistenceConfiguration} instance.
+ * @since 7.0
+ * @see #getPersistenceConfiguration()
+ * @see #getPersistenceUnitName()
+ */
+ public void setPersistenceConfiguration(PersistenceConfiguration configuration) {
+ Assert.notNull(configuration, "PersistenceConfiguration must not be null");
+ this.configuration = configuration;
+ setPersistenceUnitName(configuration.name());
+ }
+
+ /**
+ * Set a local JPA 3.2 {@link PersistenceConfiguration} to use for creating
+ * the EntityManagerFactory. If none is in use yet, a new plain
+ * {@link PersistenceConfiguration} for the configured persistence unit name
+ * will be created and returned.
+ * @since 7.0
+ * @see #setPersistenceConfiguration
+ * @see #setPersistenceUnitName
+ */
+ public PersistenceConfiguration getPersistenceConfiguration() {
+ if (this.configuration == null) {
+ this.configuration = new PersistenceConfiguration(getPersistenceUnitName());
+ }
+ return this.configuration;
+ }
+
+ @Override
+ public void setPersistenceUnitName(@Nullable String persistenceUnitName) {
+ Assert.state(this.configuration == null || this.configuration.name().equals(persistenceUnitName),
+ "Cannot change setPersistenceUnitName when PersistenceConfiguration has been set");
+ super.setPersistenceUnitName(persistenceUnitName);
+ }
/**
* Specify the JDBC DataSource that the JPA persistence provider is supposed
@@ -104,10 +182,17 @@ public class LocalEntityManagerFactoryBean extends AbstractEntityManagerFactoryB
if (logger.isDebugEnabled()) {
logger.debug("Building JPA EntityManagerFactory for persistence unit '" + getPersistenceUnitName() + "'");
}
+
+ if (this.configuration != null) {
+ this.configuration.properties(getJpaPropertyMap());
+ }
+
PersistenceProvider provider = getPersistenceProvider();
if (provider != null) {
// Create EntityManagerFactory directly through PersistenceProvider.
- EntityManagerFactory emf = provider.createEntityManagerFactory(getPersistenceUnitName(), getJpaPropertyMap());
+ EntityManagerFactory emf = (this.configuration != null ?
+ provider.createEntityManagerFactory(this.configuration) :
+ provider.createEntityManagerFactory(getPersistenceUnitName(), getJpaPropertyMap()));
if (emf == null) {
throw new IllegalStateException(
"PersistenceProvider [" + provider + "] did not return an EntityManagerFactory for name '" +
@@ -117,7 +202,9 @@ public class LocalEntityManagerFactoryBean extends AbstractEntityManagerFactoryB
}
else {
// Let JPA perform its standard PersistenceProvider autodetection.
- return Persistence.createEntityManagerFactory(getPersistenceUnitName(), getJpaPropertyMap());
+ return (this.configuration != null ?
+ Persistence.createEntityManagerFactory(this.configuration) :
+ Persistence.createEntityManagerFactory(getPersistenceUnitName(), getJpaPropertyMap()));
}
}
diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java
index 49e267f4343..c014ef43fc1 100644
--- a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java
+++ b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java
@@ -27,6 +27,7 @@ import java.util.Set;
import javax.sql.DataSource;
+import jakarta.persistence.PersistenceConfiguration;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.SharedCacheMode;
import jakarta.persistence.ValidationMode;
@@ -108,6 +109,8 @@ public class DefaultPersistenceUnitManager
private @Nullable String defaultPersistenceUnitName = ORIGINAL_DEFAULT_PERSISTENCE_UNIT_NAME;
+ private @Nullable PersistenceConfiguration persistenceConfiguration;
+
private @Nullable PersistenceManagedTypes managedTypes;
private String @Nullable [] packagesToScan;
@@ -179,9 +182,19 @@ public class DefaultPersistenceUnitManager
this.defaultPersistenceUnitName = defaultPersistenceUnitName;
}
+ /**
+ * Set a JPA 3.2 {@link PersistenceConfiguration} to apply to the default
+ * persistence unit. The contained configuration will be merged with the
+ * common settings specified on this {@code DefaultPersistenceUnitManager}.
+ * @since 7.0
+ */
+ public void setPersistenceConfiguration(PersistenceConfiguration configuration) {
+ this.persistenceConfiguration = configuration;
+ }
+
/**
* Set the {@link PersistenceManagedTypes} to use to build the list of managed types
- * as an alternative to entity scanning.
+ * for the default persistence unit, as an alternative to entity scanning.
* @param managedTypes the managed types
* @since 6.0
*/
@@ -492,7 +505,8 @@ public class DefaultPersistenceUnitManager
private List readPersistenceUnitInfos() {
List infos = new ArrayList<>(1);
String defaultName = this.defaultPersistenceUnitName;
- boolean buildDefaultUnit = (this.managedTypes != null || this.packagesToScan != null || this.mappingResources != null);
+ boolean buildDefaultUnit = (this.persistenceConfiguration != null || this.managedTypes != null ||
+ this.packagesToScan != null || this.mappingResources != null);
boolean foundDefaultUnit = false;
PersistenceUnitReader reader = new PersistenceUnitReader(this.resourcePatternResolver, this.dataSourceLookup);
@@ -507,8 +521,9 @@ public class DefaultPersistenceUnitManager
if (buildDefaultUnit) {
if (foundDefaultUnit) {
if (logger.isWarnEnabled()) {
- logger.warn("Found explicit default persistence unit with name '" + defaultName + "' in persistence.xml - " +
- "overriding local default persistence unit settings ('managedTypes', 'packagesToScan' or 'mappingResources')");
+ logger.warn("Found explicit default persistence unit with name '" + defaultName +
+ "' in persistence.xml - overriding local default persistence unit settings " +
+ "(`persistenceConfiguration`, 'managedTypes', 'packagesToScan' or 'mappingResources')");
}
}
else {
@@ -523,33 +538,37 @@ public class DefaultPersistenceUnitManager
* @see #setPackagesToScan
*/
private SpringPersistenceUnitInfo buildDefaultPersistenceUnitInfo() {
- SpringPersistenceUnitInfo scannedUnit = new SpringPersistenceUnitInfo();
+ SpringPersistenceUnitInfo defaultUnit = new SpringPersistenceUnitInfo();
if (this.defaultPersistenceUnitName != null) {
- scannedUnit.setPersistenceUnitName(this.defaultPersistenceUnitName);
+ defaultUnit.setPersistenceUnitName(this.defaultPersistenceUnitName);
+ }
+ defaultUnit.setExcludeUnlistedClasses(true);
+
+ if (this.persistenceConfiguration != null) {
+ defaultUnit.apply(this.persistenceConfiguration, this.dataSourceLookup);
}
- scannedUnit.setExcludeUnlistedClasses(true);
if (this.managedTypes != null) {
- applyManagedTypes(scannedUnit, this.managedTypes);
+ defaultUnit.apply(this.managedTypes);
}
else if (this.packagesToScan != null) {
PersistenceManagedTypesScanner scanner = new PersistenceManagedTypesScanner(
this.resourcePatternResolver, this.managedClassNameFilter);
- applyManagedTypes(scannedUnit, scanner.scan(this.packagesToScan));
+ defaultUnit.apply(scanner.scan(this.packagesToScan));
}
if (this.mappingResources != null) {
for (String mappingFileName : this.mappingResources) {
- scannedUnit.addMappingFileName(mappingFileName);
+ defaultUnit.addMappingFileName(mappingFileName);
}
}
else {
Resource ormXml = getOrmXmlForDefaultPersistenceUnit();
if (ormXml != null) {
- scannedUnit.addMappingFileName(DEFAULT_ORM_XML_RESOURCE);
- if (scannedUnit.getPersistenceUnitRootUrl() == null) {
+ defaultUnit.addMappingFileName(DEFAULT_ORM_XML_RESOURCE);
+ if (defaultUnit.getPersistenceUnitRootUrl() == null) {
try {
- scannedUnit.setPersistenceUnitRootUrl(
+ defaultUnit.setPersistenceUnitRootUrl(
PersistenceUnitReader.determinePersistenceUnitRootUrl(ormXml));
}
catch (IOException ex) {
@@ -559,16 +578,7 @@ public class DefaultPersistenceUnitManager
}
}
- return scannedUnit;
- }
-
- private void applyManagedTypes(SpringPersistenceUnitInfo scannedUnit, PersistenceManagedTypes managedTypes) {
- managedTypes.getManagedClassNames().forEach(scannedUnit::addManagedClassName);
- managedTypes.getManagedPackages().forEach(scannedUnit::addManagedPackage);
- URL persistenceUnitRootUrl = managedTypes.getPersistenceUnitRootUrl();
- if (scannedUnit.getPersistenceUnitRootUrl() == null && persistenceUnitRootUrl != null) {
- scannedUnit.setPersistenceUnitRootUrl(persistenceUnitRootUrl);
- }
+ return defaultUnit;
}
/**
diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/MutablePersistenceUnitInfo.java b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/MutablePersistenceUnitInfo.java
index 35d02a65d59..5a66df719aa 100644
--- a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/MutablePersistenceUnitInfo.java
+++ b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/MutablePersistenceUnitInfo.java
@@ -60,16 +60,12 @@ public class MutablePersistenceUnitInfo {
private @Nullable String persistenceProviderClassName;
- private @Nullable String scopeAnnotationName;
-
- private final List qualifierAnnotationNames = new ArrayList<>();
-
private @Nullable PersistenceUnitTransactionType transactionType;
- private @Nullable DataSource nonJtaDataSource;
-
private @Nullable DataSource jtaDataSource;
+ private @Nullable DataSource nonJtaDataSource;
+
private final List mappingFileNames = new ArrayList<>();
private final List jarFileUrls = new ArrayList<>();
@@ -109,22 +105,6 @@ public class MutablePersistenceUnitInfo {
return this.persistenceProviderClassName;
}
- public void setScopeAnnotationName(@Nullable String scopeAnnotationName) {
- this.scopeAnnotationName = scopeAnnotationName;
- }
-
- public @Nullable String getScopeAnnotationName() {
- return this.scopeAnnotationName;
- }
-
- public void addQualifierAnnotationName(String qualifierAnnotationName) {
- this.qualifierAnnotationNames.add(qualifierAnnotationName);
- }
-
- public List getQualifierAnnotationNames() {
- return this.qualifierAnnotationNames;
- }
-
public void setTransactionType(PersistenceUnitTransactionType transactionType) {
this.transactionType = transactionType;
}
diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/SpringPersistenceUnitInfo.java b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/SpringPersistenceUnitInfo.java
index 5c9a83682ec..d8b1ef37747 100644
--- a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/SpringPersistenceUnitInfo.java
+++ b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/SpringPersistenceUnitInfo.java
@@ -19,7 +19,11 @@ package org.springframework.orm.jpa.persistenceunit;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import jakarta.persistence.PersistenceConfiguration;
import jakarta.persistence.PersistenceUnitTransactionType;
import jakarta.persistence.spi.ClassTransformer;
import jakarta.persistence.spi.PersistenceUnitInfo;
@@ -29,7 +33,9 @@ import org.jspecify.annotations.Nullable;
import org.springframework.core.DecoratingClassLoader;
import org.springframework.instrument.classloading.LoadTimeWeaver;
import org.springframework.instrument.classloading.SimpleThrowawayClassLoader;
+import org.springframework.jdbc.datasource.lookup.DataSourceLookup;
import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
@@ -55,6 +61,10 @@ public class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo {
private @Nullable ClassLoader classLoader;
+ private @Nullable String scopeAnnotationName;
+
+ private final List qualifierAnnotationNames = new ArrayList<>();
+
/**
* Construct a new SpringPersistenceUnitInfo for custom purposes.
@@ -132,6 +142,87 @@ public class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo {
return tcl;
}
+ public void setScopeAnnotationName(@Nullable String scopeAnnotationName) {
+ this.scopeAnnotationName = scopeAnnotationName;
+ }
+
+ public @Nullable String getScopeAnnotationName() {
+ return this.scopeAnnotationName;
+ }
+
+ public void addQualifierAnnotationName(String qualifierAnnotationName) {
+ this.qualifierAnnotationNames.add(qualifierAnnotationName);
+ }
+
+ public List getQualifierAnnotationNames() {
+ return this.qualifierAnnotationNames;
+ }
+
+
+ /**
+ * Apply the given {@link PersistenceManagedTypes} to this persistence unit,
+ * typically coming from Spring AOT.
+ * @param managedTypes the managed persistent types to register
+ * @since 7.0
+ */
+ public void apply(PersistenceManagedTypes managedTypes) {
+ Assert.notNull(managedTypes, "PersistenceManagedTypes must not be null");
+ managedTypes.getManagedClassNames().forEach(this::addManagedClassName);
+ managedTypes.getManagedPackages().forEach(this::addManagedPackage);
+ URL persistenceUnitRootUrl = managedTypes.getPersistenceUnitRootUrl();
+ if (getPersistenceUnitRootUrl() == null && persistenceUnitRootUrl != null) {
+ setPersistenceUnitRootUrl(persistenceUnitRootUrl);
+ }
+ }
+
+ /**
+ * Apply the given JPA 3.2 {@link PersistenceConfiguration} to this persistence unit,
+ * copying all applicable settings.
+ * Beyond the standard {@code PersistenceConfiguration} settings, "rootUrl" and
+ * "jarFileUrls" from {@link org.hibernate.jpa.HibernatePersistenceConfiguration}
+ * are also detected and applied.
+ * @param config the JPA persistence configuration to apply
+ * @param dataSourceLookup the JDBC DataSourceLookup that provides DataSources for the
+ * persistence provider, resolving data source names in {@code PersistenceConfiguration}
+ * against Spring-managed DataSource instances
+ * @since 7.0
+ */
+ @SuppressWarnings("unchecked")
+ public void apply(PersistenceConfiguration config, DataSourceLookup dataSourceLookup) {
+ Assert.notNull(config, "PersistenceConfiguration must not be null");
+
+ setPersistenceUnitName(config.name());
+ setPersistenceProviderClassName(config.provider());
+ setTransactionType(config.transactionType());
+
+ if (config.nonJtaDataSource() != null) {
+ setNonJtaDataSource(dataSourceLookup.getDataSource(config.nonJtaDataSource()));
+ }
+ if (config.jtaDataSource() != null) {
+ setJtaDataSource(dataSourceLookup.getDataSource(config.jtaDataSource()));
+ }
+
+ config.mappingFiles().forEach(this::addMappingFileName);
+ config.managedClasses().forEach(clazz -> addManagedClassName(clazz.getName()));
+
+ setSharedCacheMode(config.sharedCacheMode());
+ setValidationMode(config.validationMode());
+ getProperties().putAll(config.properties());
+
+ // Further relevant settings from HibernatePersistenceConfiguration
+ Method rootUrl = ClassUtils.getMethodIfAvailable(config.getClass(), "rootUrl");
+ if (rootUrl != null) {
+ setPersistenceUnitRootUrl((URL) ReflectionUtils.invokeMethod(rootUrl, config));
+ }
+ Method jarFileUrls = ClassUtils.getMethodIfAvailable(config.getClass(), "jarFileUrls");
+ if (jarFileUrls != null) {
+ List urlList = ((List) ReflectionUtils.invokeMethod(jarFileUrls, config));
+ if (urlList != null) {
+ urlList.forEach(this::addJarFileUrl);
+ }
+ }
+ }
+
/**
* Expose a standard {@code jakarta.persistence.spi.PersistenceUnitInfo} proxy for the
* persistence unit configuration in this {@code SpringPersistenceUnitInfo} instance.
@@ -153,7 +244,7 @@ public class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo {
*/
private class SmartPersistenceUnitInfoInvocationHandler implements InvocationHandler {
- @SuppressWarnings({ "unchecked", "rawtypes" })
+ @SuppressWarnings({"rawtypes", "unchecked"})
@Override
public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Fast path for SmartPersistenceUnitInfo JTA check
diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBeanTests.java b/spring-orm/src/test/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBeanTests.java
index fee9328518e..2473b46aab7 100644
--- a/spring-orm/src/test/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBeanTests.java
+++ b/spring-orm/src/test/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBeanTests.java
@@ -39,27 +39,27 @@ public abstract class AbstractEntityManagerFactoryBeanTests {
protected static EntityManagerFactory mockEmf;
+
@BeforeEach
- void setUp() {
+ void setup() {
mockEmf = mock();
}
@AfterEach
- void tearDown() {
+ void cleanup() {
assertThat(TransactionSynchronizationManager.getResourceMap()).isEmpty();
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse();
assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isFalse();
}
- protected void checkInvariants(AbstractEntityManagerFactoryBean demf) {
- assertThat(EntityManagerFactory.class.isAssignableFrom(demf.getObjectType())).isTrue();
- Object gotObject = demf.getObject();
- boolean condition = gotObject instanceof EntityManagerFactoryInfo;
- assertThat(condition).as("Object created by factory implements EntityManagerFactoryInfo").isTrue();
- EntityManagerFactoryInfo emfi = (EntityManagerFactoryInfo) demf.getObject();
- assertThat(demf.getObject()).as("Successive invocations of getObject() return same object").isSameAs(emfi);
- assertThat(demf.getObject()).isSameAs(emfi);
+ protected void checkInvariants(AbstractEntityManagerFactoryBean emfb) {
+ assertThat(EntityManagerFactory.class.isAssignableFrom(emfb.getObjectType())).isTrue();
+ EntityManagerFactory emf = emfb.getObject();
+ assertThat(emf instanceof EntityManagerFactoryInfo).as("Object created by factory implements EntityManagerFactoryInfo").isTrue();
+ EntityManagerFactoryInfo emfi = (EntityManagerFactoryInfo) emf;
+ assertThat(emfb.getObject()).as("Successive invocations of getObject() return same object").isSameAs(emfi);
+ assertThat(emfb.getObject()).isSameAs(emfi);
assertThat(mockEmf).isSameAs(emfi.getNativeEntityManagerFactory());
}
diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBeanTests.java b/spring-orm/src/test/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBeanTests.java
index 57294dd321a..055c117402e 100644
--- a/spring-orm/src/test/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBeanTests.java
+++ b/spring-orm/src/test/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBeanTests.java
@@ -36,6 +36,8 @@ import org.springframework.core.testfixture.io.SerializationTestUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
+import org.springframework.orm.jpa.domain.DriversLicense;
+import org.springframework.orm.jpa.domain.Person;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
@@ -53,23 +55,16 @@ import static org.mockito.Mockito.verify;
* @author Juergen Hoeller
* @author Phillip Webb
*/
-@SuppressWarnings({"rawtypes", "removal"})
+@SuppressWarnings("rawtypes")
class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerFactoryBeanTests {
- // Static fields set by inner class DummyPersistenceProvider
-
- private static Map actualProps;
-
- private static PersistenceUnitInfo actualPui;
-
-
@Test
- void testValidPersistenceUnit() throws Exception {
+ void validPersistenceUnit() {
parseValidPersistenceUnit();
}
@Test
- void testExceptionTranslationWithNoDialect() throws Exception {
+ void exceptionTranslationWithNoDialect() {
LocalContainerEntityManagerFactoryBean cefb = parseValidPersistenceUnit();
cefb.getObject();
assertThat(cefb.getJpaDialect()).as("No dialect set").isNull();
@@ -83,7 +78,7 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF
}
@Test
- void testEntityManagerFactoryIsProxied() throws Exception {
+ void entityManagerFactoryIsProxied() throws Exception {
LocalContainerEntityManagerFactoryBean cefb = parseValidPersistenceUnit();
EntityManagerFactory emf = cefb.getObject();
assertThat(cefb.getObject()).as("EntityManagerFactory reference must be cached after init").isSameAs(emf);
@@ -100,7 +95,7 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF
}
@Test
- void testApplicationManagedEntityManagerWithoutTransaction() throws Exception {
+ void applicationManagedEntityManagerWithoutTransaction() {
Object testEntity = new Object();
EntityManager mockEm = mock();
@@ -120,7 +115,7 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF
}
@Test
- void testApplicationManagedEntityManagerWithTransaction() throws Exception {
+ void applicationManagedEntityManagerWithTransaction() {
Object testEntity = new Object();
EntityTransaction mockTx = mock();
@@ -161,7 +156,7 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF
}
@Test
- void testApplicationManagedEntityManagerWithTransactionAndCommitException() throws Exception {
+ void applicationManagedEntityManagerWithTransactionAndCommitException() {
Object testEntity = new Object();
EntityTransaction mockTx = mock();
@@ -203,7 +198,7 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF
}
@Test
- void testApplicationManagedEntityManagerWithJtaTransaction() throws Exception {
+ void applicationManagedEntityManagerWithJtaTransaction() {
Object testEntity = new Object();
// This one's for the tx (shared)
@@ -240,67 +235,79 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF
verify(mockEmf).close();
}
- public LocalContainerEntityManagerFactoryBean parseValidPersistenceUnit(
- PersistenceUnitPostProcessor... postProcessors) {
+ @Test
+ void invalidPersistenceUnitName() {
+ assertThatIllegalArgumentException().isThrownBy(() ->
+ createEntityManagerFactoryBean("org/springframework/orm/jpa/domain/persistence.xml", null, "call me Bob"));
+ }
- return createEntityManagerFactoryBean(
- "org/springframework/orm/jpa/domain/persistence.xml", null,
- "Person", postProcessors);
+ @Test
+ void rejectsMissingPersistenceUnitInfo() {
+ LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
+ emfb.setPersistenceProviderClass(DummyContainerPersistenceProvider.class);
+
+ String entityManagerName = "call me Bob";
+ emfb.setPersistenceUnitName(entityManagerName);
+
+ assertThatIllegalArgumentException().isThrownBy(emfb::afterPropertiesSet);
}
@Test
- void testInvalidPersistenceUnitName() {
- assertThatIllegalArgumentException().isThrownBy(() ->
- createEntityManagerFactoryBean("org/springframework/orm/jpa/domain/persistence.xml", null, "call me Bob"));
+ void acceptsPersistenceConfiguration() {
+ DummyContainerPersistenceProvider persistenceProvider = new DummyContainerPersistenceProvider();
+ LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
+ emfb.setPersistenceProvider(persistenceProvider);
+
+ String entityManagerName = "call me Bob";
+ emfb.setPersistenceConfiguration(new PersistenceConfiguration(entityManagerName).
+ managedClass(DriversLicense.class).managedClass(Person.class));
+
+ emfb.afterPropertiesSet();
+ assertThat(persistenceProvider.actualPui.getPersistenceUnitName()).isEqualTo(entityManagerName);
+ assertThat(persistenceProvider.actualPui.getManagedClassNames()).containsExactly(
+ DriversLicense.class.getName(), Person.class.getName());
+ }
+
+
+ private LocalContainerEntityManagerFactoryBean parseValidPersistenceUnit(PersistenceUnitPostProcessor... postProcessors) {
+ return createEntityManagerFactoryBean(
+ "org/springframework/orm/jpa/domain/persistence.xml", null,
+ "Person", postProcessors);
}
@SuppressWarnings("unchecked")
- protected LocalContainerEntityManagerFactoryBean createEntityManagerFactoryBean(
+ private LocalContainerEntityManagerFactoryBean createEntityManagerFactoryBean(
String persistenceXml, Properties props, String entityManagerName,
PersistenceUnitPostProcessor... postProcessors) {
- // This will be set by DummyPersistenceProvider
- actualPui = null;
- actualProps = null;
+ DummyContainerPersistenceProvider persistenceProvider = new DummyContainerPersistenceProvider();
+ LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
- LocalContainerEntityManagerFactoryBean containerEmfb = new LocalContainerEntityManagerFactoryBean();
-
- containerEmfb.setPersistenceUnitName(entityManagerName);
- containerEmfb.setPersistenceProviderClass(DummyContainerPersistenceProvider.class);
+ emfb.setPersistenceUnitName(entityManagerName);
+ emfb.setPersistenceProvider(persistenceProvider);
if (props != null) {
- containerEmfb.setJpaProperties(props);
+ emfb.setJpaProperties(props);
}
- containerEmfb.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
- containerEmfb.setPersistenceXmlLocation(persistenceXml);
- containerEmfb.setPersistenceUnitPostProcessors(postProcessors);
- containerEmfb.afterPropertiesSet();
+ emfb.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
+ emfb.setPersistenceXmlLocation(persistenceXml);
+ emfb.setPersistenceUnitPostProcessors(postProcessors);
+ emfb.afterPropertiesSet();
- assertThat(actualPui.getPersistenceUnitName()).isEqualTo(entityManagerName);
+ assertThat(persistenceProvider.actualPui.getPersistenceUnitName()).isEqualTo(entityManagerName);
if (props != null) {
- assertThat(actualProps).isEqualTo(props);
+ assertThat(persistenceProvider.actualProps).isEqualTo(props);
}
- //checkInvariants(containerEmfb);
-
- return containerEmfb;
+ checkInvariants(emfb);
- //containerEmfb.destroy();
- //emfMc.verify();
+ return emfb;
}
- @Test
- void testRejectsMissingPersistenceUnitInfo() {
- LocalContainerEntityManagerFactoryBean containerEmfb = new LocalContainerEntityManagerFactoryBean();
- String entityManagerName = "call me Bob";
-
- containerEmfb.setPersistenceUnitName(entityManagerName);
- containerEmfb.setPersistenceProviderClass(DummyContainerPersistenceProvider.class);
- assertThatIllegalArgumentException().isThrownBy(
- containerEmfb::afterPropertiesSet);
- }
+ private static class DummyContainerPersistenceProvider implements PersistenceProvider {
+ PersistenceUnitInfo actualPui;
- private static class DummyContainerPersistenceProvider implements PersistenceProvider {
+ Map actualProps;
@Override
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo pui, Map map) {
diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBeanTests.java b/spring-orm/src/test/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBeanTests.java
index da0d3e7fd9d..2c08105a7a0 100644
--- a/spring-orm/src/test/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBeanTests.java
+++ b/spring-orm/src/test/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBeanTests.java
@@ -27,64 +27,138 @@ import jakarta.persistence.spi.ProviderUtil;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
+import org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager;
+
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verify;
/**
* @author Rod Johnson
+ * @author Juergen Hoeller
* @author Phillip Webb
*/
@SuppressWarnings("rawtypes")
class LocalEntityManagerFactoryBeanTests extends AbstractEntityManagerFactoryBeanTests {
- // Static fields set by inner class DummyPersistenceProvider
-
- private static String actualName;
-
- private static Map actualProps;
-
@AfterEach
void verifyClosed() {
verify(mockEmf).close();
}
+
@Test
- void testValidUsageWithDefaultProperties() throws Exception {
- testValidUsage(null);
+ void withDefault() {
+ DummyPersistenceProvider persistenceProvider = new DummyPersistenceProvider();
+ LocalEntityManagerFactoryBean emfb = new LocalEntityManagerFactoryBean();
+
+ emfb.setPersistenceProvider(persistenceProvider);
+ emfb.afterPropertiesSet();
+
+ assertThat(persistenceProvider.actualName).isSameAs(
+ DefaultPersistenceUnitManager.ORIGINAL_DEFAULT_PERSISTENCE_UNIT_NAME);
+ assertThat(persistenceProvider.actualProps).isEqualTo(emfb.getJpaPropertyMap());
+ checkInvariants(emfb);
+
+ emfb.destroy();
}
@Test
- void testValidUsageWithExplicitProperties() throws Exception {
- testValidUsage(new Properties());
+ void withName() {
+ DummyPersistenceProvider persistenceProvider = new DummyPersistenceProvider();
+ LocalEntityManagerFactoryBean emfb = new LocalEntityManagerFactoryBean();
+ String name = "call me Bob";
+
+ emfb.setPersistenceUnitName(name);
+ emfb.setPersistenceProvider(persistenceProvider);
+ emfb.afterPropertiesSet();
+
+ assertThat(persistenceProvider.actualName).isSameAs(name);
+ assertThat(persistenceProvider.actualProps).isEqualTo(emfb.getJpaPropertyMap());
+ checkInvariants(emfb);
+
+ emfb.destroy();
}
- @SuppressWarnings("unchecked")
- protected void testValidUsage(Properties props) {
- // This will be set by DummyPersistenceProvider
- actualName = null;
- actualProps = null;
+ @Test
+ void withNameAndExplicitProperties() {
+ DummyPersistenceProvider persistenceProvider = new DummyPersistenceProvider();
+ LocalEntityManagerFactoryBean emfb = new LocalEntityManagerFactoryBean();
+ String name = "call me Bob";
+ Properties props = new Properties();
+ props.setProperty("myProp", "myVal");
+
+ emfb.setPersistenceUnitName(name);
+ emfb.setPersistenceProvider(persistenceProvider);
+ emfb.setJpaProperties(props);
+ emfb.afterPropertiesSet();
+
+ assertThat(persistenceProvider.actualName).isSameAs(name);
+ assertThat(persistenceProvider.actualProps).isEqualTo(props);
+ checkInvariants(emfb);
+
+ emfb.destroy();
+ }
- LocalEntityManagerFactoryBean lemfb = new LocalEntityManagerFactoryBean();
- String entityManagerName = "call me Bob";
+ @Test
+ void withDefaultPersistenceConfiguration() {
+ DummyPersistenceProvider persistenceProvider = new DummyPersistenceProvider();
+ LocalEntityManagerFactoryBean emfb = new LocalEntityManagerFactoryBean();
- lemfb.setPersistenceUnitName(entityManagerName);
- lemfb.setPersistenceProviderClass(DummyPersistenceProvider.class);
- if (props != null) {
- lemfb.setJpaProperties(props);
- }
- lemfb.afterPropertiesSet();
+ emfb.getPersistenceConfiguration().property("myProp", "myVal");
+ emfb.setPersistenceProvider(persistenceProvider);
+ emfb.afterPropertiesSet();
- assertThat(actualName).isSameAs(entityManagerName);
- if (props != null) {
- assertThat(actualProps).isEqualTo(props);
- }
- checkInvariants(lemfb);
+ assertThat(persistenceProvider.actualName).isSameAs(
+ DefaultPersistenceUnitManager.ORIGINAL_DEFAULT_PERSISTENCE_UNIT_NAME);
+ assertThat(persistenceProvider.actualProps).containsEntry("myProp", "myVal");
+ checkInvariants(emfb);
+
+ emfb.destroy();
+ }
+
+ @Test
+ void withNameAndDefaultPersistenceConfiguration() {
+ DummyPersistenceProvider persistenceProvider = new DummyPersistenceProvider();
+ LocalEntityManagerFactoryBean emfb = new LocalEntityManagerFactoryBean();
+ String name = "call me Bob";
+
+ emfb.setPersistenceUnitName(name);
+ emfb.getPersistenceConfiguration().property("myProp", "myVal");
+ emfb.setPersistenceProvider(persistenceProvider);
+ emfb.afterPropertiesSet();
+
+ assertThat(persistenceProvider.actualName).isSameAs(name);
+ assertThat(persistenceProvider.actualProps).containsEntry("myProp", "myVal");
+ checkInvariants(emfb);
- lemfb.destroy();
+ emfb.destroy();
}
+ @Test
+ void withExplicitPersistenceConfiguration() {
+ DummyPersistenceProvider persistenceProvider = new DummyPersistenceProvider();
+ LocalEntityManagerFactoryBean emfb = new LocalEntityManagerFactoryBean();
+ String name = "call me Bob";
+ PersistenceConfiguration config = new PersistenceConfiguration(name);
+ config.property("myProp", "myVal");
+
+ emfb.setPersistenceConfiguration(config);
+ emfb.setPersistenceProvider(persistenceProvider);
+ emfb.afterPropertiesSet();
+
+ assertThat(persistenceProvider.actualName).isSameAs(name);
+ assertThat(persistenceProvider.actualProps).containsEntry("myProp", "myVal");
+ checkInvariants(emfb);
+
+ emfb.destroy();
+ }
+
+
+ private static class DummyPersistenceProvider implements PersistenceProvider {
- protected static class DummyPersistenceProvider implements PersistenceProvider {
+ String actualName;
+
+ Map actualProps;
@Override
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo pui, Map map) {
@@ -99,8 +173,10 @@ class LocalEntityManagerFactoryBeanTests extends AbstractEntityManagerFactoryBea
}
@Override
- public EntityManagerFactory createEntityManagerFactory(PersistenceConfiguration persistenceConfiguration) {
- throw new UnsupportedOperationException();
+ public EntityManagerFactory createEntityManagerFactory(PersistenceConfiguration config) {
+ actualName = config.name();
+ actualProps = config.properties();
+ return mockEmf;
}
@Override
@@ -108,13 +184,11 @@ class LocalEntityManagerFactoryBeanTests extends AbstractEntityManagerFactoryBea
throw new UnsupportedOperationException();
}
- // JPA 2.1 method
@Override
public void generateSchema(PersistenceUnitInfo persistenceUnitInfo, Map map) {
throw new UnsupportedOperationException();
}
- // JPA 2.1 method
@Override
public boolean generateSchema(String persistenceUnitName, Map map) {
throw new UnsupportedOperationException();
diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/persistenceunit/PersistenceXmlParsingTests.java b/spring-orm/src/test/java/org/springframework/orm/jpa/persistenceunit/PersistenceXmlParsingTests.java
index 3bd3ba4b3d0..f693018a469 100644
--- a/spring-orm/src/test/java/org/springframework/orm/jpa/persistenceunit/PersistenceXmlParsingTests.java
+++ b/spring-orm/src/test/java/org/springframework/orm/jpa/persistenceunit/PersistenceXmlParsingTests.java
@@ -46,7 +46,6 @@ import static org.assertj.core.api.Assertions.assertThatRuntimeException;
* @author Juergen Hoeller
* @author Nicholas Williams
*/
-@SuppressWarnings("removal")
class PersistenceXmlParsingTests {
@Test