Expose package-info classes through PersistenceUnitInfo#getAllClassNames()

Closes gh-36784
This commit is contained in:
Juergen Hoeller
2026-05-12 13:09:51 +02:00
parent ca8a1ea375
commit b7882d703c
6 changed files with 23 additions and 16 deletions
@@ -94,6 +94,12 @@ public abstract class ClassUtils {
/** The ".class" file suffix. */
public static final String CLASS_FILE_SUFFIX = ".class";
/**
* The ".package-info" suffix for package class names.
* @since 7.0.8
*/
public static final String PACKAGE_INFO_SUFFIX = ".package-info";
/** Precomputed value for the combination of private, static and final modifiers. */
private static final int NON_OVERRIDABLE_MODIFIER = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL;
@@ -78,7 +78,7 @@ final class ClassFileAnnotationMetadata implements AnnotationMetadata {
this.className = className;
this.accessFlags = accessFlags;
this.enclosingClassName = enclosingClassName;
this.superClassName = (!className.endsWith(".package-info")) ? superClassName : null;
this.superClassName = (!className.endsWith(ClassUtils.PACKAGE_INFO_SUFFIX) ? superClassName : null);
this.independentInnerClass = independentInnerClass;
this.interfaceNames = interfaceNames;
this.memberClassNames = memberClassNames;
@@ -76,9 +76,6 @@ public class LocalEntityManagerFactoryBean extends AbstractEntityManagerFactoryB
private static final String NON_JTA_DATASOURCE_PROPERTY = "jakarta.persistence.nonJtaDataSource";
private static final String PACKAGE_INFO_SUFFIX = ".package-info";
private @Nullable PersistenceConfiguration configuration;
private String @Nullable [] packagesToScan;
@@ -249,7 +246,7 @@ public class LocalEntityManagerFactoryBean extends AbstractEntityManagerFactoryB
// Expose managed packages as package-info class names if not included already
// (accepted by PersistenceConfiguration on Hibernate as well as EclipseLink)
for (String managedPackage : result.getManagedPackages()) {
classNameSet.add(managedPackage + PACKAGE_INFO_SUFFIX);
classNameSet.add(managedPackage + ClassUtils.PACKAGE_INFO_SUFFIX);
}
// Expose pre-resolved Class references to PersistenceConfiguration.
PersistenceConfiguration config = getPersistenceConfiguration();
@@ -103,8 +103,6 @@ public class LocalSessionFactoryBuilder extends Configuration {
private static final String RESOURCE_PATTERN = "/**/*.class";
private static final String PACKAGE_INFO_SUFFIX = ".package-info";
private static final TypeFilter[] DEFAULT_ENTITY_TYPE_FILTERS = new TypeFilter[] {
new AnnotationTypeFilter(Entity.class, false),
new AnnotationTypeFilter(Embeddable.class, false),
@@ -312,8 +310,9 @@ public class LocalSessionFactoryBuilder extends Configuration {
else if (CONVERTER_TYPE_FILTER.match(reader, readerFactory)) {
converterClassNames.add(className);
}
else if (className.endsWith(PACKAGE_INFO_SUFFIX)) {
packageNames.add(className.substring(0, className.length() - PACKAGE_INFO_SUFFIX.length()));
else if (className.endsWith(ClassUtils.PACKAGE_INFO_SUFFIX)) {
packageNames.add(className.substring(0,
className.length() - ClassUtils.PACKAGE_INFO_SUFFIX.length()));
}
}
catch (FileNotFoundException ex) {
@@ -59,8 +59,6 @@ public final class PersistenceManagedTypesScanner {
private static final String CLASS_RESOURCE_PATTERN = "/**/*.class";
private static final String PACKAGE_INFO_SUFFIX = ".package-info";
private static final String IGNORE_CLASSFORMAT_PROPERTY_NAME = "spring.classformat.ignore";
private static final boolean shouldIgnoreClassFormatException =
@@ -157,9 +155,9 @@ public final class PersistenceManagedTypesScanner {
}
}
}
if (className.endsWith(PACKAGE_INFO_SUFFIX)) {
if (className.endsWith(ClassUtils.PACKAGE_INFO_SUFFIX)) {
scanResult.managedPackages.add(className.substring(0,
className.length() - PACKAGE_INFO_SUFFIX.length()));
className.length() - ClassUtils.PACKAGE_INFO_SUFFIX.length()));
}
}
catch (FileNotFoundException ex) {
@@ -264,11 +264,18 @@ public class SpringPersistenceUnitInfo extends MutablePersistenceUnitInfo {
}
else if (method.getName().equals("getAllClassNames")) {
// JPA 4.0 letting the container perform the scanning
if (excludeUnlistedClasses()) {
return getManagedClassNames(); // typically coming from Spring default persistence unit
if (excludeUnlistedClasses()) { // typically coming from Spring default persistence unit
List<String> mergedClassesAndPackages =
new ArrayList<>(getManagedClassNames().size() + getManagedPackages().size());
mergedClassesAndPackages.addAll(getManagedClassNames());
for (String managedPackage : getManagedPackages()) {
mergedClassesAndPackages.add(managedPackage + ClassUtils.PACKAGE_INFO_SUFFIX);
}
return mergedClassesAndPackages;
}
throw new UnsupportedOperationException(
"JPA 4.0 getAllClassNames only supported with exclude-unlisted-classes");
"JPA 4.0 getAllClassNames only supported with Spring-configured packagesToScan or " +
"with completely listed managed classes plus exclude-unlisted-classes=true");
}
// Regular methods to be delegated to SpringPersistenceUnitInfo