From 90227d94bdda269bda5d91409045485f125ab778 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Mon, 8 May 2023 13:05:57 -0500 Subject: [PATCH] HHH-16542 - Bad get/is handling with bytecode enhancement --- .../internal/util/ReflectHelper.java | 27 ++- .../internal/AbstractFieldSerialForm.java | 6 +- .../AbstractSetterMethodSerialForm.java | 69 ++++++ .../access/internal/AccessStrategyHelper.java | 208 ++++++++++++++++++ .../internal/ChainedPropertyAccessImpl.java | 1 + .../PropertyAccessCompositeUserTypeImpl.java | 1 + .../internal/PropertyAccessEmbeddedImpl.java | 1 + .../internal/PropertyAccessEnhancedImpl.java | 83 ++++++- .../internal/PropertyAccessMapImpl.java | 1 + .../internal/PropertyAccessMixedImpl.java | 60 +---- .../PropertyAccessStrategyBackRefImpl.java | 1 + .../PropertyAccessStrategyEnhancedImpl.java | 24 +- ...ropertyAccessStrategyIndexBackRefImpl.java | 1 + .../PropertyAccessStrategyNoopImpl.java | 1 + ...rtyAccessStrategyResolverStandardImpl.java | 8 +- .../access/spi/EnhancedSetterImpl.java | 42 +--- .../access/spi/EnhancedSetterMethodImpl.java | 61 +++++ .../property/access/spi/GetterFieldImpl.java | 3 + .../property/access/spi/GetterMethodImpl.java | 3 + .../property/access/spi/SetterFieldImpl.java | 7 +- .../property/access/spi/SetterMethodImpl.java | 39 +--- .../FieldMappingWithGetterAndIsTest.java | 63 ++++++ .../FieldMappingWithGetterAndIsTest2.java | 68 ++++++ 23 files changed, 642 insertions(+), 136 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/property/access/internal/AbstractSetterMethodSerialForm.java create mode 100644 hibernate-core/src/main/java/org/hibernate/property/access/internal/AccessStrategyHelper.java create mode 100644 hibernate-core/src/main/java/org/hibernate/property/access/spi/EnhancedSetterMethodImpl.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/property/FieldMappingWithGetterAndIsTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/property/FieldMappingWithGetterAndIsTest2.java diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/ReflectHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/ReflectHelper.java index f9453a7a3f..2c1f65648c 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/ReflectHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/ReflectHelper.java @@ -17,7 +17,6 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.Locale; -import jakarta.persistence.Transient; import org.hibernate.AssertionFailure; import org.hibernate.MappingException; @@ -31,6 +30,8 @@ import org.hibernate.type.BasicType; import org.hibernate.type.Type; import org.hibernate.type.descriptor.java.spi.PrimitiveJavaType; +import jakarta.persistence.Transient; + /** * Utility class for various reflection operations. * @@ -511,7 +512,17 @@ public final class ReflectHelper { return getter; } - private static Method getGetterOrNull(Class containerClass, String propertyName) { + /** + * Find the method that can be used as the setter for this property. + * + * @param containerClass The Class which contains the property + * @param propertyName The name of the property + * + * @return The getter method, or {@code null} if there is none. + * + * @throws MappingException If the {@code containerClass} has both a get- and an is- form. + */ + public static Method getGetterOrNull(Class containerClass, String propertyName) { if ( isRecord( containerClass ) ) { try { return containerClass.getMethod( propertyName, NO_PARAM_SIGNATURE ); @@ -520,6 +531,7 @@ public final class ReflectHelper { // Ignore } } + for ( Method method : containerClass.getDeclaredMethods() ) { // if the method has parameters, skip it if ( method.getParameterCount() != 0 ) { @@ -557,6 +569,8 @@ public final class ReflectHelper { final String stemName = methodName.substring( 2 ); String decapitalizedStemName = Introspector.decapitalize( stemName ); if ( stemName.equals( propertyName ) || decapitalizedStemName.equals( propertyName ) ) { + // not sure that this can ever really happen given the handling of "get" above. + // but be safe verifyNoGetVariantExists( containerClass, propertyName, method, stemName ); return method; } @@ -566,8 +580,8 @@ public final class ReflectHelper { return null; } - private static void verifyNoIsVariantExists( - Class containerClass, + public static void verifyNoIsVariantExists( + Class containerClass, String propertyName, Method getMethod, String stemName) { @@ -584,7 +598,8 @@ public final class ReflectHelper { } } - private static void checkGetAndIsVariants( + + public static void checkGetAndIsVariants( Class containerClass, String propertyName, Method getMethod, @@ -606,7 +621,7 @@ public final class ReflectHelper { } } - private static void verifyNoGetVariantExists( + public static void verifyNoGetVariantExists( Class containerClass, String propertyName, Method isMethod, diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/AbstractFieldSerialForm.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/AbstractFieldSerialForm.java index ea9bfd5566..5baab10521 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/internal/AbstractFieldSerialForm.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/AbstractFieldSerialForm.java @@ -13,19 +13,19 @@ import org.hibernate.internal.util.ReflectHelper; import org.hibernate.property.access.spi.PropertyAccessSerializationException; /** - * Abstract serialization replacement for field based Getter and Setter impls. + * Base Serializable form for field (used as Getter or Setter) * * @author Steve Ebersole */ public abstract class AbstractFieldSerialForm implements Serializable { - private final Class declaringClass; + private final Class declaringClass; private final String fieldName; protected AbstractFieldSerialForm(Field field) { this( field.getDeclaringClass(), field.getName() ); } - protected AbstractFieldSerialForm(Class declaringClass, String fieldName) { + protected AbstractFieldSerialForm(Class declaringClass, String fieldName) { this.declaringClass = declaringClass; this.fieldName = fieldName; } diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/AbstractSetterMethodSerialForm.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/AbstractSetterMethodSerialForm.java new file mode 100644 index 0000000000..6132a9d16c --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/AbstractSetterMethodSerialForm.java @@ -0,0 +1,69 @@ +/* + * 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.property.access.internal; + +import java.io.Serializable; +import java.lang.reflect.Method; + +import org.hibernate.internal.util.ReflectHelper; +import org.hibernate.property.access.spi.PropertyAccessSerializationException; + +/** + * Base Serializable form for setter methods + * + * @author Steve Ebersole + */ +public abstract class AbstractSetterMethodSerialForm implements Serializable { + private final Class containerClass; + private final String propertyName; + + private final Class declaringClass; + private final String methodName; + private final Class argumentType; + + public AbstractSetterMethodSerialForm(Class containerClass, String propertyName, Method method) { + this.containerClass = containerClass; + this.propertyName = propertyName; + this.declaringClass = method.getDeclaringClass(); + this.methodName = method.getName(); + this.argumentType = method.getParameterTypes()[0]; + } + + public Class getContainerClass() { + return containerClass; + } + + public String getPropertyName() { + return propertyName; + } + + public Class getDeclaringClass() { + return declaringClass; + } + + public String getMethodName() { + return methodName; + } + + public Class getArgumentType() { + return argumentType; + } + + protected Method resolveMethod() { + try { + final Method method = declaringClass.getDeclaredMethod( methodName, argumentType ); + ReflectHelper.ensureAccessibility( method ); + return method; + } + catch (NoSuchMethodException e) { + throw new PropertyAccessSerializationException( + "Unable to resolve setter method on deserialization : " + declaringClass.getName() + "#" + + methodName + "(" + argumentType.getName() + ")" + ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/AccessStrategyHelper.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/AccessStrategyHelper.java new file mode 100644 index 0000000000..d337e25f77 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/AccessStrategyHelper.java @@ -0,0 +1,208 @@ +/* + * 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.property.access.internal; + +import java.beans.Introspector; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Locale; + +import org.hibernate.MappingException; +import org.hibernate.PropertyNotFoundException; +import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor; +import org.hibernate.engine.spi.CompositeOwner; +import org.hibernate.engine.spi.CompositeTracker; +import org.hibernate.engine.spi.PersistentAttributeInterceptor; + +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; +import jakarta.persistence.Transient; + +import static org.hibernate.engine.internal.ManagedTypeHelper.asCompositeOwner; +import static org.hibernate.engine.internal.ManagedTypeHelper.asCompositeTracker; +import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; +import static org.hibernate.engine.internal.ManagedTypeHelper.isCompositeTracker; +import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptableType; +import static org.hibernate.internal.util.ReflectHelper.NO_PARAM_SIGNATURE; +import static org.hibernate.internal.util.ReflectHelper.findField; +import static org.hibernate.internal.util.ReflectHelper.isRecord; + +/** + * @author Steve Ebersole + */ +public class AccessStrategyHelper { + public static final int COMPOSITE_TRACKER_MASK = 1; + public static final int COMPOSITE_OWNER = 2; + public static final int PERSISTENT_ATTRIBUTE_INTERCEPTABLE_MASK = 4; + + public static Field fieldOrNull(Class containerJavaType, String propertyName) { + try { + return findField( containerJavaType, propertyName ); + } + catch (PropertyNotFoundException e) { + return null; + } + } + + public static AccessType getAccessType(Class containerJavaType, String propertyName) { + final Field field = fieldOrNull( containerJavaType, propertyName ); + final AccessType explicitAccessType = getExplicitAccessType( containerJavaType, propertyName, field ); + if ( explicitAccessType != null ) { + return explicitAccessType; + } + + // No @Access on property or field; check to see if containerJavaType has an explicit @Access + AccessType classAccessType = getAccessTypeOrNull( containerJavaType ); + if ( classAccessType != null ) { + return classAccessType; + } + + // prefer using the field for getting if we can + return field != null ? AccessType.FIELD : AccessType.PROPERTY; + } + + public static AccessType getExplicitAccessType(Class containerClass, String propertyName, Field field) { + if ( isRecord( containerClass ) ) { + try { + containerClass.getMethod( propertyName, NO_PARAM_SIGNATURE ); + return AccessType.PROPERTY; + } + catch (NoSuchMethodException e) { + // Ignore + } + } + + if ( field != null + && field.isAnnotationPresent( Access.class ) + && !field.isAnnotationPresent( Transient.class ) + && !Modifier.isStatic( field.getModifiers() ) ) { + return AccessType.FIELD; + } + + for ( Method method : containerClass.getDeclaredMethods() ) { + // if the method has parameters, skip it + if ( method.getParameterCount() != 0 ) { + continue; + } + + // if the method is a "bridge", skip it + if ( method.isBridge() ) { + continue; + } + + if ( method.isAnnotationPresent( Transient.class ) ) { + continue; + } + + if ( Modifier.isStatic( method.getModifiers() ) ) { + continue; + } + + final String methodName = method.getName(); + + // try "get" + if ( methodName.startsWith( "get" ) ) { + final String stemName = methodName.substring( 3 ); + final String decapitalizedStemName = Introspector.decapitalize( stemName ); + if ( stemName.equals( propertyName ) || decapitalizedStemName.equals( propertyName ) ) { + if ( method.isAnnotationPresent( Access.class ) ) { + return AccessType.PROPERTY; + } + else { + checkIsMethodVariant( containerClass, propertyName, method, stemName ); + } + } + } + + // if not "get", then try "is" + if ( methodName.startsWith( "is" ) ) { + final String stemName = methodName.substring( 2 ); + String decapitalizedStemName = Introspector.decapitalize( stemName ); + if ( stemName.equals( propertyName ) || decapitalizedStemName.equals( propertyName ) ) { + if ( method.isAnnotationPresent( Access.class ) ) { + return AccessType.PROPERTY; + } + } + } + } + + return null; + } + + private static void checkIsMethodVariant( + Class containerClass, + String propertyName, + Method method, + String stemName) { + final Method isMethodVariant = findIsMethodVariant( containerClass, stemName ); + if ( isMethodVariant == null ) { + return; + } + + if ( !isMethodVariant.isAnnotationPresent( Access.class ) ) { + throw new MappingException( + String.format( + Locale.ROOT, + "In trying to locate getter for property [%s], Class [%s] defined " + + "both a `get` [%s] and `is` [%s] variant", + propertyName, + containerClass.getName(), + method.toString(), + isMethodVariant.toString() + ) + ); + } + } + + public static Method findIsMethodVariant(Class containerClass, String stemName) { + // verify that the Class does not also define a method with the same stem name with 'is' + try { + final Method isMethod = containerClass.getDeclaredMethod( "is" + stemName ); + if ( !Modifier.isStatic( isMethod.getModifiers() ) && isMethod.getAnnotation( Transient.class ) == null ) { + return isMethod; + } + } + catch (NoSuchMethodException ignore) { + } + + return null; + } + + protected static AccessType getAccessTypeOrNull(AnnotatedElement element) { + if ( element == null ) { + return null; + } + Access elementAccess = element.getAnnotation( Access.class ); + return elementAccess == null ? null : elementAccess.value(); + } + + public static int determineEnhancementState(Class containerClass, Class attributeType) { + return ( CompositeOwner.class.isAssignableFrom( containerClass ) ? AccessStrategyHelper.COMPOSITE_OWNER : 0 ) + | ( CompositeTracker.class.isAssignableFrom( attributeType ) ? AccessStrategyHelper.COMPOSITE_TRACKER_MASK : 0 ) + | ( isPersistentAttributeInterceptableType( containerClass ) ? AccessStrategyHelper.PERSISTENT_ATTRIBUTE_INTERCEPTABLE_MASK : 0 ); + } + + public static void handleEnhancedInjection(Object target, Object value, int enhancementState, String propertyName) { + // This sets the component relation for dirty tracking purposes + if ( ( enhancementState & COMPOSITE_OWNER ) != 0 + && ( ( enhancementState & COMPOSITE_TRACKER_MASK ) != 0 + && value != null + || isCompositeTracker( value ) ) ) { + asCompositeTracker( value ).$$_hibernate_setOwner( propertyName, asCompositeOwner( target ) ); + } + + // This marks the attribute as initialized, so it doesn't get lazily loaded afterward + if ( ( enhancementState & PERSISTENT_ATTRIBUTE_INTERCEPTABLE_MASK ) != 0 ) { + PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable( target ).$$_hibernate_getInterceptor(); + if ( interceptor instanceof BytecodeLazyAttributeInterceptor ) { + ( (BytecodeLazyAttributeInterceptor) interceptor ).attributeInitialized( propertyName ); + } + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/ChainedPropertyAccessImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/ChainedPropertyAccessImpl.java index 38c0502138..33f92abddd 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/internal/ChainedPropertyAccessImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/ChainedPropertyAccessImpl.java @@ -51,6 +51,7 @@ public class ChainedPropertyAccessImpl implements PropertyAccess, Getter, Setter return owner; } + @SuppressWarnings("rawtypes") @Override public Object getForInsert(Object owner, Map mergeMap, SharedSessionContractImplementor session) { for ( int i = 0; i < propertyAccesses.length; i++ ) { diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessCompositeUserTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessCompositeUserTypeImpl.java index 37a517294c..78a4096d2e 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessCompositeUserTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessCompositeUserTypeImpl.java @@ -55,6 +55,7 @@ public class PropertyAccessCompositeUserTypeImpl implements PropertyAccess, Gett return strategy.compositeUserType.getPropertyValue( owner, propertyIndex ); } + @SuppressWarnings("rawtypes") @Override public Object getForInsert(Object owner, Map mergeMap, SharedSessionContractImplementor session) { return get( owner ); diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessEmbeddedImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessEmbeddedImpl.java index 65c919352c..4c9b3cff34 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessEmbeddedImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessEmbeddedImpl.java @@ -67,6 +67,7 @@ public class PropertyAccessEmbeddedImpl implements PropertyAccess { return owner; } + @SuppressWarnings("rawtypes") @Override public Object getForInsert(Object owner, Map mergeMap, SharedSessionContractImplementor session) { return owner; diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessEnhancedImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessEnhancedImpl.java index dfbfec376b..7bcd1bb4ca 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessEnhancedImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessEnhancedImpl.java @@ -7,11 +7,24 @@ package org.hibernate.property.access.internal; import org.hibernate.property.access.spi.EnhancedSetterImpl; +import org.hibernate.property.access.spi.EnhancedSetterMethodImpl; +import org.hibernate.property.access.spi.Getter; +import org.hibernate.property.access.spi.GetterFieldImpl; +import org.hibernate.property.access.spi.GetterMethodImpl; import org.hibernate.property.access.spi.PropertyAccess; +import org.hibernate.property.access.spi.PropertyAccessBuildingException; import org.hibernate.property.access.spi.PropertyAccessStrategy; import org.hibernate.property.access.spi.Setter; +import org.hibernate.property.access.spi.SetterMethodImpl; import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import jakarta.persistence.AccessType; + +import static org.hibernate.internal.util.ReflectHelper.findSetterMethod; +import static org.hibernate.internal.util.ReflectHelper.getterMethodOrNull; +import static org.hibernate.property.access.internal.AccessStrategyHelper.fieldOrNull; /** * A {@link PropertyAccess} for byte code enhanced entities. Enhanced setter methods ( if available ) are used for @@ -20,17 +33,77 @@ import java.lang.reflect.Field; * @author Steve Ebersole * @author Luis Barreiro */ -public class PropertyAccessEnhancedImpl extends PropertyAccessMixedImpl { +public class PropertyAccessEnhancedImpl implements PropertyAccess { + private final PropertyAccessStrategy strategy; + + private final Getter getter; + private final Setter setter; public PropertyAccessEnhancedImpl( PropertyAccessStrategy strategy, Class containerJavaType, - String propertyName) { - super( strategy, containerJavaType, propertyName ); + String propertyName, + AccessType getterAccessType) { + this.strategy = strategy; + + final AccessType propertyAccessType = resolveAccessType( getterAccessType, containerJavaType, propertyName ); + + switch ( propertyAccessType ) { + case FIELD: { + final Field field = fieldOrNull( containerJavaType, propertyName ); + if ( field == null ) { + throw new PropertyAccessBuildingException( + "Could not locate field for property named [" + containerJavaType.getName() + "#" + propertyName + "]" + ); + } + this.getter = new GetterFieldImpl( containerJavaType, propertyName, field ); + this.setter = new EnhancedSetterImpl( containerJavaType, propertyName, field ); + break; + } + case PROPERTY: { + final Method getterMethod = getterMethodOrNull( containerJavaType, propertyName ); + if ( getterMethod == null ) { + throw new PropertyAccessBuildingException( + "Could not locate getter for property named [" + containerJavaType.getName() + "#" + propertyName + "]" + ); + } + final Method setterMethod = findSetterMethod( containerJavaType, propertyName, getterMethod.getReturnType() ); + + this.getter = new GetterMethodImpl( containerJavaType, propertyName, getterMethod ); + this.setter = new EnhancedSetterMethodImpl( containerJavaType, propertyName, setterMethod ); + break; + } + default: { + throw new PropertyAccessBuildingException( + "Invalid access type " + propertyAccessType + " for property named [" + containerJavaType.getName() + "#" + propertyName + "]" + ); + } + } + } + + private AccessType resolveAccessType(AccessType getterAccessType, Class containerJavaType, String propertyName) { + if ( getterAccessType != null ) { + // this should indicate FIELD access + return getterAccessType; + } + + // prefer using the field for getting if we can + final Field field = AccessStrategyHelper.fieldOrNull( containerJavaType, propertyName ); + return field != null ? AccessType.FIELD : AccessType.PROPERTY; } @Override - protected Setter fieldSetter(Class containerJavaType, String propertyName, Field field) { - return new EnhancedSetterImpl( containerJavaType, propertyName, field ); + public PropertyAccessStrategy getPropertyAccessStrategy() { + return strategy; + } + + @Override + public Getter getGetter() { + return getter; + } + + @Override + public Setter getSetter() { + return setter; } } diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessMapImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessMapImpl.java index 7cfb316a51..433fde5dd0 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessMapImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessMapImpl.java @@ -58,6 +58,7 @@ public class PropertyAccessMapImpl implements PropertyAccess { } @Override + @SuppressWarnings("rawtypes") public Object get(Object owner) { return ( (Map) owner ).get( propertyName ); } diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessMixedImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessMixedImpl.java index 2d9fe5e6bd..b274e6641d 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessMixedImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessMixedImpl.java @@ -6,14 +6,9 @@ */ package org.hibernate.property.access.internal; -import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.Method; -import jakarta.persistence.Access; -import jakarta.persistence.AccessType; -import org.hibernate.PropertyNotFoundException; -import org.hibernate.internal.util.ReflectHelper; import org.hibernate.property.access.spi.Getter; import org.hibernate.property.access.spi.GetterFieldImpl; import org.hibernate.property.access.spi.GetterMethodImpl; @@ -24,8 +19,10 @@ import org.hibernate.property.access.spi.Setter; import org.hibernate.property.access.spi.SetterFieldImpl; import org.hibernate.property.access.spi.SetterMethodImpl; -import static org.hibernate.internal.util.ReflectHelper.getterMethodOrNull; +import jakarta.persistence.AccessType; + import static org.hibernate.internal.util.ReflectHelper.findSetterMethod; +import static org.hibernate.internal.util.ReflectHelper.getterMethodOrNull; /** * A {@link PropertyAccess} based on mix of getter/setter method and/or field. @@ -38,20 +35,16 @@ public class PropertyAccessMixedImpl implements PropertyAccess { private final Getter getter; private final Setter setter; - public PropertyAccessMixedImpl( - PropertyAccessStrategy strategy, - Class containerJavaType, - String propertyName) { + public PropertyAccessMixedImpl(PropertyAccessStrategy strategy, Class containerJavaType, String propertyName) { this.strategy = strategy; - AccessType propertyAccessType = getAccessType( containerJavaType, propertyName ); - + final AccessType propertyAccessType = AccessStrategyHelper.getAccessType( containerJavaType, propertyName ); switch ( propertyAccessType ) { case FIELD: { - Field field = fieldOrNull( containerJavaType, propertyName ); + Field field = AccessStrategyHelper.fieldOrNull( containerJavaType, propertyName ); if ( field == null ) { throw new PropertyAccessBuildingException( - "Could not locate field for property named [" + containerJavaType.getName() + "#" + propertyName + "]" + "Could not locate field for property named [" + containerJavaType.getName() + "#" + propertyName + "]" ); } this.getter = fieldGetter( containerJavaType, propertyName, field ); @@ -62,7 +55,7 @@ public class PropertyAccessMixedImpl implements PropertyAccess { Method getterMethod = getterMethodOrNull( containerJavaType, propertyName ); if ( getterMethod == null ) { throw new PropertyAccessBuildingException( - "Could not locate getter for property named [" + containerJavaType.getName() + "#" + propertyName + "]" + "Could not locate getter for property named [" + containerJavaType.getName() + "#" + propertyName + "]" ); } Method setterMethod = findSetterMethod( containerJavaType, propertyName, getterMethod.getReturnType() ); @@ -73,47 +66,12 @@ public class PropertyAccessMixedImpl implements PropertyAccess { } default: { throw new PropertyAccessBuildingException( - "Invalid access type " + propertyAccessType + " for property named [" + containerJavaType.getName() + "#" + propertyName + "]" + "Invalid access type " + propertyAccessType + " for property named [" + containerJavaType.getName() + "#" + propertyName + "]" ); } } } - protected static Field fieldOrNull(Class containerJavaType, String propertyName) { - try { - return ReflectHelper.findField( containerJavaType, propertyName ); - } - catch (PropertyNotFoundException e) { - return null; - } - } - - protected static AccessType getAccessType(Class containerJavaType, String propertyName) { - Field field = fieldOrNull( containerJavaType, propertyName ); - AccessType fieldAccessType = getAccessTypeOrNull( field ); - if ( fieldAccessType != null ) { - return fieldAccessType; - } - AccessType methodAccessType = getAccessTypeOrNull( getterMethodOrNull( containerJavaType, propertyName ) ); - if ( methodAccessType != null ) { - return methodAccessType; - } - // No @Access on property or field; check to see if containerJavaType has an explicit @Access - AccessType classAccessType = getAccessTypeOrNull( containerJavaType ); - if ( classAccessType != null ) { - return classAccessType; - } - return field != null ? AccessType.FIELD : AccessType.PROPERTY; - } - - private static AccessType getAccessTypeOrNull(AnnotatedElement element) { - if ( element == null ) { - return null; - } - Access elementAccess = element.getAnnotation( Access.class ); - return elementAccess == null ? null : elementAccess.value(); - } - // --- // protected Getter fieldGetter(Class containerJavaType, String propertyName, Field field) { diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyBackRefImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyBackRefImpl.java index c6d1e5e354..904da3c3ad 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyBackRefImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyBackRefImpl.java @@ -92,6 +92,7 @@ public class PropertyAccessStrategyBackRefImpl implements PropertyAccessStrategy } @Override + @SuppressWarnings("rawtypes") public Object getForInsert(Object owner, Map mergeMap, SharedSessionContractImplementor session) { if ( session == null ) { return UNKNOWN; diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyEnhancedImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyEnhancedImpl.java index 1c7d89d2fc..255f818e04 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyEnhancedImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyEnhancedImpl.java @@ -6,9 +6,12 @@ */ package org.hibernate.property.access.internal; +import org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.property.access.spi.PropertyAccessStrategy; +import jakarta.persistence.AccessType; + /** * Defines a strategy for accessing property values via a get/set pair, which may be nonpublic. This * is the default (and recommended) strategy. @@ -17,13 +20,24 @@ import org.hibernate.property.access.spi.PropertyAccessStrategy; * @author Gavin King */ public class PropertyAccessStrategyEnhancedImpl implements PropertyAccessStrategy { - /** - * Singleton access - */ - public static final PropertyAccessStrategyEnhancedImpl INSTANCE = new PropertyAccessStrategyEnhancedImpl(); + public static PropertyAccessStrategyEnhancedImpl with(AccessType getterAccessType) { + if ( getterAccessType == AccessType.FIELD ) { + return FIELD; + } + return STANDARD; + } + + private final AccessType getterAccessType; + + public static PropertyAccessStrategyEnhancedImpl STANDARD = new PropertyAccessStrategyEnhancedImpl( null ); + public static PropertyAccessStrategyEnhancedImpl FIELD = new PropertyAccessStrategyEnhancedImpl( AccessType.FIELD ); + + public PropertyAccessStrategyEnhancedImpl(AccessType getterAccessType) { + this.getterAccessType = getterAccessType; + } @Override public PropertyAccess buildPropertyAccess(Class containerJavaType, final String propertyName, boolean setterRequired) { - return new PropertyAccessEnhancedImpl( this, containerJavaType, propertyName ); + return new PropertyAccessEnhancedImpl( this, containerJavaType, propertyName, getterAccessType ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyIndexBackRefImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyIndexBackRefImpl.java index 8e243bbd20..8112f6d57f 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyIndexBackRefImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyIndexBackRefImpl.java @@ -74,6 +74,7 @@ public class PropertyAccessStrategyIndexBackRefImpl implements PropertyAccessStr return PropertyAccessStrategyBackRefImpl.UNKNOWN; } + @SuppressWarnings("rawtypes") @Override public Object getForInsert(Object owner, Map mergeMap, SharedSessionContractImplementor session) { if ( session == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyNoopImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyNoopImpl.java index 50dab5db7f..8328565b99 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyNoopImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyNoopImpl.java @@ -67,6 +67,7 @@ public class PropertyAccessStrategyNoopImpl implements PropertyAccessStrategy { } @Override + @SuppressWarnings("rawtypes") public Object getForInsert(Object owner, Map mergeMap, SharedSessionContractImplementor session) { return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyResolverStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyResolverStandardImpl.java index 142a39404d..12bad9f270 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyResolverStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyResolverStandardImpl.java @@ -15,6 +15,8 @@ import org.hibernate.property.access.spi.PropertyAccessStrategy; import org.hibernate.property.access.spi.PropertyAccessStrategyResolver; import org.hibernate.service.ServiceRegistry; +import jakarta.persistence.AccessType; + import static org.hibernate.engine.internal.ManagedTypeHelper.isManagedType; /** @@ -40,8 +42,10 @@ public class PropertyAccessStrategyResolverStandardImpl implements PropertyAcces || BuiltInPropertyAccessStrategies.MIXED.getExternalName().equals( explicitAccessStrategyName ) ) { //type-cache-pollution agent: it's crucial to use the ManagedTypeHelper rather than attempting a direct cast if ( isManagedType( containerClass ) ) { - // PROPERTY (BASIC) and MIXED are not valid for bytecode enhanced entities... - return PropertyAccessStrategyEnhancedImpl.INSTANCE; + if ( BuiltInPropertyAccessStrategies.FIELD.getExternalName().equals( explicitAccessStrategyName ) ) { + return PropertyAccessStrategyEnhancedImpl.FIELD; + } + return PropertyAccessStrategyEnhancedImpl.STANDARD; } } diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/spi/EnhancedSetterImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/spi/EnhancedSetterImpl.java index 2a706fd695..1585159a48 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/spi/EnhancedSetterImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/spi/EnhancedSetterImpl.java @@ -9,18 +9,11 @@ package org.hibernate.property.access.spi; import java.io.Serializable; import java.lang.reflect.Field; -import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor; -import org.hibernate.engine.internal.ManagedTypeHelper; -import org.hibernate.engine.spi.CompositeOwner; -import org.hibernate.engine.spi.CompositeTracker; -import org.hibernate.engine.spi.PersistentAttributeInterceptor; +import org.hibernate.Internal; import org.hibernate.property.access.internal.AbstractFieldSerialForm; +import org.hibernate.property.access.internal.AccessStrategyHelper; -import static org.hibernate.engine.internal.ManagedTypeHelper.asCompositeOwner; -import static org.hibernate.engine.internal.ManagedTypeHelper.asCompositeTracker; -import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; -import static org.hibernate.engine.internal.ManagedTypeHelper.isCompositeTracker; -import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptableType; +import static org.hibernate.property.access.internal.AccessStrategyHelper.determineEnhancementState; /** * A specialized Setter implementation for handling setting values into @@ -31,39 +24,21 @@ import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttrib * @author Steve Ebersole * @author Luis Barreiro */ +@Internal public class EnhancedSetterImpl extends SetterFieldImpl { - - private static final int COMPOSITE_TRACKER_MASK = 1; - private static final int COMPOSITE_OWNER = 2; - private static final int PERSISTENT_ATTRIBUTE_INTERCEPTABLE_MASK = 4; - private final String propertyName; private final int enhancementState; public EnhancedSetterImpl(Class containerClass, String propertyName, Field field) { super( containerClass, propertyName, field ); this.propertyName = propertyName; - this.enhancementState = ( CompositeOwner.class.isAssignableFrom( containerClass ) ? COMPOSITE_OWNER : 0 ) - | ( CompositeTracker.class.isAssignableFrom( field.getType() ) ? COMPOSITE_TRACKER_MASK : 0 ) - | ( isPersistentAttributeInterceptableType( containerClass ) ? PERSISTENT_ATTRIBUTE_INTERCEPTABLE_MASK : 0 ); + this.enhancementState = determineEnhancementState( containerClass, field.getType() ); } @Override public void set(Object target, Object value) { super.set( target, value ); - - // This sets the component relation for dirty tracking purposes - if ( ( enhancementState & COMPOSITE_OWNER ) != 0 && ( ( enhancementState & COMPOSITE_TRACKER_MASK ) != 0 && value != null || isCompositeTracker( value ) ) ) { - asCompositeTracker( value ).$$_hibernate_setOwner( propertyName, asCompositeOwner( target ) ); - } - - // This marks the attribute as initialized, so it doesn't get lazily loaded afterwards - if ( ( enhancementState & PERSISTENT_ATTRIBUTE_INTERCEPTABLE_MASK ) != 0 ) { - PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable( target ).$$_hibernate_getInterceptor(); - if ( interceptor instanceof BytecodeLazyAttributeInterceptor ) { - ( (BytecodeLazyAttributeInterceptor) interceptor ).attributeInitialized( propertyName ); - } - } + AccessStrategyHelper.handleEnhancedInjection( target, value, enhancementState, propertyName ); } @@ -74,13 +49,12 @@ public class EnhancedSetterImpl extends SetterFieldImpl { return new SerialForm( getContainerClass(), propertyName, getField() ); } - @SuppressWarnings("rawtypes") private static class SerialForm extends AbstractFieldSerialForm implements Serializable { - private final Class containerClass; + private final Class containerClass; private final String propertyName; - private SerialForm(Class containerClass, String propertyName, Field field) { + private SerialForm(Class containerClass, String propertyName, Field field) { super( field ); this.containerClass = containerClass; this.propertyName = propertyName; diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/spi/EnhancedSetterMethodImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/spi/EnhancedSetterMethodImpl.java new file mode 100644 index 0000000000..1a8513224d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/property/access/spi/EnhancedSetterMethodImpl.java @@ -0,0 +1,61 @@ +/* + * 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.property.access.spi; + +import java.io.Serializable; +import java.lang.reflect.Method; + +import org.hibernate.Internal; +import org.hibernate.property.access.internal.AbstractSetterMethodSerialForm; +import org.hibernate.property.access.internal.AccessStrategyHelper; + +import static org.hibernate.property.access.internal.AccessStrategyHelper.determineEnhancementState; + +/** + * A specialized Setter implementation for handling setting values into a bytecode-enhanced Class + * using a setter method. The reason we need specialized handling is to render the fact that we + * need to account for certain enhancement features during the setting process. + * + * @author Steve Ebersole + * @author Luis Barreiro + */ +@Internal +public class EnhancedSetterMethodImpl extends SetterMethodImpl { + private final String propertyName; + private final int enhancementState; + + public EnhancedSetterMethodImpl(Class containerClass, String propertyName, Method setterMethod) { + super( containerClass, propertyName, setterMethod ); + this.propertyName = propertyName; + this.enhancementState = determineEnhancementState( containerClass, setterMethod.getReturnType() ); + } + + @Override + public void set(Object target, Object value) { + super.set( target, value ); + + AccessStrategyHelper.handleEnhancedInjection( target, value, enhancementState, propertyName ); + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // serialization + + private Object writeReplace() { + return new SerialForm( getContainerClass(), propertyName, getMethod() ); + } + + private static class SerialForm extends AbstractSetterMethodSerialForm implements Serializable { + private SerialForm(Class containerClass, String propertyName, Method method) { + super( containerClass, propertyName, method ); + } + + private Object readResolve() { + return new EnhancedSetterMethodImpl( getContainerClass(), getPropertyName(), resolveMethod() ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/spi/GetterFieldImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/spi/GetterFieldImpl.java index 1d352a3506..9b53f6a415 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/spi/GetterFieldImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/spi/GetterFieldImpl.java @@ -15,6 +15,7 @@ import java.lang.reflect.Type; import java.util.Locale; import java.util.Map; +import org.hibernate.Internal; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.property.access.internal.AbstractFieldSerialForm; @@ -24,6 +25,7 @@ import org.hibernate.property.access.internal.AbstractFieldSerialForm; * * @author Steve Ebersole */ +@Internal public class GetterFieldImpl implements Getter { private final Class containerClass; private final String propertyName; @@ -58,6 +60,7 @@ public class GetterFieldImpl implements Getter { } } + @SuppressWarnings("rawtypes") @Override public Object getForInsert(Object owner, Map mergeMap, SharedSessionContractImplementor session) { return get( owner ); diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/spi/GetterMethodImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/spi/GetterMethodImpl.java index 10fb419257..6f1a948d11 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/spi/GetterMethodImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/spi/GetterMethodImpl.java @@ -14,6 +14,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Map; +import org.hibernate.Internal; import org.hibernate.PropertyAccessException; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.CoreMessageLogger; @@ -25,6 +26,7 @@ import static org.hibernate.internal.CoreLogging.messageLogger; /** * @author Steve Ebersole */ +@Internal public class GetterMethodImpl implements Getter { private static final CoreMessageLogger LOG = messageLogger( GetterMethodImpl.class ); @@ -79,6 +81,7 @@ public class GetterMethodImpl implements Getter { } } + @SuppressWarnings("rawtypes") @Override public Object getForInsert(Object owner, Map mergeMap, SharedSessionContractImplementor session) { return get( owner ); diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/spi/SetterFieldImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/spi/SetterFieldImpl.java index 7c555ca431..8bffda51be 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/spi/SetterFieldImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/spi/SetterFieldImpl.java @@ -11,6 +11,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Locale; +import org.hibernate.Internal; import org.hibernate.PropertyAccessException; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.property.access.internal.AbstractFieldSerialForm; @@ -22,6 +23,7 @@ import org.hibernate.proxy.LazyInitializer; * * @author Steve Ebersole */ +@Internal public class SetterFieldImpl implements Setter { private final Class containerClass; private final String propertyName; @@ -73,9 +75,12 @@ public class SetterFieldImpl implements Setter { if ( lazyInitializer != null ) { valueType = lazyInitializer.getEntityName(); } - else { + else if ( value != null ) { valueType = value.getClass().getTypeName(); } + else { + valueType = ""; + } throw new PropertyAccessException( e, String.format( diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/spi/SetterMethodImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/spi/SetterMethodImpl.java index 20e706d624..ebbb9533dd 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/spi/SetterMethodImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/spi/SetterMethodImpl.java @@ -10,16 +10,18 @@ import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import org.hibernate.Internal; import org.hibernate.PropertyAccessException; import org.hibernate.PropertySetterAccessException; import org.hibernate.internal.CoreMessageLogger; -import org.hibernate.internal.util.ReflectHelper; +import org.hibernate.property.access.internal.AbstractSetterMethodSerialForm; import static org.hibernate.internal.CoreLogging.messageLogger; /** * @author Steve Ebersole */ +@Internal public class SetterMethodImpl implements Setter { private static final CoreMessageLogger LOG = messageLogger( SetterMethodImpl.class ); @@ -112,6 +114,10 @@ public class SetterMethodImpl implements Setter { } } + public Class getContainerClass() { + return containerClass; + } + @Override public String getMethodName() { return setterMethod.getName(); @@ -126,38 +132,13 @@ public class SetterMethodImpl implements Setter { return new SerialForm( containerClass, propertyName, setterMethod ); } - private static class SerialForm implements Serializable { - private final Class containerClass; - private final String propertyName; - - private final Class declaringClass; - private final String methodName; - private final Class argumentType; - + private static class SerialForm extends AbstractSetterMethodSerialForm implements Serializable { private SerialForm(Class containerClass, String propertyName, Method method) { - this.containerClass = containerClass; - this.propertyName = propertyName; - this.declaringClass = method.getDeclaringClass(); - this.methodName = method.getName(); - this.argumentType = method.getParameterTypes()[0]; + super( containerClass, propertyName, method ); } private Object readResolve() { - return new SetterMethodImpl( containerClass, propertyName, resolveMethod() ); - } - - private Method resolveMethod() { - try { - final Method method = declaringClass.getDeclaredMethod( methodName, argumentType ); - ReflectHelper.ensureAccessibility( method ); - return method; - } - catch (NoSuchMethodException e) { - throw new PropertyAccessSerializationException( - "Unable to resolve setter method on deserialization : " + declaringClass.getName() + "#" - + methodName + "(" + argumentType.getName() + ")" - ); - } + return new SetterMethodImpl( getContainerClass(), getPropertyName(), resolveMethod() ); } } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/property/FieldMappingWithGetterAndIsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/property/FieldMappingWithGetterAndIsTest.java new file mode 100644 index 0000000000..7f6adfb7f9 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/property/FieldMappingWithGetterAndIsTest.java @@ -0,0 +1,63 @@ +/* + * 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.orm.test.property; + +import org.hibernate.mapping.PersistentClass; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.DomainModelScope; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +/** + * @author Steve Ebersole + */ +@DomainModel(annotatedClasses = FieldMappingWithGetterAndIsTest.Tester.class) +@SessionFactory +public class FieldMappingWithGetterAndIsTest { + @Test + public void testResolution(DomainModelScope modelScope, SessionFactoryScope factoryScope) { + final PersistentClass entityBinding = modelScope.getEntityBinding( Tester.class ); + factoryScope.getCollectingStatementInspector(); + } + + @Entity(name="Tester") + @Table(name="Tester") + public static class Tester { + @Id + private Integer id; + @Basic + private String name; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public boolean isName() { + return name != null; + } + + public void setName(String name) { + this.name = name; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/property/FieldMappingWithGetterAndIsTest2.java b/hibernate-core/src/test/java/org/hibernate/orm/test/property/FieldMappingWithGetterAndIsTest2.java new file mode 100644 index 0000000000..3b25a63cc9 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/property/FieldMappingWithGetterAndIsTest2.java @@ -0,0 +1,68 @@ +/* + * 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.orm.test.property; + +import org.hibernate.boot.MetadataSources; + +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.EnhancementOptions; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.Test; +import org.junit.runner.RunWith; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +/** + * @author Steve Ebersole + */ +@RunWith(BytecodeEnhancerRunner.class) +@EnhancementOptions( inlineDirtyChecking = true, lazyLoading = true ) +public class FieldMappingWithGetterAndIsTest2 extends BaseNonConfigCoreFunctionalTestCase { + @Override + protected void applyMetadataSources(MetadataSources sources) { + super.applyMetadataSources( sources ); + sources.addAnnotatedClass( Tester.class ); + } + + @Test + public void testResolution() { + sessionFactory(); + } + + @Entity(name="Tester") + @Table(name="Tester") + public static class Tester { + @Id + private Integer id; + @Basic + private String name; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public boolean isName() { + return name != null; + } + + public void setName(String name) { + this.name = name; + } + } + +}