Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: hibernate/hibernate-orm
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 6.6.11
Choose a base ref
...
head repository: hibernate/hibernate-orm
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 6.6.12
Choose a head ref
  • 14 commits
  • 28 files changed
  • 6 contributors

Commits on Mar 16, 2025

  1. Post-steps for release : 6.6.11.Final

    Hibernate-CI committed Mar 16, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    Lord-Kamina Gregorio Litenstein
    Copy the full SHA
    4438ada View commit details

Commits on Mar 27, 2025

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    e6816f5 View commit details
  2. Copy the full SHA
    73c490c View commit details
  3. Copy the full SHA
    6de6d36 View commit details
  4. HHH-19230 Avoid class loader leak in enhancement and improve bytebudd…

    …y type caching efficiency
    beikov committed Mar 27, 2025
    Copy the full SHA
    02ef8c1 View commit details
  5. Copy the full SHA
    776e4a5 View commit details
  6. HHH-18920 Proper class name is JpaSelection.getJavaTypeName(), not Jp…

    …aSelection.getJavaType().getName()
    cigaly authored and beikov committed Mar 27, 2025
    Copy the full SHA
    e357123 View commit details
  7. HHH-19109 fix use of @TransactionScoped by repository

    presence of JTA was not detected due to misspelling of package name
    gavinking authored and beikov committed Mar 27, 2025
    Copy the full SHA
    69f4951 View commit details
  8. HHH-18745 Add test for issue

    mbladel authored and beikov committed Mar 27, 2025
    Copy the full SHA
    a92e0db View commit details
  9. HHH-18745 Avoid resolving unnecessary table references for subtypes

    Also, handle type expressions in case statements like we do for
    comparison logic.
    mbladel authored and beikov committed Mar 27, 2025
    Copy the full SHA
    cb89514 View commit details
  10. HHH-19017: Address ClassCastException for PersistentAttributeIntercep…

    …table
    
    (cherry picked from commit 98ec951)
    jimsimon-wk authored and beikov committed Mar 27, 2025
    Copy the full SHA
    565693b View commit details

Commits on Mar 28, 2025

  1. HHH-19059 Add test for issue

    mbladel committed Mar 28, 2025
    Copy the full SHA
    2fd1191 View commit details
  2. HHH-19059 Fix check for property access fields on hierarchies

    mbladel committed Mar 28, 2025
    Copy the full SHA
    84e5d19 View commit details

Commits on Mar 30, 2025

  1. Copy the full SHA
    5f65de1 View commit details
Showing with 989 additions and 388 deletions.
  1. +17 −0 changelog.txt
  2. +1 −1 gradle/version.properties
  3. +66 −0 ...e-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerCacheProvider.java
  4. +40 −0 ...ore/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerClassFileLocator.java
  5. +49 −57 hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java
  6. +21 −32 hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/ModelTypePool.java
  7. +0 −82 ...e/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/OverridingClassFileLocator.java
  8. +0 −49 ...rnate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/SafeCacheProvider.java
  9. +4 −7 hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java
  10. +81 −12 hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java
  11. +90 −36 hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BytecodeProviderImpl.java
  12. +0 −3 hibernate-core/src/main/java/org/hibernate/jpa/internal/enhance/EnhancingClassTransformerImpl.java
  13. +17 −7 hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyHelper.java
  14. +38 −4 hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java
  15. +28 −20 .../src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java
  16. +60 −65 ...src/test/java/org/hibernate/orm/test/bytecode/enhancement/access/HierarchyPropertyAccessTest.java
  17. +37 −1 ...t/java/org/hibernate/orm/test/bytecode/enhancement/access/UnsupportedEnhancementStrategyTest.java
  18. +18 −1 hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/JoinedInheritanceTreatQueryTest.java
  19. +4 −4 ...org/hibernate/orm/test/inheritance/discriminator/JoinedInheritanceDiscriminatorSelectionTest.java
  20. +117 −0 ...rnate-core/src/test/java/org/hibernate/orm/test/lazyonetoone/LazyOneToOneWithEntityGraphTest.java
  21. +193 −0 hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyClassReuseTest.java
  22. +21 −0 ...el-generator/src/jakartaData/java/org/hibernate/processor/test/data/selectenumproperty/Topic.java
  23. +12 −0 ...enerator/src/jakartaData/java/org/hibernate/processor/test/data/selectenumproperty/TopicData.java
  24. +20 −0 ...or/src/jakartaData/java/org/hibernate/processor/test/data/selectenumproperty/TopicRepository.java
  25. +22 −0 ...erator/src/jakartaData/java/org/hibernate/processor/test/data/selectenumproperty/TopicStatus.java
  26. +27 −0 .../src/jakartaData/java/org/hibernate/processor/test/data/selectenumproperty/TopicTypeEnumTest.java
  27. +3 −3 tooling/metamodel-generator/src/main/java/org/hibernate/processor/HibernateProcessor.java
  28. +3 −4 ...ng/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java
17 changes: 17 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -3,6 +3,23 @@ Hibernate 6 Changelog

Note: Please refer to JIRA to learn more about each issue.

Changes in 6.6.12.Final (March 30, 2025)
------------------------------------------------------------------------------------------------------------------------

https://hibernate.atlassian.net/projects/HHH/versions/32945

** Bug
* [HHH-19109] - Hibernate Data Repositories are @RequestScoped
* [HHH-19059] - Bytecode enhancement fails when inherited fields are mapped using property access in subclass
* [HHH-19017] - Class Cast Exception for PersistentAttributeInterceptable
* [HHH-18920] - Enum parameters in Jakarta Data repository method return type constructor are not properly matched
* [HHH-18745] - Unnecessary joins when use TREAT operator
* [HHH-14694] - Use stable proxy names to avoid managing proxy state and memory leaks

** Task
* [HHH-19230] - Ensure that thread local for org.hibernate.bytecode.enhance.internal.bytebuddy.SafeCacheProvider + OverridingClassFileLocator are completely cleared


Changes in 6.6.11.Final (March 16, 2025)
------------------------------------------------------------------------------------------------------------------------

2 changes: 1 addition & 1 deletion gradle/version.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
hibernateVersion=6.6.11.Final
hibernateVersion=6.6.12.Final
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.bytecode.enhance.internal.bytebuddy;

import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.pool.TypePool;

/**
* A simple cache provider that allows overriding the resolution for the class that is currently being enhanced.
*/
final class EnhancerCacheProvider extends TypePool.CacheProvider.Simple {

private final ThreadLocal<EnhancementState> enhancementState = new ThreadLocal<>();

@Override
public TypePool.Resolution find(final String name) {
final EnhancementState enhancementState = getEnhancementState();
if ( enhancementState != null && enhancementState.getClassName().equals( name ) ) {
return enhancementState.getTypePoolResolution();
}
return super.find( name );
}

EnhancementState getEnhancementState() {
return enhancementState.get();
}

void setEnhancementState(EnhancementState state) {
enhancementState.set( state );
}

void removeEnhancementState() {
enhancementState.remove();
}

static final class EnhancementState {
private final String className;
private final ClassFileLocator.Resolution classFileResolution;
private TypePool.Resolution typePoolResolution;

public EnhancementState(String className, ClassFileLocator.Resolution classFileResolution) {
this.className = className;
this.classFileResolution = classFileResolution;
}

public String getClassName() {
return className;
}

public ClassFileLocator.Resolution getClassFileResolution() {
return classFileResolution;
}

public TypePool.Resolution getTypePoolResolution() {
return typePoolResolution;
}

public void setTypePoolResolution(TypePool.Resolution typePoolResolution) {
this.typePoolResolution = typePoolResolution;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.bytecode.enhance.internal.bytebuddy;

import net.bytebuddy.dynamic.ClassFileLocator;

import java.io.IOException;

/**
* A delegating ClassFileLocator that allows overriding the resolution for the class that is currently being enhanced.
*/
final class EnhancerClassFileLocator implements ClassFileLocator {

private final EnhancerCacheProvider cacheProvider;
private final ClassFileLocator delegate;

public EnhancerClassFileLocator(EnhancerCacheProvider cacheProvider, ClassFileLocator delegate) {
this.cacheProvider = cacheProvider;
this.delegate = delegate;
}

@Override
public Resolution locate(final String name) throws IOException {
final EnhancerCacheProvider.EnhancementState enhancementState = cacheProvider.getEnhancementState();
if ( enhancementState != null && enhancementState.getClassName().equals( name ) ) {
return enhancementState.getClassFileResolution();
}
return delegate.locate( name );
}

@Override
public void close() throws IOException {
delegate.close();
}

}
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.Transient;
import jakarta.persistence.metamodel.Type;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.annotation.AnnotationDescription;
@@ -74,6 +75,9 @@
import static net.bytebuddy.matcher.ElementMatchers.isDefaultFinalizer;
import static net.bytebuddy.matcher.ElementMatchers.isGetter;
import static net.bytebuddy.matcher.ElementMatchers.isSetter;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;

public class EnhancerImpl implements Enhancer {

@@ -476,21 +480,13 @@ private static boolean hasMappingAnnotation(AnnotationList annotations) {
|| annotations.isAnnotationPresent( Embeddable.class );
}

private static boolean hasPersistenceAnnotation(AnnotationList annotations) {
boolean found = false;
for ( AnnotationDescription annotation : annotations ) {
final String annotationName = annotation.getAnnotationType().getName();
if ( annotationName.startsWith( "jakarta.persistence" ) ) {
if ( annotationName.equals( "jakarta.persistence.Transient" ) ) {
// transient property so ignore it
return false;
}
else if ( !found && !IGNORED_PERSISTENCE_ANNOTATIONS.contains( annotationName ) ) {
found = true;
}
}
private static boolean isPersistentMethod(MethodDescription method) {
final AnnotationList annotations = method.getDeclaredAnnotations();
if ( annotations.isAnnotationPresent( Transient.class ) ) {
return false;
}
return found;

return annotations.stream().noneMatch( a -> IGNORED_PERSISTENCE_ANNOTATIONS.contains( a.getAnnotationType().getName() ) );
}

private static final Set<String> IGNORED_PERSISTENCE_ANNOTATIONS = Set.of(
@@ -503,6 +499,17 @@ else if ( !found && !IGNORED_PERSISTENCE_ANNOTATIONS.contains( annotationName )
"jakarta.persistence.PreUpdate"
);

private static boolean containsField(Generic type, String fieldName) {
do {
if ( !type.getDeclaredFields().filter( not( isStatic() ).and( named( fieldName ) ) ).isEmpty() ) {
return true;
}
type = type.getSuperClass();
}
while ( type != null && !type.represents( Object.class ) );
return false;
}

/**
* Check whether an entity class ({@code managedCtClass}) has mismatched names between a persistent field and its
* getter/setter when using {@link AccessType#PROPERTY}, which Hibernate does not currently support for enhancement.
@@ -545,61 +552,46 @@ private static boolean checkUnsupportedAttributeNaming(TypeDescription managedCt
.asMethodList()
.filter( isGetter().or( isSetter() ) );
for ( final MethodDescription methodDescription : methods ) {
if ( determineAccessType( methodDescription, defaultAccessType ) != AccessType.PROPERTY ) {
if ( methodDescription.getDeclaringType().represents( Object.class )
|| determineAccessType( methodDescription, defaultAccessType ) != AccessType.PROPERTY ) {
// We only need to check this for AccessType.PROPERTY
continue;
}

final String methodName = methodDescription.getActualName();
String methodFieldName;
String fieldName;
if ( methodName.startsWith( "get" ) || methodName.startsWith( "set" ) ) {
methodFieldName = methodName.substring( 3 );
fieldName = methodName.substring( 3 );
}
else {
assert methodName.startsWith( "is" );
methodFieldName = methodName.substring( 2 );
fieldName = methodName.substring( 2 );
}
// convert first field letter to lower case
methodFieldName = getJavaBeansFieldName( methodFieldName );
if ( methodFieldName != null && hasPersistenceAnnotation( methodDescription.getDeclaredAnnotations() ) ) {
boolean propertyNameMatchesFieldName = false;
for ( final FieldDescription field : methodDescription.getDeclaringType().getDeclaredFields() ) {
if ( !Modifier.isStatic( field.getModifiers() ) ) {
final AnnotatedFieldDescription annotatedField = new AnnotatedFieldDescription(
enhancementContext,
field
fieldName = getJavaBeansFieldName( fieldName );
if ( fieldName != null && isPersistentMethod( methodDescription )
&& !containsField( managedCtClass.asGenericType(), fieldName ) ) {
// We shouldn't even be in this method if using LEGACY, see top of this method.
switch ( strategy ) {
case SKIP:
log.debugf(
"Skipping enhancement of [%s] because no field named [%s] could be found for property accessor method [%s]."
+ " To fix this, make sure all property accessor methods have a matching field.",
managedCtClass.getName(),
fieldName,
methodDescription.getName()
);
if ( enhancementContext.isPersistentField( annotatedField ) ) {
if ( methodFieldName.equals( field.getActualName() ) ) {
propertyNameMatchesFieldName = true;
break;
}
}
}
}
if ( !propertyNameMatchesFieldName ) {
// We shouldn't even be in this method if using LEGACY, see top of this method.
switch ( strategy ) {
case SKIP:
log.debugf(
"Skipping enhancement of [%s] because no field named [%s] could be found for property accessor method [%s]."
+ " To fix this, make sure all property accessor methods have a matching field.",
managedCtClass.getName(),
methodFieldName,
methodDescription.getName()
);
return true;
case FAIL:
throw new EnhancementException( String.format(
"Enhancement of [%s] failed because no field named [%s] could be found for property accessor method [%s]."
+ " To fix this, make sure all property accessor methods have a matching field.",
managedCtClass.getName(),
methodFieldName,
methodDescription.getName()
) );
default:
throw new AssertionFailure( "Unexpected strategy at this point: " + strategy );
}
return true;
case FAIL:
throw new EnhancementException( String.format(
"Enhancement of [%s] failed because no field named [%s] could be found for property accessor method [%s]."
+ " To fix this, make sure all property accessor methods have a matching field.",
managedCtClass.getName(),
fieldName,
methodDescription.getName()
) );
default:
throw new AssertionFailure( "Unexpected strategy at this point: " + strategy );
}
}
}
Original file line number Diff line number Diff line change
@@ -6,23 +6,21 @@
*/
package org.hibernate.bytecode.enhance.internal.bytebuddy;

import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.pool.TypePool;

import java.util.Objects;

/**
* A TypePool suitable for loading user's classes,
* potentially in parallel operations.
*/
public class ModelTypePool extends TypePool.Default implements EnhancerClassLocator {

private final ConcurrentHashMap<String, Resolution> resolutions = new ConcurrentHashMap<>();
private final OverridingClassFileLocator locator;
private final SafeCacheProvider poolCache;
private final EnhancerClassFileLocator locator;
private final EnhancerCacheProvider poolCache;

private ModelTypePool(SafeCacheProvider cacheProvider, OverridingClassFileLocator classFileLocator, CoreTypePool parent) {
private ModelTypePool(EnhancerCacheProvider cacheProvider, EnhancerClassFileLocator classFileLocator, CoreTypePool parent) {
super( cacheProvider, classFileLocator, ReaderMode.FAST, parent );
this.poolCache = cacheProvider;
this.locator = classFileLocator;
@@ -64,7 +62,7 @@ public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFile
* @return
*/
public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFileLocator, CoreTypePool coreTypePool) {
return buildModelTypePool( classFileLocator, coreTypePool, new SafeCacheProvider() );
return buildModelTypePool( classFileLocator, coreTypePool, new EnhancerCacheProvider() );
}

/**
@@ -74,44 +72,35 @@ public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFile
* @param cacheProvider
* @return
*/
public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFileLocator, CoreTypePool coreTypePool, SafeCacheProvider cacheProvider) {
static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFileLocator, CoreTypePool coreTypePool, EnhancerCacheProvider cacheProvider) {
Objects.requireNonNull( classFileLocator );
Objects.requireNonNull( coreTypePool );
Objects.requireNonNull( cacheProvider );
return new ModelTypePool( cacheProvider, new OverridingClassFileLocator( classFileLocator ), coreTypePool );
}

@Override
protected Resolution doDescribe(final String name) {
final Resolution resolution = resolutions.get( name );
if ( resolution != null ) {
return resolution;
}
else {
return resolutions.computeIfAbsent( name, super::doDescribe );
}
return new ModelTypePool( cacheProvider, new EnhancerClassFileLocator( cacheProvider, classFileLocator ), coreTypePool );
}

@Override
public void registerClassNameAndBytes(final String className, final byte[] bytes) {
//Very important: ensure the registered override is actually effective in case this class
//was already resolved in the recent past; this could have happened for example as a side effect
//of symbol resolution during enhancement of a different class, or very simply when attempting
//to re-enhanced the same class - which happens frequently in WildFly because of the class transformers
//being triggered concurrently by multiple parallel deployments.
resolutions.remove( className );
poolCache.remove( className );
locator.put( className, new ClassFileLocator.Resolution.Explicit( Objects.requireNonNull( bytes ) ) );
final EnhancerCacheProvider.EnhancementState currentEnhancementState = poolCache.getEnhancementState();
if ( currentEnhancementState != null ) {
throw new IllegalStateException( "Re-entrant enhancement is not supported: " + className );
}
final EnhancerCacheProvider.EnhancementState state = new EnhancerCacheProvider.EnhancementState(
className,
new ClassFileLocator.Resolution.Explicit( Objects.requireNonNull( bytes ) )
);
// Set the state first because the ClassFileLocator needs this in the doDescribe() call below
poolCache.setEnhancementState( state );
state.setTypePoolResolution( doDescribe( className ) );
}

@Override
public void deregisterClassNameAndBytes(final String className) {
locator.remove( className );
public void deregisterClassNameAndBytes(String className) {
poolCache.removeEnhancementState();
}

@Override
public ClassFileLocator asClassFileLocator() {
return locator;
}

}
Loading