Browse Source

Add support for JPA 3.2 PersistenceConfiguration

See gh-35662
pull/35665/head
Juergen Hoeller 6 months ago
parent
commit
beb224e3f9
  1. 4
      spring-orm/src/main/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBean.java
  2. 28
      spring-orm/src/main/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.java
  3. 91
      spring-orm/src/main/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBean.java
  4. 56
      spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java
  5. 24
      spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/MutablePersistenceUnitInfo.java
  6. 93
      spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/SpringPersistenceUnitInfo.java
  7. 20
      spring-orm/src/test/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBeanTests.java
  8. 115
      spring-orm/src/test/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBeanTests.java
  9. 142
      spring-orm/src/test/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBeanTests.java
  10. 1
      spring-orm/src/test/java/org/springframework/orm/jpa/persistenceunit/PersistenceXmlParsingTests.java

4
spring-orm/src/main/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBean.java

@ -71,7 +71,9 @@ import org.springframework.util.CollectionUtils; @@ -71,7 +71,9 @@ import org.springframework.util.CollectionUtils;
* making {@code EntityManager} available for dependency injection as well.
*
* <p>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.
*
* <p>Implements support for standard JPA configuration conventions as well as
* Spring's customizable {@link JpaVendorAdapter} mechanism, and controls the

28
spring-orm/src/main/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.java

@ -21,6 +21,7 @@ import java.util.List; @@ -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 @@ -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.
* <p>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 @@ -424,15 +441,16 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
* Determine the PersistenceUnitInfo to use for the EntityManagerFactory
* created by this bean.
* <p>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();

91
spring-orm/src/main/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBean.java

@ -20,10 +20,14 @@ import javax.sql.DataSource; @@ -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 @@ -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}.
* <p>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).
* <p>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 @@ -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 @@ -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()));
}
}

56
spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java

@ -27,6 +27,7 @@ import java.util.Set; @@ -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 @@ -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 @@ -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 @@ -492,7 +505,8 @@ public class DefaultPersistenceUnitManager
private List<SpringPersistenceUnitInfo> readPersistenceUnitInfos() {
List<SpringPersistenceUnitInfo> 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 @@ -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 @@ -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 @@ -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;
}
/**

24
spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/MutablePersistenceUnitInfo.java

@ -60,16 +60,12 @@ public class MutablePersistenceUnitInfo { @@ -60,16 +60,12 @@ public class MutablePersistenceUnitInfo {
private @Nullable String persistenceProviderClassName;
private @Nullable String scopeAnnotationName;
private final List<String> qualifierAnnotationNames = new ArrayList<>();
private @Nullable PersistenceUnitTransactionType transactionType;
private @Nullable DataSource nonJtaDataSource;
private @Nullable DataSource jtaDataSource;
private @Nullable DataSource nonJtaDataSource;
private final List<String> mappingFileNames = new ArrayList<>();
private final List<URL> jarFileUrls = new ArrayList<>();
@ -109,22 +105,6 @@ public class MutablePersistenceUnitInfo { @@ -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<String> getQualifierAnnotationNames() {
return this.qualifierAnnotationNames;
}
public void setTransactionType(PersistenceUnitTransactionType transactionType) {
this.transactionType = transactionType;
}

93
spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/SpringPersistenceUnitInfo.java

@ -19,7 +19,11 @@ package org.springframework.orm.jpa.persistenceunit; @@ -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; @@ -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 { @@ -55,6 +61,10 @@ public class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo {
private @Nullable ClassLoader classLoader;
private @Nullable String scopeAnnotationName;
private final List<String> qualifierAnnotationNames = new ArrayList<>();
/**
* Construct a new SpringPersistenceUnitInfo for custom purposes.
@ -132,6 +142,87 @@ public class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo { @@ -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<String> 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.
* <p>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<URL> urlList = ((List<URL>) 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 { @@ -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

20
spring-orm/src/test/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBeanTests.java

@ -39,27 +39,27 @@ public abstract class AbstractEntityManagerFactoryBeanTests { @@ -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());
}

115
spring-orm/src/test/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBeanTests.java

@ -36,6 +36,8 @@ import org.springframework.core.testfixture.io.SerializationTestUtils; @@ -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; @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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) {

142
spring-orm/src/test/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBeanTests.java

@ -27,64 +27,138 @@ import jakarta.persistence.spi.ProviderUtil; @@ -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 @@ -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 @@ -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();

1
spring-orm/src/test/java/org/springframework/orm/jpa/persistenceunit/PersistenceXmlParsingTests.java

@ -46,7 +46,6 @@ import static org.assertj.core.api.Assertions.assertThatRuntimeException; @@ -46,7 +46,6 @@ import static org.assertj.core.api.Assertions.assertThatRuntimeException;
* @author Juergen Hoeller
* @author Nicholas Williams
*/
@SuppressWarnings("removal")
class PersistenceXmlParsingTests {
@Test

Loading…
Cancel
Save