Make SpringPersistenceUnitInfo public for custom bootstrapping purposes

See gh-35622
This commit is contained in:
Juergen Hoeller
2025-10-13 23:24:46 +02:00
parent 2f8d52fd47
commit e901516732
3 changed files with 60 additions and 18 deletions
@@ -675,7 +675,7 @@ public class DefaultPersistenceUnitManager
}
SpringPersistenceUnitInfo pui = this.persistenceUnitInfos.values().iterator().next();
this.persistenceUnitInfos.clear();
return pui.toSmartPersistenceUnitInfo();
return pui.asStandardPersistenceUnitInfo();
}
@Override
@@ -691,7 +691,7 @@ public class DefaultPersistenceUnitManager
"Persistence unit with name '" + persistenceUnitName + "' already obtained");
}
}
return pui.toSmartPersistenceUnitInfo();
return pui.asStandardPersistenceUnitInfo();
}
}
@@ -34,6 +34,7 @@ import org.springframework.util.Assert;
* Spring's mutable equivalent of the JPA
* {@link jakarta.persistence.spi.PersistenceUnitInfo} interface,
* used to bootstrap an {@code EntityManagerFactory} in a container.
* This is the type exposed to {@link PersistenceUnitPostProcessor}.
*
* <p>This implementation is largely a JavaBean, offering mutators
* for all standard {@code PersistenceUnitInfo} properties.
@@ -42,10 +43,16 @@ import org.springframework.util.Assert;
* (for achieving compatibility between JPA 3.2 and 4.0 and for preventing
* late mutation attempts through {@code PersistenceUnitInfo} downcasts).
*
* <p>For custom bootstrapping purposes, use {@link SpringPersistenceUnitInfo}
* instead, turning it into a {@code jakarta.persistence.spi.PersistenceUnitInfo}
* through {@link SpringPersistenceUnitInfo#asStandardPersistenceUnitInfo()}.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Costin Leau
* @since 2.0
* @see PersistenceUnitPostProcessor#postProcessPersistenceUnitInfo
* @see SpringPersistenceUnitInfo#asStandardPersistenceUnitInfo()
*/
public class MutablePersistenceUnitInfo {
@@ -22,6 +22,7 @@ import java.lang.reflect.Proxy;
import jakarta.persistence.PersistenceUnitTransactionType;
import jakarta.persistence.spi.ClassTransformer;
import jakarta.persistence.spi.PersistenceUnitInfo;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
@@ -35,26 +36,56 @@ import org.springframework.util.ReflectionUtils;
* Subclass of {@link MutablePersistenceUnitInfo} that adds instrumentation hooks based on
* Spring's {@link org.springframework.instrument.classloading.LoadTimeWeaver} abstraction.
*
* <p>This class is restricted to package visibility, in contrast to its superclass.
* <p>As of 7.0, this class is public for custom bootstrapping purposes. A fully configured
* {@code SpringPersistenceUnitInfo} instance can be turned into a standard JPA descriptor
* through {@link #asStandardPersistenceUnitInfo} (returning a JPA 3.2/4.0 adapted proxy).
*
* <p>Note: For post-processing within a {@code LocalContainerEntityManagerFactoryBean}
* bootstrap, the base type {@code MutablePersistenceUnitInfo} is entirely sufficient.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Costin Leau
* @since 2.0
* @see PersistenceUnitManager
* @since 7.0
* @see DefaultPersistenceUnitManager
*/
class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo {
public class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo {
private @Nullable LoadTimeWeaver loadTimeWeaver;
private @Nullable ClassLoader classLoader;
/**
* Construct a new SpringPersistenceUnitInfo for custom purposes.
* @param loadTimeWeaver the LoadTimeWeaver to use
*/
public SpringPersistenceUnitInfo(LoadTimeWeaver loadTimeWeaver) {
init(loadTimeWeaver);
}
/**
* Construct a new SpringPersistenceUnitInfo for custom purposes.
* @param classLoader the ClassLoader to use
*/
public SpringPersistenceUnitInfo(ClassLoader classLoader) {
init(classLoader);
}
/**
* Construct a SpringPersistenceUnitInfo for internal purposes.
* @see #init(LoadTimeWeaver)
* @see #init(ClassLoader)
*/
SpringPersistenceUnitInfo() {
}
/**
* Initialize this PersistenceUnitInfo with the LoadTimeWeaver SPI interface
* used by Spring to add instrumentation to the current class loader.
*/
public void init(LoadTimeWeaver loadTimeWeaver) {
void init(LoadTimeWeaver loadTimeWeaver) {
Assert.notNull(loadTimeWeaver, "LoadTimeWeaver must not be null");
this.loadTimeWeaver = loadTimeWeaver;
this.classLoader = loadTimeWeaver.getInstrumentableClassLoader();
@@ -64,11 +95,10 @@ class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo {
* Initialize this PersistenceUnitInfo with the current class loader
* (instead of with a LoadTimeWeaver).
*/
public void init(@Nullable ClassLoader classLoader) {
void init(@Nullable ClassLoader classLoader) {
this.classLoader = classLoader;
}
/**
* This implementation returns the LoadTimeWeaver's instrumentable ClassLoader,
* if specified.
@@ -102,20 +132,26 @@ class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo {
return tcl;
}
/**
* Expose a {@link SmartPersistenceUnitInfo} proxy for the persistence unit
* configuration in this {@link MutablePersistenceUnitInfo} instance.
* @since 7.0
* Expose a standard {@code jakarta.persistence.spi.PersistenceUnitInfo} proxy for the
* persistence unit configuration in this {@code SpringPersistenceUnitInfo} instance.
* <p>The returned proxy implements {@code jakarta.persistence.spi.PersistenceUnitInfo}
* (and its extended variant {@link SmartPersistenceUnitInfo}) for use with persistence
* provider bootstrapping. Note that the returned proxy is effectively unmodifiable and
* cannot be downcast to {@code Mutable/SpringPersistenceUnitInfo}.
*/
public SmartPersistenceUnitInfo toSmartPersistenceUnitInfo() {
return (SmartPersistenceUnitInfo) Proxy.newProxyInstance(getClass().getClassLoader(),
public PersistenceUnitInfo asStandardPersistenceUnitInfo() {
return (PersistenceUnitInfo) Proxy.newProxyInstance(getClass().getClassLoader(),
new Class<?>[] {SmartPersistenceUnitInfo.class},
new JpaPersistenceUnitInfoInvocationHandler());
new SmartPersistenceUnitInfoInvocationHandler());
}
private class JpaPersistenceUnitInfoInvocationHandler implements InvocationHandler {
/**
* Invocation handler for a JPA-compliant {@link SmartPersistenceUnitInfo} proxy,
* delegating to the corresponding methods on {@link SpringPersistenceUnitInfo}.
*/
private class SmartPersistenceUnitInfoInvocationHandler implements InvocationHandler {
@SuppressWarnings("unchecked")
@Override
@@ -127,7 +163,6 @@ class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo {
// 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