diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java b/spring-orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java index 72b050dd902..72f377b41fb 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java @@ -30,7 +30,6 @@ import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.EntityTransaction; import jakarta.persistence.TransactionRequiredException; import jakarta.persistence.spi.PersistenceUnitInfo; -import jakarta.persistence.spi.PersistenceUnitTransactionType; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jspecify.annotations.Nullable; @@ -38,6 +37,7 @@ import org.jspecify.annotations.Nullable; import org.springframework.core.Ordered; import org.springframework.dao.DataAccessException; import org.springframework.dao.support.PersistenceExceptionTranslator; +import org.springframework.orm.jpa.persistenceunit.SmartPersistenceUnitInfo; import org.springframework.transaction.support.ResourceHolderSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; @@ -192,14 +192,13 @@ public abstract class ExtendedEntityManagerCreator { * transactions (according to the JPA 2.1 SynchronizationType rules) * @return the EntityManager proxy */ - @SuppressWarnings("removal") private static EntityManager createProxy(EntityManager rawEntityManager, EntityManagerFactoryInfo emfInfo, boolean containerManaged, boolean synchronizedWithTransaction) { Assert.notNull(emfInfo, "EntityManagerFactoryInfo must not be null"); JpaDialect jpaDialect = emfInfo.getJpaDialect(); PersistenceUnitInfo pui = emfInfo.getPersistenceUnitInfo(); - Boolean jta = (pui != null ? pui.getTransactionType() == PersistenceUnitTransactionType.JTA : null); + Boolean jta = (pui instanceof SmartPersistenceUnitInfo spui ? spui.isConfiguredForJta() : null); return createProxy(rawEntityManager, emfInfo.getEntityManagerInterface(), emfInfo.getBeanClassLoader(), jpaDialect, jta, containerManaged, synchronizedWithTransaction); } 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 1ffc7aa9f4b..2c237529ea8 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 @@ -134,7 +134,7 @@ public class DefaultPersistenceUnitManager private final Set persistenceUnitInfoNames = new HashSet<>(); - private final Map persistenceUnitInfos = new HashMap<>(); + private final Map persistenceUnitInfos = new HashMap<>(); /** @@ -620,26 +620,25 @@ public class DefaultPersistenceUnitManager /** - * Return the specified PersistenceUnitInfo from this manager's cache - * of processed persistence units, keeping it in the cache (i.e. not - * 'obtaining' it for use but rather just accessing it for post-processing). + * Return the specified {@link MutablePersistenceUnitInfo} from this manager's cache + * of processed persistence units, keeping it in the cache (i.e. not 'obtaining' it + * for use but rather just accessing it for post-processing). *

This can be used in {@link #postProcessPersistenceUnitInfo} implementations, * detecting existing persistence units of the same name and potentially merging them. * @param persistenceUnitName the name of the desired persistence unit * @return the PersistenceUnitInfo in mutable form, or {@code null} if not available */ protected final @Nullable MutablePersistenceUnitInfo getPersistenceUnitInfo(String persistenceUnitName) { - PersistenceUnitInfo pui = this.persistenceUnitInfos.get(persistenceUnitName); - return (MutablePersistenceUnitInfo) pui; + return this.persistenceUnitInfos.get(persistenceUnitName); } /** - * Hook method allowing subclasses to customize each PersistenceUnitInfo. + * Hook method allowing subclasses to customize each {@link MutablePersistenceUnitInfo}. *

The default implementation delegates to all registered PersistenceUnitPostProcessors. * It is usually preferable to register further entity classes, jar files etc there * rather than in a subclass of this manager, to be able to reuse the post-processors. - * @param pui the chosen PersistenceUnitInfo, as read from {@code persistence.xml}. - * Passed in as MutablePersistenceUnitInfo. + * @param pui the chosen persistence unit configuration, as read from + * {@code persistence.xml}. Passed in as MutablePersistenceUnitInfo. * @see #setPersistenceUnitPostProcessors */ protected void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) { @@ -674,14 +673,14 @@ public class DefaultPersistenceUnitManager if (this.persistenceUnitInfos.size() > 1 && this.defaultPersistenceUnitName != null) { return obtainPersistenceUnitInfo(this.defaultPersistenceUnitName); } - PersistenceUnitInfo pui = this.persistenceUnitInfos.values().iterator().next(); + SpringPersistenceUnitInfo pui = this.persistenceUnitInfos.values().iterator().next(); this.persistenceUnitInfos.clear(); - return pui; + return pui.toSmartPersistenceUnitInfo(); } @Override public PersistenceUnitInfo obtainPersistenceUnitInfo(String persistenceUnitName) { - PersistenceUnitInfo pui = this.persistenceUnitInfos.remove(persistenceUnitName); + SpringPersistenceUnitInfo pui = this.persistenceUnitInfos.remove(persistenceUnitName); if (pui == null) { if (!this.persistenceUnitInfoNames.contains(persistenceUnitName)) { throw new IllegalArgumentException( @@ -692,7 +691,7 @@ public class DefaultPersistenceUnitManager "Persistence unit with name '" + persistenceUnitName + "' already obtained"); } } - return pui; + return pui.toSmartPersistenceUnitInfo(); } } 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 587f0426b49..20c0386eef5 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 @@ -23,30 +23,31 @@ import java.util.Properties; import javax.sql.DataSource; +import jakarta.persistence.PersistenceUnitTransactionType; import jakarta.persistence.SharedCacheMode; import jakarta.persistence.ValidationMode; -import jakarta.persistence.spi.ClassTransformer; -import jakarta.persistence.spi.PersistenceUnitTransactionType; import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; /** - * Spring's base implementation of the JPA + * Spring's mutable equivalent of the JPA * {@link jakarta.persistence.spi.PersistenceUnitInfo} interface, * used to bootstrap an {@code EntityManagerFactory} in a container. * *

This implementation is largely a JavaBean, offering mutators * for all standard {@code PersistenceUnitInfo} properties. + * As of 7.0, it does not implement {@code PersistenceUnitInfo} but + * rather serves as the state behind a runtime {@code PersistenceUnitInfo} + * (for achieving compatibility between JPA 3.2 and 4.0 and for preventing + * late mutation attempts through {@code PersistenceUnitInfo} downcasts). * * @author Rod Johnson * @author Juergen Hoeller * @author Costin Leau * @since 2.0 */ -@SuppressWarnings("removal") -public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { +public class MutablePersistenceUnitInfo { private @Nullable String persistenceUnitName; @@ -89,7 +90,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.persistenceUnitName = persistenceUnitName; } - @Override public @Nullable String getPersistenceUnitName() { return this.persistenceUnitName; } @@ -98,7 +98,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.persistenceProviderClassName = persistenceProviderClassName; } - @Override public @Nullable String getPersistenceProviderClassName() { return this.persistenceProviderClassName; } @@ -107,7 +106,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.scopeAnnotationName = scopeAnnotationName; } - @Override public @Nullable String getScopeAnnotationName() { return this.scopeAnnotationName; } @@ -116,7 +114,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.qualifierAnnotationNames.add(qualifierAnnotationName); } - @Override public List getQualifierAnnotationNames() { return this.qualifierAnnotationNames; } @@ -125,7 +122,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.transactionType = transactionType; } - @Override public PersistenceUnitTransactionType getTransactionType() { if (this.transactionType != null) { return this.transactionType; @@ -140,7 +136,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.jtaDataSource = jtaDataSource; } - @Override public @Nullable DataSource getJtaDataSource() { return this.jtaDataSource; } @@ -149,7 +144,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.nonJtaDataSource = nonJtaDataSource; } - @Override public @Nullable DataSource getNonJtaDataSource() { return this.nonJtaDataSource; } @@ -158,7 +152,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.mappingFileNames.add(mappingFileName); } - @Override public List getMappingFileNames() { return this.mappingFileNames; } @@ -167,7 +160,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.jarFileUrls.add(jarFileUrl); } - @Override public List getJarFileUrls() { return this.jarFileUrls; } @@ -176,7 +168,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.persistenceUnitRootUrl = persistenceUnitRootUrl; } - @Override public @Nullable URL getPersistenceUnitRootUrl() { return this.persistenceUnitRootUrl; } @@ -190,7 +181,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.managedClassNames.add(managedClassName); } - @Override public List getManagedClassNames() { return this.managedClassNames; } @@ -208,7 +198,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.managedPackages.add(packageName); } - @Override public List getManagedPackages() { return this.managedPackages; } @@ -217,7 +206,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.excludeUnlistedClasses = excludeUnlistedClasses; } - @Override public boolean excludeUnlistedClasses() { return this.excludeUnlistedClasses; } @@ -226,7 +214,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.sharedCacheMode = sharedCacheMode; } - @Override public SharedCacheMode getSharedCacheMode() { return this.sharedCacheMode; } @@ -235,7 +222,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.validationMode = validationMode; } - @Override public ValidationMode getValidationMode() { return this.validationMode; } @@ -249,7 +235,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.properties = properties; } - @Override public Properties getProperties() { return this.properties; } @@ -258,12 +243,10 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { this.persistenceXMLSchemaVersion = persistenceXMLSchemaVersion; } - @Override public String getPersistenceXMLSchemaVersion() { return this.persistenceXMLSchemaVersion; } - @Override public void setPersistenceProviderPackageName(@Nullable String persistenceProviderPackageName) { this.persistenceProviderPackageName = persistenceProviderPackageName; } @@ -273,32 +256,6 @@ public class MutablePersistenceUnitInfo implements SmartPersistenceUnitInfo { } - /** - * This implementation returns the default ClassLoader. - * @see org.springframework.util.ClassUtils#getDefaultClassLoader() - */ - @Override - public @Nullable ClassLoader getClassLoader() { - return ClassUtils.getDefaultClassLoader(); - } - - /** - * This implementation throws an UnsupportedOperationException. - */ - @Override - public void addTransformer(ClassTransformer classTransformer) { - throw new UnsupportedOperationException("addTransformer not supported"); - } - - /** - * This implementation throws an UnsupportedOperationException. - */ - @Override - public ClassLoader getNewTempClassLoader() { - throw new UnsupportedOperationException("getNewTempClassLoader not supported"); - } - - @Override public String toString() { return "PersistenceUnitInfo: name '" + this.persistenceUnitName + diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/PersistenceUnitPostProcessor.java b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/PersistenceUnitPostProcessor.java index adce335b6f1..cd0366dc454 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/PersistenceUnitPostProcessor.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/PersistenceUnitPostProcessor.java @@ -17,9 +17,11 @@ package org.springframework.orm.jpa.persistenceunit; /** - * Callback interface for post-processing a JPA PersistenceUnitInfo. - * Implementations can be registered with a DefaultPersistenceUnitManager - * or via a LocalContainerEntityManagerFactoryBean. + * Callback interface for post-processing a {@link MutablePersistenceUnitInfo} + * configuration that Spring prepares for JPA persistence unit bootstrapping. + * + *

Implementations can be registered with a {@link DefaultPersistenceUnitManager} + * or via a {@link org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean}. * * @author Juergen Hoeller * @since 2.0 @@ -29,10 +31,10 @@ package org.springframework.orm.jpa.persistenceunit; public interface PersistenceUnitPostProcessor { /** - * Post-process the given PersistenceUnitInfo, for example registering - * further entity classes and jar files. - * @param pui the chosen PersistenceUnitInfo, as read from {@code persistence.xml}. - * Passed in as MutablePersistenceUnitInfo. + * Post-process the given {@link MutablePersistenceUnitInfo}, + * for example registering further entity classes and jar files. + * @param pui the chosen persistence unit configuration, as read from + * {@code persistence.xml}. Passed in as MutablePersistenceUnitInfo. */ void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui); diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/PersistenceUnitReader.java b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/PersistenceUnitReader.java index 5336bca34dd..ab1fe866387 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/PersistenceUnitReader.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/PersistenceUnitReader.java @@ -26,9 +26,9 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import jakarta.persistence.PersistenceUnitTransactionType; import jakarta.persistence.SharedCacheMode; import jakarta.persistence.ValidationMode; -import jakarta.persistence.spi.PersistenceUnitTransactionType; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jspecify.annotations.Nullable; @@ -189,7 +189,6 @@ final class PersistenceUnitReader { /** * Parse the unit info DOM element. */ - @SuppressWarnings("removal") SpringPersistenceUnitInfo parsePersistenceUnitInfo( Element persistenceUnit, String version, @Nullable URL rootUrl) throws IOException { diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/SmartPersistenceUnitInfo.java b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/SmartPersistenceUnitInfo.java index 6977e9250e1..1cfb91728d5 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/SmartPersistenceUnitInfo.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/SmartPersistenceUnitInfo.java @@ -47,4 +47,13 @@ public interface SmartPersistenceUnitInfo extends PersistenceUnitInfo { */ void setPersistenceProviderPackageName(String persistenceProviderPackageName); + /** + * Determine whether this persistence unit is configured for JTA transactions. + *

This allows for a quick check without referring to the JPA transaction type enum + * (primarily for achieving compatibility between JPA 3.2 and 4.0). + * @since 7.0 + * @see jakarta.persistence.PersistenceUnitTransactionType#JTA + */ + boolean isConfiguredForJta(); + } 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 7c078483a3e..af7f587ae86 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 @@ -16,6 +16,11 @@ package org.springframework.orm.jpa.persistenceunit; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import jakarta.persistence.PersistenceUnitTransactionType; import jakarta.persistence.spi.ClassTransformer; import org.apache.commons.logging.LogFactory; import org.jspecify.annotations.Nullable; @@ -24,6 +29,7 @@ import org.springframework.core.DecoratingClassLoader; import org.springframework.instrument.classloading.LoadTimeWeaver; import org.springframework.instrument.classloading.SimpleThrowawayClassLoader; import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; /** * Subclass of {@link MutablePersistenceUnitInfo} that adds instrumentation hooks based on @@ -67,7 +73,6 @@ class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo { * This implementation returns the LoadTimeWeaver's instrumentable ClassLoader, * if specified. */ - @Override public @Nullable ClassLoader getClassLoader() { return this.classLoader; } @@ -75,7 +80,6 @@ class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo { /** * This implementation delegates to the LoadTimeWeaver, if specified. */ - @Override public void addTransformer(ClassTransformer classTransformer) { if (this.loadTimeWeaver != null) { this.loadTimeWeaver.addTransformer(new ClassFileTransformerAdapter(classTransformer)); @@ -88,7 +92,6 @@ class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo { /** * This implementation delegates to the LoadTimeWeaver, if specified. */ - @Override public ClassLoader getNewTempClassLoader() { ClassLoader tcl = (this.loadTimeWeaver != null ? this.loadTimeWeaver.getThrowawayClassLoader() : new SimpleThrowawayClassLoader(this.classLoader)); @@ -99,4 +102,42 @@ class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo { return tcl; } + + /** + * Expose a {@link SmartPersistenceUnitInfo} proxy for the persistence unit + * configuration in this {@link MutablePersistenceUnitInfo} instance. + * @since 7.0 + */ + public SmartPersistenceUnitInfo toSmartPersistenceUnitInfo() { + return (SmartPersistenceUnitInfo) Proxy.newProxyInstance(getClass().getClassLoader(), + new Class[] {SmartPersistenceUnitInfo.class}, + new JpaPersistenceUnitInfoInvocationHandler()); + } + + + private class JpaPersistenceUnitInfoInvocationHandler implements InvocationHandler { + + @SuppressWarnings("unchecked") + @Override + public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + // Fast path for SmartPersistenceUnitInfo JTA check + if (method.getName().equals("isConfiguredForJta")) { + return (getTransactionType() == PersistenceUnitTransactionType.JTA); + } + + // Regular methods to be delegated to SpringPersistenceUnitInfo + Method targetMethod = SpringPersistenceUnitInfo.class.getMethod(method.getName(), method.getParameterTypes()); + ReflectionUtils.makeAccessible(targetMethod); + Object returnValue = ReflectionUtils.invokeMethod(targetMethod, SpringPersistenceUnitInfo.this, args); + + // Special handling for JPA 3.2 vs 4.0 getTransactionType() return type + Class returnType = method.getReturnType(); + if (returnType.isEnum() && returnValue != null && !returnType.isInstance(returnValue)) { + return Enum.valueOf((Class) returnType, returnValue.toString()); + } + + return returnValue; + } + } + } diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaVendorAdapter.java b/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaVendorAdapter.java index bb1eb81b4d8..b76b07c7139 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaVendorAdapter.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaVendorAdapter.java @@ -23,7 +23,6 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.spi.PersistenceProvider; import jakarta.persistence.spi.PersistenceUnitInfo; -import jakarta.persistence.spi.PersistenceUnitTransactionType; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.AvailableSettings; @@ -39,6 +38,8 @@ import org.hibernate.dialect.SybaseDialect; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.jspecify.annotations.Nullable; +import org.springframework.orm.jpa.persistenceunit.SmartPersistenceUnitInfo; + /** * {@link org.springframework.orm.jpa.JpaVendorAdapter} implementation for Hibernate. * Compatible with Hibernate ORM 7.x. @@ -120,11 +121,10 @@ public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter { return "org.hibernate"; } - @SuppressWarnings("removal") @Override public Map getJpaPropertyMap(PersistenceUnitInfo pui) { return buildJpaPropertyMap(this.jpaDialect.prepareConnection && - pui.getTransactionType() != PersistenceUnitTransactionType.JTA); + (pui instanceof SmartPersistenceUnitInfo spui && !spui.isConfiguredForJta())); } @Override 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 8b04293a13b..57294dd321a 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 @@ -25,9 +25,9 @@ import jakarta.persistence.EntityTransaction; import jakarta.persistence.OptimisticLockException; import jakarta.persistence.PersistenceConfiguration; import jakarta.persistence.PersistenceException; +import jakarta.persistence.PersistenceUnitTransactionType; import jakarta.persistence.spi.PersistenceProvider; import jakarta.persistence.spi.PersistenceUnitInfo; -import jakarta.persistence.spi.PersistenceUnitTransactionType; import jakarta.persistence.spi.ProviderUtil; import org.junit.jupiter.api.Test; @@ -36,7 +36,7 @@ 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.persistenceunit.MutablePersistenceUnitInfo; +import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.interceptor.DefaultTransactionAttribute; @@ -215,9 +215,8 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF given(mockEmf.createEntityManager()).willReturn(sharedEm, mockEm); - LocalContainerEntityManagerFactoryBean cefb = parseValidPersistenceUnit(); - MutablePersistenceUnitInfo pui = ((MutablePersistenceUnitInfo) cefb.getPersistenceUnitInfo()); - pui.setTransactionType(PersistenceUnitTransactionType.JTA); + LocalContainerEntityManagerFactoryBean cefb = parseValidPersistenceUnit( + pui -> pui.setTransactionType(PersistenceUnitTransactionType.JTA)); JpaTransactionManager jpatm = new JpaTransactionManager(); jpatm.setEntityManagerFactory(cefb.getObject()); @@ -241,10 +240,12 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF verify(mockEmf).close(); } - public LocalContainerEntityManagerFactoryBean parseValidPersistenceUnit() throws Exception { + public LocalContainerEntityManagerFactoryBean parseValidPersistenceUnit( + PersistenceUnitPostProcessor... postProcessors) { + return createEntityManagerFactoryBean( "org/springframework/orm/jpa/domain/persistence.xml", null, - "Person"); + "Person", postProcessors); } @Test @@ -255,7 +256,8 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF @SuppressWarnings("unchecked") protected LocalContainerEntityManagerFactoryBean createEntityManagerFactoryBean( - String persistenceXml, Properties props, String entityManagerName) { + String persistenceXml, Properties props, String entityManagerName, + PersistenceUnitPostProcessor... postProcessors) { // This will be set by DummyPersistenceProvider actualPui = null; @@ -270,6 +272,7 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF } containerEmfb.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver()); containerEmfb.setPersistenceXmlLocation(persistenceXml); + containerEmfb.setPersistenceUnitPostProcessors(postProcessors); containerEmfb.afterPropertiesSet(); assertThat(actualPui.getPersistenceUnitName()).isEqualTo(entityManagerName); diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManagerTests.java b/spring-orm/src/test/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManagerTests.java index b55e88a6d43..feff0ea1f9e 100644 --- a/spring-orm/src/test/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManagerTests.java +++ b/spring-orm/src/test/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManagerTests.java @@ -16,6 +16,7 @@ package org.springframework.orm.jpa.persistenceunit; +import jakarta.persistence.spi.PersistenceUnitInfo; import org.junit.jupiter.api.Test; import org.springframework.context.testfixture.index.CandidateComponentsTestClassLoader; @@ -25,17 +26,17 @@ import org.springframework.orm.jpa.domain.Person; import static org.assertj.core.api.Assertions.assertThat; - - /** * Tests for {@link DefaultPersistenceUnitManager}. * * @author Stephane Nicoll + * @author Juergen Hoeller */ class DefaultPersistenceUnitManagerTests { private final DefaultPersistenceUnitManager manager = new DefaultPersistenceUnitManager(); + @Test void defaultDomainWithScan() { this.manager.setPackagesToScan("org.springframework.orm.jpa.domain"); @@ -54,15 +55,11 @@ class DefaultPersistenceUnitManagerTests { } private void testDefaultDomain() { - SpringPersistenceUnitInfo puInfo = buildDefaultPersistenceUnitInfo(); - assertThat(puInfo.getManagedClassNames()).contains( + this.manager.preparePersistenceUnitInfos(); + PersistenceUnitInfo pui = this.manager.obtainDefaultPersistenceUnitInfo(); + assertThat(pui.getManagedClassNames()).contains( "org.springframework.orm.jpa.domain.Person", "org.springframework.orm.jpa.domain.DriversLicense"); } - private SpringPersistenceUnitInfo buildDefaultPersistenceUnitInfo() { - this.manager.preparePersistenceUnitInfos(); - return (SpringPersistenceUnitInfo) this.manager.obtainDefaultPersistenceUnitInfo(); - } - } 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 4671b64456b..3bd3ba4b3d0 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 @@ -22,8 +22,7 @@ import java.util.Map; import javax.sql.DataSource; -import jakarta.persistence.spi.PersistenceUnitInfo; -import jakarta.persistence.spi.PersistenceUnitTransactionType; +import jakarta.persistence.PersistenceUnitTransactionType; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -55,7 +54,7 @@ class PersistenceXmlParsingTests { PersistenceUnitReader reader = new PersistenceUnitReader( new PathMatchingResourcePatternResolver(), new JndiDataSourceLookup()); String resource = "/org/springframework/orm/jpa/META-INF/persistence.xml"; - PersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); + SpringPersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); assertThat(info).isNotNull(); assertThat(info).hasSize(1); @@ -72,7 +71,7 @@ class PersistenceXmlParsingTests { PersistenceUnitReader reader = new PersistenceUnitReader( new PathMatchingResourcePatternResolver(), new JndiDataSourceLookup()); String resource = "/org/springframework/orm/jpa/persistence-example1.xml"; - PersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); + SpringPersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); assertThat(info).isNotNull(); assertThat(info).hasSize(1); @@ -86,7 +85,7 @@ class PersistenceXmlParsingTests { PersistenceUnitReader reader = new PersistenceUnitReader( new PathMatchingResourcePatternResolver(), new JndiDataSourceLookup()); String resource = "/org/springframework/orm/jpa/persistence-example2.xml"; - PersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); + SpringPersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); assertThat(info).isNotNull(); assertThat(info).hasSize(1); @@ -104,7 +103,7 @@ class PersistenceXmlParsingTests { PersistenceUnitReader reader = new PersistenceUnitReader( new PathMatchingResourcePatternResolver(), new JndiDataSourceLookup()); String resource = "/org/springframework/orm/jpa/persistence-example3.xml"; - PersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); + SpringPersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); assertThat(info).isNotNull(); assertThat(info).hasSize(1); @@ -130,7 +129,7 @@ class PersistenceXmlParsingTests { PersistenceUnitReader reader = new PersistenceUnitReader( new PathMatchingResourcePatternResolver(), new JndiDataSourceLookup()); String resource = "/org/springframework/orm/jpa/persistence-example4.xml"; - PersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); + SpringPersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); assertThat(info).isNotNull(); assertThat(info).hasSize(1); @@ -154,7 +153,7 @@ class PersistenceXmlParsingTests { PersistenceUnitReader reader = new PersistenceUnitReader( new PathMatchingResourcePatternResolver(), new JndiDataSourceLookup()); String resource = "/org/springframework/orm/jpa/persistence-example5.xml"; - PersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); + SpringPersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); assertThat(info).isNotNull(); assertThat(info).hasSize(1); @@ -184,11 +183,11 @@ class PersistenceXmlParsingTests { dataSourceLookup.setDataSources(dataSources); PersistenceUnitReader reader = new PersistenceUnitReader( new PathMatchingResourcePatternResolver(), dataSourceLookup); - PersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); + SpringPersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); assertThat(info).hasSize(2); - PersistenceUnitInfo pu1 = info[0]; + SpringPersistenceUnitInfo pu1 = info[0]; assertThat(pu1.getPersistenceUnitName()).isEqualTo("pu1"); @@ -211,7 +210,7 @@ class PersistenceXmlParsingTests { assertThat(pu1.excludeUnlistedClasses()).as("Exclude unlisted should default false in 1.0.").isFalse(); - PersistenceUnitInfo pu2 = info[1]; + SpringPersistenceUnitInfo pu2 = info[1]; assertThat(pu2.getTransactionType()).isSameAs(PersistenceUnitTransactionType.JTA); assertThat(pu2.getPersistenceProviderClassName()).isEqualTo("com.acme.AcmePersistence"); @@ -235,7 +234,7 @@ class PersistenceXmlParsingTests { PersistenceUnitReader reader = new PersistenceUnitReader( new PathMatchingResourcePatternResolver(), new JndiDataSourceLookup()); String resource = "/org/springframework/orm/jpa/persistence-example6.xml"; - PersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); + SpringPersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); assertThat(info).hasSize(1); assertThat(info[0].getPersistenceUnitName()).isEqualTo("pu"); assertThat(info[0].getProperties()).isEmpty(); @@ -287,27 +286,27 @@ class PersistenceXmlParsingTests { PersistenceUnitReader reader = new PersistenceUnitReader( new PathMatchingResourcePatternResolver(), new JndiDataSourceLookup()); String resource = "/org/springframework/orm/jpa/persistence-exclude-1.0.xml"; - PersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); + SpringPersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); assertThat(info).isNotNull(); assertThat(info.length).as("The number of persistence units is incorrect.").isEqualTo(4); - PersistenceUnitInfo noExclude = info[0]; + SpringPersistenceUnitInfo noExclude = info[0]; assertThat(noExclude).as("noExclude should not be null.").isNotNull(); assertThat(noExclude.getPersistenceUnitName()).as("noExclude name is not correct.").isEqualTo("NoExcludeElement"); assertThat(noExclude.excludeUnlistedClasses()).as("Exclude unlisted should default false in 1.0.").isFalse(); - PersistenceUnitInfo emptyExclude = info[1]; + SpringPersistenceUnitInfo emptyExclude = info[1]; assertThat(emptyExclude).as("emptyExclude should not be null.").isNotNull(); assertThat(emptyExclude.getPersistenceUnitName()).as("emptyExclude name is not correct.").isEqualTo("EmptyExcludeElement"); assertThat(emptyExclude.excludeUnlistedClasses()).as("emptyExclude should be true.").isTrue(); - PersistenceUnitInfo trueExclude = info[2]; + SpringPersistenceUnitInfo trueExclude = info[2]; assertThat(trueExclude).as("trueExclude should not be null.").isNotNull(); assertThat(trueExclude.getPersistenceUnitName()).as("trueExclude name is not correct.").isEqualTo("TrueExcludeElement"); assertThat(trueExclude.excludeUnlistedClasses()).as("trueExclude should be true.").isTrue(); - PersistenceUnitInfo falseExclude = info[3]; + SpringPersistenceUnitInfo falseExclude = info[3]; assertThat(falseExclude).as("falseExclude should not be null.").isNotNull(); assertThat(falseExclude.getPersistenceUnitName()).as("falseExclude name is not correct.").isEqualTo("FalseExcludeElement"); assertThat(falseExclude.excludeUnlistedClasses()).as("falseExclude should be false.").isFalse(); @@ -318,27 +317,27 @@ class PersistenceXmlParsingTests { PersistenceUnitReader reader = new PersistenceUnitReader( new PathMatchingResourcePatternResolver(), new JndiDataSourceLookup()); String resource = "/org/springframework/orm/jpa/persistence-exclude-2.0.xml"; - PersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); + SpringPersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource); assertThat(info).isNotNull(); assertThat(info.length).as("The number of persistence units is incorrect.").isEqualTo(4); - PersistenceUnitInfo noExclude = info[0]; + SpringPersistenceUnitInfo noExclude = info[0]; assertThat(noExclude).as("noExclude should not be null.").isNotNull(); assertThat(noExclude.getPersistenceUnitName()).as("noExclude name is not correct.").isEqualTo("NoExcludeElement"); assertThat(noExclude.excludeUnlistedClasses()).as("Exclude unlisted still defaults to false in 2.0.").isFalse(); - PersistenceUnitInfo emptyExclude = info[1]; + SpringPersistenceUnitInfo emptyExclude = info[1]; assertThat(emptyExclude).as("emptyExclude should not be null.").isNotNull(); assertThat(emptyExclude.getPersistenceUnitName()).as("emptyExclude name is not correct.").isEqualTo("EmptyExcludeElement"); assertThat(emptyExclude.excludeUnlistedClasses()).as("emptyExclude should be true.").isTrue(); - PersistenceUnitInfo trueExclude = info[2]; + SpringPersistenceUnitInfo trueExclude = info[2]; assertThat(trueExclude).as("trueExclude should not be null.").isNotNull(); assertThat(trueExclude.getPersistenceUnitName()).as("trueExclude name is not correct.").isEqualTo("TrueExcludeElement"); assertThat(trueExclude.excludeUnlistedClasses()).as("trueExclude should be true.").isTrue(); - PersistenceUnitInfo falseExclude = info[3]; + SpringPersistenceUnitInfo falseExclude = info[3]; assertThat(falseExclude).as("falseExclude should not be null.").isNotNull(); assertThat(falseExclude.getPersistenceUnitName()).as("falseExclude name is not correct.").isEqualTo("FalseExcludeElement"); assertThat(falseExclude.excludeUnlistedClasses()).as("falseExclude should be false.").isFalse();