This is an automated email from the ASF dual-hosted git repository.

borinquenkid pushed a commit to branch 8.0.x-hibernate7-dev
in repository https://gitbox.apache.org/repos/asf/grails-core.git


The following commit(s) were added to refs/heads/8.0.x-hibernate7-dev by this 
push:
     new 4b38b08a97 hibernate7: cleanup TraitPropertyAccessStrategy
4b38b08a97 is described below

commit 4b38b08a97d3729834cd08976326b3beccfa5f71
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Mon Mar 9 15:54:09 2026 -0500

    hibernate7: cleanup TraitPropertyAccessStrategy
---
 .../access/TraitPropertyAccessStrategy.java        | 118 ++++++++++++---------
 .../access/TraitPropertyAccessStrategySpec.groovy  |  28 +++++
 2 files changed, 93 insertions(+), 53 deletions(-)

diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/access/TraitPropertyAccessStrategy.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/access/TraitPropertyAccessStrategy.java
index fa59f130ff..265fb7a93e 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/access/TraitPropertyAccessStrategy.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/access/TraitPropertyAccessStrategy.java
@@ -25,6 +25,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
 import org.checkerframework.checker.nullness.qual.UnknownKeyFor;
 import org.codehaus.groovy.transform.trait.Traits;
 import org.grails.datastore.mapping.reflect.NameUtils;
+import org.hibernate.MappingException;
 import org.hibernate.property.access.spi.*;
 import org.springframework.util.ReflectionUtils;
 
@@ -38,6 +39,19 @@ import org.springframework.util.ReflectionUtils;
 public class TraitPropertyAccessStrategy implements PropertyAccessStrategy {
 
   public PropertyAccess buildPropertyAccess(Class containerJavaType, String 
propertyName) {
+    return buildPropertyAccess(containerJavaType, propertyName, true);
+  }
+
+  protected String getTraitFieldName(Class traitClass, String fieldName) {
+    return traitClass.getName().replace('.', '_') + "__" + fieldName;
+  }
+
+  @java.lang.Override
+  public @UnknownKeyFor @NonNull @Initialized PropertyAccess 
buildPropertyAccess(
+      java.lang.@UnknownKeyFor @NonNull @Initialized Class<@UnknownKeyFor 
@NonNull @Initialized ?>
+          containerJavaType,
+      java.lang.@UnknownKeyFor @NonNull @Initialized String propertyName,
+      @UnknownKeyFor @Initialized boolean setterRequired) {
     Method readMethod =
         ReflectionUtils.findMethod(containerJavaType, 
NameUtils.getGetterName(propertyName));
     if (readMethod == null) {
@@ -59,72 +73,70 @@ public class TraitPropertyAccessStrategy implements 
PropertyAccessStrategy {
               + "] of class ["
               + containerJavaType.getName()
               + "] that is not provided by a trait!");
+    }
+
+    Traits.Implemented traitImplemented = 
readMethod.getAnnotation(Traits.Implemented.class);
+    final String traitFieldName;
+    if (traitImplemented == null) {
+      Traits.TraitBridge traitBridge = 
readMethod.getAnnotation(Traits.TraitBridge.class);
+      if (traitBridge != null) {
+        traitFieldName = getTraitFieldName(traitBridge.traitClass(), 
propertyName);
+      } else {
+        throw new IllegalStateException(
+            "TraitPropertyAccessStrategy used on property ["
+                + propertyName
+                + "] of class ["
+                + containerJavaType.getName()
+                + "] that is not provided by a trait!");
+      }
     } else {
+      traitFieldName = getTraitFieldName(readMethod.getDeclaringClass(), 
propertyName);
+    }
 
-      Traits.Implemented traitImplemented = 
readMethod.getAnnotation(Traits.Implemented.class);
-      final String traitFieldName;
-      if (traitImplemented == null) {
-        Traits.TraitBridge traitBridge = 
readMethod.getAnnotation(Traits.TraitBridge.class);
-        if (traitBridge != null) {
-          traitFieldName = getTraitFieldName(traitBridge.traitClass(), 
propertyName);
-        } else {
-          throw new IllegalStateException(
+    Field field = ReflectionUtils.findField(containerJavaType, traitFieldName);
+    final Getter getter;
+    final Setter setter;
+    if (field == null) {
+      getter = new GetterMethodImpl(containerJavaType, propertyName, 
readMethod);
+      Method writeMethod =
+          ReflectionUtils.findMethod(
+              containerJavaType,
+              NameUtils.getSetterName(propertyName),
+              readMethod.getReturnType());
+      if (writeMethod == null) {
+        if (setterRequired) {
+          throw new MappingException(
               "TraitPropertyAccessStrategy used on property ["
                   + propertyName
                   + "] of class ["
                   + containerJavaType.getName()
-                  + "] that is not provided by a trait!");
+                  + "] that has no setter!");
         }
+        setter = null;
       } else {
-        traitFieldName = getTraitFieldName(readMethod.getDeclaringClass(), 
propertyName);
-      }
-
-      Field field = ReflectionUtils.findField(containerJavaType, 
traitFieldName);
-      final Getter getter;
-      final Setter setter;
-      if (field == null) {
-        getter = new GetterMethodImpl(containerJavaType, propertyName, 
readMethod);
-        Method writeMethod =
-            ReflectionUtils.findMethod(
-                containerJavaType,
-                NameUtils.getSetterName(propertyName),
-                readMethod.getReturnType());
         setter = new SetterMethodImpl(containerJavaType, propertyName, 
writeMethod);
-      } else {
-
-        getter = new GetterFieldImpl(containerJavaType, propertyName, field);
-        setter = new SetterFieldImpl(containerJavaType, propertyName, field);
       }
+    } else {
 
-      return new PropertyAccess() {
-        @Override
-        public PropertyAccessStrategy getPropertyAccessStrategy() {
-          return TraitPropertyAccessStrategy.this;
-        }
-
-        @Override
-        public Getter getGetter() {
-          return getter;
-        }
-
-        @Override
-        public Setter getSetter() {
-          return setter;
-        }
-      };
+      getter = new GetterFieldImpl(containerJavaType, propertyName, field);
+      setter = new SetterFieldImpl(containerJavaType, propertyName, field);
     }
-  }
 
-  protected String getTraitFieldName(Class traitClass, String fieldName) {
-    return traitClass.getName().replace('.', '_') + "__" + fieldName;
-  }
+    return new PropertyAccess() {
+      @Override
+      public PropertyAccessStrategy getPropertyAccessStrategy() {
+        return TraitPropertyAccessStrategy.this;
+      }
 
-  @java.lang.Override
-  public @UnknownKeyFor @NonNull @Initialized PropertyAccess 
buildPropertyAccess(
-      java.lang.@UnknownKeyFor @NonNull @Initialized Class<@UnknownKeyFor 
@NonNull @Initialized ?>
-          containerJavaType,
-      java.lang.@UnknownKeyFor @NonNull @Initialized String propertyName,
-      @UnknownKeyFor @NonNull @Initialized boolean setterRequired) {
-    return buildPropertyAccess(containerJavaType, propertyName);
+      @Override
+      public Getter getGetter() {
+        return getter;
+      }
+
+      @Override
+      public Setter getSetter() {
+        return setter;
+      }
+    };
   }
 }
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/access/TraitPropertyAccessStrategySpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/access/TraitPropertyAccessStrategySpec.groovy
index 540338b416..017c7703e1 100644
--- 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/access/TraitPropertyAccessStrategySpec.groovy
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/access/TraitPropertyAccessStrategySpec.groovy
@@ -18,6 +18,7 @@
  */
 package org.grails.orm.hibernate.access
 
+import org.hibernate.MappingException
 import org.hibernate.property.access.spi.GetterFieldImpl
 import org.hibernate.property.access.spi.GetterMethodImpl
 import org.hibernate.property.access.spi.SetterFieldImpl
@@ -39,6 +40,10 @@ trait HasFlag {
     Boolean flag
 }
 
+trait HasComputed {
+    String getComputed() { "foo" }
+}
+
 /** Plain Groovy class — no trait involvement. */
 class PlainPerson {
     String plain
@@ -53,6 +58,9 @@ class ActiveEntity implements HasActive {}
 /** Groovy class implementing a boxed-Boolean trait. */
 class FlaggedEntity implements HasFlag {}
 
+/** Groovy class implementing a computed-property trait. */
+class ComputedEntity implements HasComputed {}
+
 // ─── Spec 
─────────────────────────────────────────────────────────────────────
 
 class TraitPropertyAccessStrategySpec extends Specification {
@@ -258,5 +266,25 @@ class TraitPropertyAccessStrategySpec extends 
Specification {
         nameAccess.getter.returnTypeClass   == String
         activeAccess.getter.returnTypeClass == boolean
     }
+
+    // ─── Read-only property (no field, no setter) ───────────────────────────
+
+    void "buildPropertyAccess for computed property returns method-based 
getter and no setter if not required"() {
+        when:
+        def access = strategy.buildPropertyAccess(ComputedEntity, 'computed', 
false)
+
+        then:
+        access != null
+        access.getter instanceof GetterMethodImpl
+        access.setter == null
+    }
+
+    void "buildPropertyAccess for computed property throws MappingException if 
setter is required"() {
+        when:
+        strategy.buildPropertyAccess(ComputedEntity, 'computed', true)
+
+        then:
+        thrown(MappingException)
+    }
 }
 

Reply via email to