Skip to content

Commit 3635a49

Browse files
VladoKurucbeikov
authored andcommitted
HHH-19140 Reproducer test and issue fix (#9758)
* HHH-19140 Add test case * HHH-19140 Fix for issue
1 parent 68f65b5 commit 3635a49

File tree

2 files changed

+157
-86
lines changed

2 files changed

+157
-86
lines changed

hibernate-core/src/main/java/org/hibernate/property/access/internal/AccessStrategyHelper.java

Lines changed: 4 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,11 @@
66
*/
77
package org.hibernate.property.access.internal;
88

9-
import java.beans.Introspector;
109
import java.lang.reflect.AnnotatedElement;
1110
import java.lang.reflect.Field;
1211
import java.lang.reflect.Method;
1312
import java.lang.reflect.Modifier;
14-
import java.util.Locale;
1513

16-
import org.hibernate.MappingException;
1714
import org.hibernate.PropertyNotFoundException;
1815
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
1916
import org.hibernate.engine.spi.CompositeOwner;
@@ -33,6 +30,7 @@
3330
import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptableType;
3431
import static org.hibernate.internal.util.ReflectHelper.NO_PARAM_SIGNATURE;
3532
import static org.hibernate.internal.util.ReflectHelper.findField;
33+
import static org.hibernate.internal.util.ReflectHelper.getterMethodOrNull;
3634
import static org.hibernate.internal.util.ReflectHelper.isRecord;
3735

3836
/**
@@ -87,89 +85,9 @@ public static AccessType getAccessType(Class<?> containerJavaType, String proper
8785
return AccessType.FIELD;
8886
}
8987

90-
for ( Method method : containerClass.getDeclaredMethods() ) {
91-
// if the method has parameters, skip it
92-
if ( method.getParameterCount() != 0 ) {
93-
continue;
94-
}
95-
96-
// if the method is a "bridge", skip it
97-
if ( method.isBridge() ) {
98-
continue;
99-
}
100-
101-
if ( method.isAnnotationPresent( Transient.class ) ) {
102-
continue;
103-
}
104-
105-
if ( Modifier.isStatic( method.getModifiers() ) ) {
106-
continue;
107-
}
108-
109-
final String methodName = method.getName();
110-
111-
// try "get"
112-
if ( methodName.startsWith( "get" ) ) {
113-
final String stemName = methodName.substring( 3 );
114-
final String decapitalizedStemName = Introspector.decapitalize( stemName );
115-
if ( stemName.equals( propertyName ) || decapitalizedStemName.equals( propertyName ) ) {
116-
if ( method.isAnnotationPresent( Access.class ) ) {
117-
return AccessType.PROPERTY;
118-
}
119-
else {
120-
checkIsMethodVariant( containerClass, propertyName, method, stemName );
121-
}
122-
}
123-
}
124-
125-
// if not "get", then try "is"
126-
if ( methodName.startsWith( "is" ) ) {
127-
final String stemName = methodName.substring( 2 );
128-
String decapitalizedStemName = Introspector.decapitalize( stemName );
129-
if ( stemName.equals( propertyName ) || decapitalizedStemName.equals( propertyName ) ) {
130-
if ( method.isAnnotationPresent( Access.class ) ) {
131-
return AccessType.PROPERTY;
132-
}
133-
}
134-
}
135-
}
136-
137-
return null;
138-
}
139-
140-
private static void checkIsMethodVariant(
141-
Class<?> containerClass,
142-
String propertyName,
143-
Method method,
144-
String stemName) {
145-
final Method isMethodVariant = findIsMethodVariant( containerClass, stemName );
146-
if ( isMethodVariant == null ) {
147-
return;
148-
}
149-
150-
if ( !isMethodVariant.isAnnotationPresent( Access.class ) ) {
151-
throw new MappingException(
152-
String.format(
153-
Locale.ROOT,
154-
"Class '%s' declares both 'get' [%s] and 'is' [%s] variants of getter for property '%s'",
155-
containerClass.getName(),
156-
method.toString(),
157-
isMethodVariant,
158-
propertyName
159-
)
160-
);
161-
}
162-
}
163-
164-
public static @Nullable Method findIsMethodVariant(Class<?> containerClass, String stemName) {
165-
// verify that the Class does not also define a method with the same stem name with 'is'
166-
try {
167-
final Method isMethod = containerClass.getDeclaredMethod( "is" + stemName );
168-
if ( !Modifier.isStatic( isMethod.getModifiers() ) && isMethod.getAnnotation( Transient.class ) == null ) {
169-
return isMethod;
170-
}
171-
}
172-
catch (NoSuchMethodException ignore) {
88+
final Method getter = getterMethodOrNull( containerClass, propertyName );
89+
if ( getter != null && getter.isAnnotationPresent( Access.class ) ) {
90+
return AccessType.PROPERTY;
17391
}
17492

17593
return null;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.bytecode.enhancement.access;
6+
7+
import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced;
8+
import org.hibernate.testing.orm.junit.DomainModel;
9+
import org.hibernate.testing.orm.junit.JiraKey;
10+
import org.hibernate.testing.orm.junit.SessionFactory;
11+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
12+
import org.junit.jupiter.api.AfterEach;
13+
import org.junit.jupiter.api.Test;
14+
15+
import jakarta.persistence.Access;
16+
import jakarta.persistence.AccessType;
17+
import jakarta.persistence.Basic;
18+
import jakarta.persistence.DiscriminatorColumn;
19+
import jakarta.persistence.DiscriminatorValue;
20+
import jakarta.persistence.Entity;
21+
import jakarta.persistence.Id;
22+
import jakarta.persistence.Inheritance;
23+
import jakarta.persistence.Table;
24+
import jakarta.persistence.Transient;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
28+
@DomainModel(
29+
annotatedClasses = {
30+
HierarchyPropertyAccessTest.ChildEntity.class,
31+
}
32+
)
33+
@SessionFactory
34+
@JiraKey("HHH-19140")
35+
@BytecodeEnhanced
36+
public class HierarchyPropertyAccessTest {
37+
38+
39+
@Test
40+
public void testParent(SessionFactoryScope scope) {
41+
scope.inTransaction( session -> {
42+
session.persist( new ParentEntity( 1L, "field", "transient: property" ) );
43+
} );
44+
45+
scope.inTransaction( session -> {
46+
ParentEntity entity = session.get( ParentEntity.class, 1L );
47+
assertThat( entity.persistProperty ).isEqualTo( "property" );
48+
assertThat( entity.property ).isEqualTo( "transient: property" );
49+
50+
entity.setProperty( "transient: updated" );
51+
} );
52+
53+
scope.inTransaction( session -> {
54+
ParentEntity entity = session.get( ParentEntity.class, 1L );
55+
assertThat( entity.persistProperty ).isEqualTo( "updated" );
56+
assertThat( entity.property ).isEqualTo( "transient: updated" );
57+
} );
58+
}
59+
60+
@Test
61+
public void testChild(SessionFactoryScope scope) {
62+
scope.inTransaction( session -> {
63+
session.persist( new ChildEntity( 2L, "field", "transient: property" ) );
64+
} );
65+
66+
scope.inTransaction( session -> {
67+
ChildEntity entity = session.get( ChildEntity.class, 2L );
68+
assertThat( entity.persistProperty ).isEqualTo( "property" );
69+
assertThat( entity.property ).isEqualTo( "transient: property" );
70+
71+
entity.setProperty( "transient: updated" );
72+
} );
73+
74+
scope.inTransaction( session -> {
75+
ChildEntity entity = session.get( ChildEntity.class, 2L );
76+
assertThat( entity.persistProperty ).isEqualTo( "updated" );
77+
assertThat( entity.property ).isEqualTo( "transient: updated" );
78+
} );
79+
}
80+
81+
@AfterEach
82+
public void cleanup(SessionFactoryScope scope) {
83+
scope.inTransaction( session -> {
84+
ParentEntity parentEntity = session.get( ParentEntity.class, 1L );
85+
if (parentEntity != null) {
86+
session.remove( parentEntity );
87+
}
88+
ChildEntity childEntity = session.get( ChildEntity.class, 2L );
89+
if (childEntity != null) {
90+
session.remove( childEntity );
91+
}
92+
} );
93+
}
94+
95+
@Entity
96+
@Table(name = "PARENT_ENTITY")
97+
@Inheritance
98+
@DiscriminatorColumn(name = "type")
99+
@DiscriminatorValue("Parent")
100+
static class ParentEntity {
101+
@Id
102+
Long id;
103+
104+
@Basic
105+
String field;
106+
107+
String persistProperty;
108+
109+
@Transient
110+
String property;
111+
112+
public ParentEntity() {
113+
}
114+
115+
public ParentEntity(Long id, String field, String property) {
116+
this.id = id;
117+
this.field = field;
118+
this.property = property;
119+
}
120+
121+
@Basic
122+
@Access(AccessType.PROPERTY)
123+
public String getPersistProperty() {
124+
this.persistProperty = this.property.substring( 11 );
125+
return this.persistProperty;
126+
}
127+
128+
public void setPersistProperty(String persistProperty) {
129+
this.property = "transient: " + persistProperty;
130+
this.persistProperty = persistProperty;
131+
}
132+
133+
public String getProperty() {
134+
return this.property;
135+
}
136+
137+
public void setProperty(String property) {
138+
this.property = property;
139+
}
140+
}
141+
142+
@Entity
143+
@DiscriminatorValue("Child")
144+
static class ChildEntity extends ParentEntity {
145+
146+
public ChildEntity() {
147+
}
148+
149+
public ChildEntity(Long id, String field, String property) {
150+
super(id, field, property);
151+
}
152+
}
153+
}

0 commit comments

Comments
 (0)