1818import grails .core .GrailsDomainClass ;
1919import grails .core .GrailsDomainClassProperty ;
2020import grails .io .IOUtils ;
21+ import grails .persistence .PersistenceMethod ;
2122import grails .util .GrailsClassUtils ;
2223import grails .validation .Constrained ;
2324import grails .validation .ConstrainedProperty ;
3435import org .grails .core .artefact .DomainClassArtefactHandler ;
3536import org .grails .core .exceptions .GrailsConfigurationException ;
3637import org .grails .core .support .GrailsDomainConfigurationUtil ;
38+ import org .springframework .beans .BeanUtils ;
39+ import org .springframework .beans .CachedIntrospectionResults ;
3740
3841import java .beans .BeanInfo ;
3942import java .beans .IntrospectionException ;
4346import java .lang .reflect .Field ;
4447import java .lang .reflect .Method ;
4548import java .lang .reflect .Modifier ;
46- import java .util .ArrayList ;
47- import java .util .Arrays ;
48- import java .util .Collection ;
49- import java .util .Collections ;
50- import java .util .HashMap ;
51- import java .util .List ;
52- import java .util .Map ;
49+ import java .util .*;
5350
5451/**
5552 * Default implementation of the {@link grails.validation.ConstraintsEvaluator} interface.
@@ -156,7 +153,7 @@ protected Map<String, Constrained> evaluateConstraints(Map<String, Constrained>
156153
157154 boolean isDomainClass = DomainClassArtefactHandler .isDomainClass (theClass );
158155 Map <String , GrailsDomainClassProperty > domainClassPropertyMap = indexPropertiesByPropertyName (domainClassProperties );
159- Map <String , Method > constrainablePropertyMap = getConstrainablePropertyMap (theClass );
156+ Map <String , Method > constrainablePropertyMap = getConstrainablePropertyMap (theClass , isDomainClass );
160157
161158 for (String propertyName : constrainablePropertyMap .keySet ()) {
162159 GrailsDomainClassProperty domainClassProperty = domainClassPropertyMap .get (propertyName );
@@ -177,6 +174,10 @@ protected Map<String, Constrained> evaluateConstraints(Map<String, Constrained>
177174 }
178175 }
179176 }
177+ else {
178+ // for domain class, a constraint of a property as only getter method is not supported
179+ continue ;
180+ }
180181 }
181182
182183 // complete constraints not defined by user.
@@ -359,38 +360,42 @@ protected void applyDefaultNullableConstraint(Constrained constrained, boolean d
359360 }
360361 }
361362
362- protected Map <String , Method > getConstrainablePropertyMap (Class theClass ) {
363- List <String > ignoredProperties = new ArrayList <>();
363+ protected Map <String , Method > getConstrainablePropertyMap (Class theClass , boolean isDomainClass ) {
364+ Set <String > ignoredProperties = new HashSet <>();
364365 ignoredProperties .add (GrailsDomainClassProperty .CLASS );
365366 ignoredProperties .add (GrailsDomainClassProperty .META_CLASS );
366367 ignoredProperties .add (GrailsDomainClassProperty .ERRORS );
367- if (DomainClassArtefactHandler .isDomainClass (theClass )) {
368+
369+ if (isDomainClass ) {
368370 ignoredProperties .add (GrailsDomainConfigurationUtil .PROPERTIES_PROPERTY );
369371 ignoredProperties .add (GrailsDomainClassProperty .IDENTITY );
370372 ignoredProperties .add (GrailsDomainClassProperty .VERSION );
371373 ignoredProperties .add (GrailsDomainClassProperty .DIRTY_PROPERTY_NAMES );
372374 ignoredProperties .add (GrailsDomainClassProperty .DIRTY );
373375 ignoredProperties .add (GrailsDomainClassProperty .ATTACHED );
374- }
375-
376- final Object transients = GrailsClassUtils .getStaticPropertyValue (theClass ,
377- GrailsDomainClassProperty .TRANSIENT );
378- if (transients instanceof List ) {
379- ignoredProperties .addAll ((List ) transients );
380- }
381- try {
382- final BeanInfo beanInfo = Introspector .getBeanInfo (theClass );
383- final PropertyDescriptor [] propertyDescriptors = beanInfo .getPropertyDescriptors ();
376+ final Object transients = GrailsClassUtils .getStaticPropertyValue (theClass ,
377+ GrailsDomainClassProperty .TRANSIENT );
378+ if (transients instanceof List ) {
379+ ignoredProperties .addAll ((List ) transients );
380+ }
381+ final PropertyDescriptor [] propertyDescriptors = BeanUtils .getPropertyDescriptors (theClass );
384382 for (PropertyDescriptor descriptor : propertyDescriptors ) {
385383 final Method readMethod = descriptor .getReadMethod ();
386- if (readMethod != null && Modifier .isTransient (readMethod .getModifiers ())) {
384+
385+ final Method writeMethod = descriptor .getWriteMethod ();
386+ if ( readMethod == null ) {
387387 ignoredProperties .add (descriptor .getName ());
388388 }
389+ else if (writeMethod == null || (Modifier .isTransient (readMethod .getModifiers ()))) {
390+ PersistenceMethod annotation = readMethod .getAnnotation (PersistenceMethod .class );
391+ if (annotation == null ) {
392+ ignoredProperties .add (descriptor .getName ());
393+ }
394+ }
389395 }
390- } catch (IntrospectionException e ) {
391- LOG .error ("An error occurred introspecting properties" , e );
392396 }
393397
398+
394399 Field [] declaredFields = theClass .getDeclaredFields ();
395400 for (Field field : declaredFields ) {
396401 if (Modifier .isTransient (field .getModifiers ())) {
0 commit comments