Browse Source

Runtime compatibility with JPA 4.0.0-M1 and Hibernate ORM 8.0.0.Alpha1

Closes gh-35705
pull/36250/head
Juergen Hoeller 2 months ago
parent
commit
ac243fae65
  1. 12
      spring-orm/src/main/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.java
  2. 17
      spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java
  3. 11
      spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/MutablePersistenceUnitInfo.java
  4. 9
      spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/PersistenceUnitReader.java
  5. 24
      spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/SpringPersistenceUnitInfo.java
  6. 53
      spring-orm/src/main/java/org/springframework/orm/jpa/vendor/SpringHibernateJpaPersistenceProvider.java
  7. 11
      spring-orm/src/test/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBeanTests.java
  8. 13
      spring-orm/src/test/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBeanTests.java

12
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.FetchType;
import jakarta.persistence.PersistenceConfiguration;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.SharedCacheMode;
@ -287,6 +288,17 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage @@ -287,6 +288,17 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
this.internalPersistenceUnitManager.setValidationMode(validationMode);
}
/**
* Specify the JPA 4.0 default fetch type for all of this manager's persistence
* units, overriding any value in {@code persistence.xml} if set.
* <p><b>NOTE: Only applied if no external PersistenceUnitManager specified. Also,
* this requires a JPA 4.0+ persistence provider and will be ignored otherwise.</b>
* @since 7.0.4
*/
public void setDefaultToOneFetchType(FetchType defaultToOneFetchType) {
this.internalPersistenceUnitManager.setDefaultToOneFetchType(defaultToOneFetchType);
}
/**
* Specify the JDBC DataSource that the JPA persistence provider is supposed
* to use for accessing the database. This is an alternative to keeping the

17
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.FetchType;
import jakarta.persistence.PersistenceConfiguration;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.SharedCacheMode;
@ -123,6 +124,8 @@ public class DefaultPersistenceUnitManager @@ -123,6 +124,8 @@ public class DefaultPersistenceUnitManager
private @Nullable ValidationMode validationMode;
private @Nullable FetchType defaultToOneFetchType;
private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
private @Nullable DataSource defaultDataSource;
@ -291,6 +294,17 @@ public class DefaultPersistenceUnitManager @@ -291,6 +294,17 @@ public class DefaultPersistenceUnitManager
this.validationMode = validationMode;
}
/**
* Specify the JPA 4.0 default fetch type for all of this manager's persistence
* units, overriding any value in {@code persistence.xml} if set.
* <p>This setting only applies against a JPA 4.0+ persistence provider.
* Otherwise, it will be ignored.
* @since 7.0.4
*/
public void setDefaultToOneFetchType(FetchType defaultToOneFetchType) {
this.defaultToOneFetchType = defaultToOneFetchType;
}
/**
* Specify the JDBC DataSources that the JPA persistence provider is supposed
* to use for accessing the database, resolving data source names in
@ -475,6 +489,9 @@ public class DefaultPersistenceUnitManager @@ -475,6 +489,9 @@ public class DefaultPersistenceUnitManager
if (this.validationMode != null) {
pui.setValidationMode(this.validationMode);
}
if (this.defaultToOneFetchType != null) {
pui.setDefaultToOneFetchType(this.defaultToOneFetchType);
}
// Initialize persistence unit ClassLoader
if (this.loadTimeWeaver != null) {

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

@ -23,6 +23,7 @@ import java.util.Properties; @@ -23,6 +23,7 @@ import java.util.Properties;
import javax.sql.DataSource;
import jakarta.persistence.FetchType;
import jakarta.persistence.PersistenceUnitTransactionType;
import jakarta.persistence.SharedCacheMode;
import jakarta.persistence.ValidationMode;
@ -82,6 +83,8 @@ public class MutablePersistenceUnitInfo { @@ -82,6 +83,8 @@ public class MutablePersistenceUnitInfo {
private ValidationMode validationMode = ValidationMode.AUTO;
private FetchType defaultToOneFetchType = FetchType.EAGER;
private Properties properties = new Properties();
private String persistenceXMLSchemaVersion = "3.2";
@ -213,6 +216,14 @@ public class MutablePersistenceUnitInfo { @@ -213,6 +216,14 @@ public class MutablePersistenceUnitInfo {
return this.validationMode;
}
public void setDefaultToOneFetchType(FetchType defaultToOneFetchType) {
this.defaultToOneFetchType = defaultToOneFetchType;
}
public FetchType getDefaultToOneFetchType() {
return this.defaultToOneFetchType;
}
public void addProperty(String name, String value) {
this.properties.setProperty(name, value);
}

9
spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/PersistenceUnitReader.java

@ -26,6 +26,7 @@ import javax.xml.parsers.DocumentBuilder; @@ -26,6 +26,7 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import jakarta.persistence.FetchType;
import jakarta.persistence.PersistenceUnitTransactionType;
import jakarta.persistence.SharedCacheMode;
import jakarta.persistence.ValidationMode;
@ -85,6 +86,8 @@ final class PersistenceUnitReader { @@ -85,6 +86,8 @@ final class PersistenceUnitReader {
private static final String VALIDATION_MODE = "validation-mode";
private static final String DEFAULT_TO_ONE_FETCH_TYPE = "default-to-one-fetch-type";
private static final String PROPERTIES = "properties";
private static final String META_INF = "META-INF";
@ -251,6 +254,12 @@ final class PersistenceUnitReader { @@ -251,6 +254,12 @@ final class PersistenceUnitReader {
unitInfo.setValidationMode(ValidationMode.valueOf(validationMode));
}
// set JPA 4.0 default fetch type
String fetchType = DomUtils.getChildElementValueByTagName(persistenceUnit, DEFAULT_TO_ONE_FETCH_TYPE);
if (StringUtils.hasText(fetchType)) {
unitInfo.setDefaultToOneFetchType(FetchType.valueOf(fetchType));
}
parseQualifiers(persistenceUnit, unitInfo);
parseMappingFiles(persistenceUnit, unitInfo);
parseJarFiles(persistenceUnit, unitInfo);

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

@ -23,6 +23,7 @@ import java.net.URL; @@ -23,6 +23,7 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import jakarta.persistence.FetchType;
import jakarta.persistence.PersistenceConfiguration;
import jakarta.persistence.PersistenceUnitTransactionType;
import jakarta.persistence.spi.ClassTransformer;
@ -207,6 +208,16 @@ public class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo { @@ -207,6 +208,16 @@ public class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo {
setSharedCacheMode(config.sharedCacheMode());
setValidationMode(config.validationMode());
// JPA 4.0 defaultToOneFetchType
Method defaultToOneFetchType = ClassUtils.getMethodIfAvailable(config.getClass(), "defaultToOneFetchType");
if (defaultToOneFetchType != null) {
FetchType fetchType = (FetchType) ReflectionUtils.invokeMethod(defaultToOneFetchType, config);
if (fetchType != null) {
setDefaultToOneFetchType(fetchType);
}
}
getProperties().putAll(config.properties());
// Further relevant settings from HibernatePersistenceConfiguration on Hibernate 7.1+
@ -247,10 +258,21 @@ public class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo { @@ -247,10 +258,21 @@ public class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo {
@SuppressWarnings({"rawtypes", "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")) {
// Fast path for SmartPersistenceUnitInfo JTA check
return (getTransactionType() == PersistenceUnitTransactionType.JTA);
}
else if (method.getName().equals("getAllManagedClassNames")) {
// JPA 4.0 letting the container perform the scanning ->
// with Spring, only makes sense with typical default persistence unit.
if (excludeUnlistedClasses() && getMappingFileNames().isEmpty()) {
List<String> mergedClassesAndPackages = new ArrayList<>(getManagedClassNames());
mergedClassesAndPackages.addAll(getManagedPackages());
return mergedClassesAndPackages;
}
throw new UnsupportedOperationException(
"JPA 4.0 getAllManagedClassNames only supported with exclude-unlisted-classes and no orm.xml");
}
// Regular methods to be delegated to SpringPersistenceUnitInfo
Method targetMethod = SpringPersistenceUnitInfo.class.getMethod(method.getName(), method.getParameterTypes());

53
spring-orm/src/main/java/org/springframework/orm/jpa/vendor/SpringHibernateJpaPersistenceProvider.java vendored

@ -30,6 +30,7 @@ import org.hibernate.jpa.boot.internal.PersistenceUnitInfoDescriptor; @@ -30,6 +30,7 @@ import org.hibernate.jpa.boot.internal.PersistenceUnitInfoDescriptor;
import org.springframework.core.NativeDetector;
import org.springframework.orm.jpa.persistenceunit.SmartPersistenceUnitInfo;
import org.springframework.util.ClassUtils;
/**
* Spring-specific subclass of the standard {@link HibernatePersistenceProvider}
@ -51,19 +52,45 @@ class SpringHibernateJpaPersistenceProvider extends HibernatePersistenceProvider @@ -51,19 +52,45 @@ class SpringHibernateJpaPersistenceProvider extends HibernatePersistenceProvider
if (info instanceof SmartPersistenceUnitInfo smartInfo) {
mergedClassesAndPackages.addAll(smartInfo.getManagedPackages());
}
return new EntityManagerFactoryBuilderImpl(
new PersistenceUnitInfoDescriptor(info) {
@Override
public List<String> getManagedClassNames() {
return mergedClassesAndPackages;
}
@Override
public void pushClassTransformer(EnhancementContext enhancementContext) {
if (!NativeDetector.inNativeImage()) {
super.pushClassTransformer(enhancementContext);
}
}
}, properties).build();
PersistenceUnitInfoDescriptor descriptor;
if (!NativeDetector.inNativeImage()) {
// No ClassTransformer adaptation necessary
descriptor = new PersistenceUnitInfoDescriptor(info) {
@Override
public List<String> getManagedClassNames() {
return mergedClassesAndPackages;
}
};
}
else if (ClassUtils.hasMethod(PersistenceUnitInfoDescriptor.class, "isClassTransformerRegistrationDisabled")) {
// Hibernate 8.0: no pushClassTransformer override necessary
descriptor = new PersistenceUnitInfoDescriptor(info) {
@Override
public List<String> getManagedClassNames() {
return mergedClassesAndPackages;
}
// @Override on Hibernate 8.0
public boolean isClassTransformerRegistrationDisabled() {
return true;
}
};
}
else {
// Hibernate 7.x: pushClassTransformer no-op in native image
descriptor = new PersistenceUnitInfoDescriptor(info) {
@Override
public List<String> getManagedClassNames() {
return mergedClassesAndPackages;
}
@Override
public void pushClassTransformer(EnhancementContext enhancementContext) {
// no-op
}
};
}
return new EntityManagerFactoryBuilderImpl(descriptor, properties).build();
}
}

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

@ -27,6 +27,7 @@ import jakarta.persistence.OptimisticLockException; @@ -27,6 +27,7 @@ import jakarta.persistence.OptimisticLockException;
import jakarta.persistence.PersistenceConfiguration;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.PersistenceUnitTransactionType;
import jakarta.persistence.spi.ClassTransformer;
import jakarta.persistence.spi.PersistenceProvider;
import jakarta.persistence.spi.PersistenceUnitInfo;
import jakarta.persistence.spi.ProviderUtil;
@ -365,6 +366,16 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF @@ -365,6 +366,16 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF
public boolean generateSchema(String persistenceUnitName, Map map) {
throw new UnsupportedOperationException();
}
// JPA 4.0 method
public boolean generateSchema(PersistenceConfiguration persistenceConfiguration) {
throw new UnsupportedOperationException();
}
// JPA 4.0 method
public ClassTransformer getClassTransformer(PersistenceUnitInfo persistenceUnitInfo, Map<?,?> map) {
return null;
}
}

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

@ -21,6 +21,7 @@ import java.util.Properties; @@ -21,6 +21,7 @@ import java.util.Properties;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.PersistenceConfiguration;
import jakarta.persistence.spi.ClassTransformer;
import jakarta.persistence.spi.PersistenceProvider;
import jakarta.persistence.spi.PersistenceUnitInfo;
import jakarta.persistence.spi.ProviderUtil;
@ -184,15 +185,27 @@ class LocalEntityManagerFactoryBeanTests extends AbstractEntityManagerFactoryBea @@ -184,15 +185,27 @@ 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();
}
// JPA 4.0 method
public boolean generateSchema(PersistenceConfiguration persistenceConfiguration) {
throw new UnsupportedOperationException();
}
// JPA 4.0 method
public ClassTransformer getClassTransformer(PersistenceUnitInfo persistenceUnitInfo, Map<?,?> map) {
return null;
}
}
}

Loading…
Cancel
Save