Skip to content

Commit 7a7b0a3

Browse files
committed
fix for GRAILS-3209 "GORM inheritance related bug with id: this.id == null != this.getId() when accessed in method for a class that is subclassed"
1 parent c482e49 commit 7a7b0a3

File tree

5 files changed

+100
-11
lines changed

5 files changed

+100
-11
lines changed

src/java/grails/util/GrailsNameUtils.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,27 @@
2727
*/
2828
public class GrailsNameUtils {
2929

30+
private static final String PROPERTY_SET_PREFIX = "set";
31+
32+
/**
33+
* Retrieves the name of a setter for the specified property name
34+
* @param propertyName The property name
35+
* @return The setter equivalent
36+
*/
37+
public static String getSetterName(String propertyName) {
38+
return PROPERTY_SET_PREFIX+propertyName.substring(0,1).toUpperCase()+ propertyName.substring(1);
39+
}
40+
41+
/**
42+
* Calculate the name for a getter method to retrieve the specified property
43+
* @param propertyName
44+
* @return The name for the getter method for this property, if it were to exist, i.e. getConstraints
45+
*/
46+
public static String getGetterName(String propertyName) {
47+
return "get" + Character.toUpperCase(propertyName.charAt(0))
48+
+ propertyName.substring(1);
49+
}
50+
3051
/**
3152
* Returns the class name for the given logical name and trailing name. For example "person" and "Controller" would evaluate to "PersonController"
3253
*

src/java/org/codehaus/groovy/grails/commons/GrailsClassUtils.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515
package org.codehaus.groovy.grails.commons;
1616

17+
import grails.util.GrailsNameUtils;
1718
import groovy.lang.*;
1819

1920
import org.apache.commons.lang.StringUtils;
@@ -36,7 +37,7 @@
3637
*/
3738
public class GrailsClassUtils {
3839

39-
private static final String PROPERTY_SET_PREFIX = "set";
40+
4041
public static final Map<Class<?>, Class<?>> PRIMITIVE_TYPE_COMPATIBLE_CLASSES = new HashMap<Class<?>, Class<?>>();
4142

4243
/**
@@ -631,8 +632,7 @@ public static boolean isPublicStatic(Field f) {
631632
* @return The name for the getter method for this property, if it were to exist, i.e. getConstraints
632633
*/
633634
public static String getGetterName(String propertyName) {
634-
return "get" + Character.toUpperCase(propertyName.charAt(0))
635-
+ propertyName.substring(1);
635+
return GrailsNameUtils.getGetterName(propertyName);
636636
}
637637

638638
/**
@@ -812,7 +812,7 @@ public static String getLogicalPropertyName(String className, String trailingNam
812812
* @return The setter equivalent
813813
*/
814814
public static String getSetterName(String propertyName) {
815-
return PROPERTY_SET_PREFIX+propertyName.substring(0,1).toUpperCase()+ propertyName.substring(1);
815+
return GrailsNameUtils.getSetterName(propertyName);
816816
}
817817

818818
/**

src/java/org/codehaus/groovy/grails/compiler/injection/DefaultGrailsDomainClassInjector.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,15 +198,19 @@ private void injectVersionProperty(ClassNode classNode) {
198198
final boolean hasVersion = GrailsASTUtils.hasOrInheritsProperty(classNode, GrailsDomainClassProperty.VERSION);
199199

200200
if (!hasVersion) {
201-
classNode.addProperty(GrailsDomainClassProperty.VERSION, Modifier.PUBLIC, new ClassNode(Long.class), null, null, null);
201+
ClassNode parent = GrailsASTUtils.getFurthestParent(classNode);
202+
parent.addProperty(GrailsDomainClassProperty.VERSION, Modifier.PUBLIC, new ClassNode(Long.class), null, null, null);
202203
}
203204
}
204205

205206
private void injectIdProperty(ClassNode classNode) {
206207
final boolean hasId = GrailsASTUtils.hasOrInheritsProperty(classNode, GrailsDomainClassProperty.IDENTITY);
207208

208209
if (!hasId) {
209-
classNode.addProperty(GrailsDomainClassProperty.IDENTITY, Modifier.PUBLIC, new ClassNode(Long.class), null, null, null);
210+
// inject into furthest relative
211+
ClassNode parent = GrailsASTUtils.getFurthestParent(classNode);
212+
213+
parent.addProperty(GrailsDomainClassProperty.IDENTITY, Modifier.PUBLIC, new ClassNode(Long.class), null, null, null);
210214
}
211215
}
212216

src/java/org/codehaus/groovy/grails/compiler/injection/GrailsASTUtils.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.codehaus.groovy.grails.compiler.injection;
1717

18+
import grails.util.GrailsNameUtils;
1819
import org.apache.commons.lang.StringUtils;
1920
import org.codehaus.groovy.ast.ClassNode;
2021
import org.codehaus.groovy.ast.MethodNode;
@@ -44,11 +45,16 @@ public class GrailsASTUtils {
4445
public static boolean hasProperty(ClassNode classNode, String propertyName) {
4546
if (classNode == null || StringUtils.isBlank(propertyName))
4647
return false;
47-
List properties = classNode.getProperties();
48-
for (Iterator i = properties.iterator(); i.hasNext();) {
49-
PropertyNode pn = (PropertyNode) i.next();
50-
if (pn.getName().equals(propertyName) && !pn.isPrivate()) {
51-
return true;
48+
49+
final MethodNode method = classNode.getMethod(GrailsNameUtils.getGetterName(propertyName), new Parameter[0]);
50+
if(method != null) return true;
51+
else {
52+
List properties = classNode.getProperties();
53+
for (Object property : properties) {
54+
PropertyNode pn = (PropertyNode) property;
55+
if (pn.getName().equals(propertyName) && !pn.isPrivate()) {
56+
return true;
57+
}
5258
}
5359
}
5460
return false;
@@ -106,4 +112,12 @@ public static String getFullName(ClassNode classNode) {
106112
return classNode.getName();
107113
}
108114

115+
public static ClassNode getFurthestParent(ClassNode classNode) {
116+
ClassNode parent = classNode.getSuperClass();
117+
while (parent != null && !getFullName(parent).equals("java.lang.Object")) {
118+
parent = parent.getSuperClass();
119+
classNode = parent;
120+
}
121+
return classNode;
122+
}
109123
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.codehaus.groovy.grails.orm.hibernate
2+
3+
/**
4+
* @author Graeme Rocher
5+
* @since 1.1
6+
*/
7+
class IdInheritanceTests extends AbstractGrailsHibernateTests {
8+
9+
protected void onSetUp() {
10+
gcl.parseClass('''
11+
import grails.persistence.*
12+
13+
@Entity
14+
abstract class Parent {
15+
String toString() {
16+
return "${id}"
17+
}
18+
}
19+
''')
20+
gcl.parseClass('''
21+
import grails.persistence.*
22+
23+
@Entity
24+
class Child extends Parent {
25+
26+
static transients = ['superId']
27+
28+
Long getSuperId() {
29+
return super.id
30+
}
31+
}
32+
33+
''')
34+
}
35+
36+
37+
void testDirectAccess() {
38+
def child = ga.getDomainClass("Child").newInstance()
39+
child.save('flush': true)
40+
41+
assert 1 == child.id
42+
}
43+
44+
void testInheritedMethodAccess() {
45+
def child = ga.getDomainClass("Child").newInstance()
46+
child.save('flush': true)
47+
48+
assert "1" == child.toString()
49+
}
50+
}

0 commit comments

Comments
 (0)