You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2017/07/14 11:31:22 UTC

cayenne git commit: CAY-2331 cgen: broken templates for data map + fix for client single class templates

Repository: cayenne
Updated Branches:
  refs/heads/master f7ef3e409 -> 60b8acc40


CAY-2331 cgen: broken templates for data map
   + fix for client single class templates


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/60b8acc4
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/60b8acc4
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/60b8acc4

Branch: refs/heads/master
Commit: 60b8acc402ad6142f31a2c84a0abd098ef9ac5c2
Parents: f7ef3e4
Author: Nikita Timofeev <st...@gmail.com>
Authored: Fri Jul 14 14:31:15 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Fri Jul 14 14:31:15 2017 +0300

----------------------------------------------------------------------
 .../gen/ClientClassGenerationAction.java        |  17 +-
 .../v1_2/client-datamap-singleclass.vm          |  10 +-
 .../templates/v1_2/client-datamap-superclass.vm |   4 +-
 .../templates/v1_2/client-singleclass.vm        | 250 +++++++++++++++++++
 .../resources/templates/v1_2/client-subclass.vm |   2 +-
 .../templates/v1_2/client-superclass.vm         |  35 ++-
 .../templates/v1_2/datamap-singleclass.vm       |  14 +-
 .../templates/v1_2/datamap-superclass.vm        |  10 +-
 docs/doc/src/main/resources/RELEASE-NOTES.txt   |   1 +
 9 files changed, 302 insertions(+), 41 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/60b8acc4/cayenne-cgen/src/main/java/org/apache/cayenne/gen/ClientClassGenerationAction.java
----------------------------------------------------------------------
diff --git a/cayenne-cgen/src/main/java/org/apache/cayenne/gen/ClientClassGenerationAction.java b/cayenne-cgen/src/main/java/org/apache/cayenne/gen/ClientClassGenerationAction.java
index 5386c96..bca02a2 100644
--- a/cayenne-cgen/src/main/java/org/apache/cayenne/gen/ClientClassGenerationAction.java
+++ b/cayenne-cgen/src/main/java/org/apache/cayenne/gen/ClientClassGenerationAction.java
@@ -32,29 +32,38 @@ public class ClientClassGenerationAction extends ClassGenerationAction {
 
     public static final String SUBCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "client-subclass.vm";
     public static final String SUPERCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "client-superclass.vm";
+    public static final String SINGLE_CLASS_TEMPLATE = TEMPLATES_DIR_NAME + "client-singleclass.vm";
     
-    public static final String DMAP_SINGLE_CLASS_TEMPLATE = TEMPLATES_DIR_NAME + "client-datamap-singleclass.vm";
     public static final String DMAP_SUBCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "client-datamap-subclass.vm";
     public static final String DMAP_SUPERCLASS_TEMPLATE = TEMPLATES_DIR_NAME + "client-datamap-superclass.vm";
-    
+    public static final String DMAP_SINGLE_CLASS_TEMPLATE = TEMPLATES_DIR_NAME + "client-datamap-singleclass.vm";
+
     public static final String CLIENT_SUPERCLASS_PREFIX = "_Client";
 
     @Override
     protected String defaultTemplateName(TemplateType type) {
         switch (type) {
             case ENTITY_SUBCLASS:
-                return ClientClassGenerationAction.SUBCLASS_TEMPLATE;
+                return SUBCLASS_TEMPLATE;
             case ENTITY_SUPERCLASS:
-                return ClientClassGenerationAction.SUPERCLASS_TEMPLATE;
+                return SUPERCLASS_TEMPLATE;
+            case ENTITY_SINGLE_CLASS:
+                return SINGLE_CLASS_TEMPLATE;
+
             case EMBEDDABLE_SUBCLASS:
                 return EMBEDDABLE_SUBCLASS_TEMPLATE;
             case EMBEDDABLE_SUPERCLASS:
                 return EMBEDDABLE_SUPERCLASS_TEMPLATE;
+            case EMBEDDABLE_SINGLE_CLASS:
+                return EMBEDDABLE_SINGLE_CLASS_TEMPLATE;
             
             case DATAMAP_SUPERCLASS:
                 return ClientClassGenerationAction.DMAP_SUPERCLASS_TEMPLATE;
             case DATAMAP_SUBCLASS:
                 return ClientClassGenerationAction.DMAP_SUBCLASS_TEMPLATE;
+            case DATAMAP_SINGLE_CLASS:
+                return DMAP_SINGLE_CLASS_TEMPLATE;
+
             default:
                 throw new IllegalArgumentException("Unsupported template type: " + type);
         }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/60b8acc4/cayenne-cgen/src/main/resources/templates/v1_2/client-datamap-singleclass.vm
----------------------------------------------------------------------
diff --git a/cayenne-cgen/src/main/resources/templates/v1_2/client-datamap-singleclass.vm b/cayenne-cgen/src/main/resources/templates/v1_2/client-datamap-singleclass.vm
index a72fd39..89abb81 100644
--- a/cayenne-cgen/src/main/resources/templates/v1_2/client-datamap-singleclass.vm
+++ b/cayenne-cgen/src/main/resources/templates/v1_2/client-datamap-singleclass.vm
@@ -34,17 +34,17 @@
 ##
 ##
 ${importUtils.setPackage($subPackageName)}##
-${importUtils.addReservedType("${subPackageName}.${subClassName}")}##
-${importUtils.addType("${basePackageName}.${baseClassName}")}##
-${importUtils.addType('java.util.List')}##
 ${importUtils.addType('org.apache.cayenne.ObjectContext')}##
+#if( ${object.hasSelectQueries()} )
+${importUtils.addType('java.util.List')}##
 ${importUtils.addType('org.apache.cayenne.query.MappedSelect')}##
 #foreach( $selectQuery in ${object.SelectQueries})
-${importUtils.addType(${selectQuery.Root.ClassName})}
+${importUtils.addType(${selectQuery.Root.ClientClassName})}
 #foreach( $parameter in ${dataMapUtils.getParameterNames(${selectQuery})})
 ${importUtils.addType(${dataMapUtils.getParameterType(${selectQuery}, ${parameter})})}
 #end
 #end
+#end
 #if($object.hasExecQueries())##
 ${importUtils.addType('java.util.Map')}##
 ${importUtils.addType('org.apache.cayenne.QueryResult')}##
@@ -78,7 +78,7 @@ public class ${subClassName} {
     }
 
 #foreach( $selectQuery in ${object.SelectQueries})
-#set($rootClass = ${stringUtils.stripPackageName($selectQuery.Root.ClassName)})
+#set($rootClass = ${stringUtils.stripPackageName($selectQuery.Root.ClientClassName)})
     public List<$rootClass> perform${dataMapUtils.getQueryMethodName(${selectQuery})}(ObjectContext context#foreach( $parameter in ${dataMapUtils.getParameterNames(${selectQuery})}), ${stringUtils.stripPackageName(${dataMapUtils.getParameterType(${selectQuery}, ${parameter})})} ${parameter}#end) {
         MappedSelect<${rootClass}> query = MappedSelect.query(${stringUtils.capitalizedAsConstant(${selectQuery.Name})}_QUERYNAME, ${rootClass}.class);
 #foreach( $parameter in ${dataMapUtils.getParameterNames(${selectQuery})})

http://git-wip-us.apache.org/repos/asf/cayenne/blob/60b8acc4/cayenne-cgen/src/main/resources/templates/v1_2/client-datamap-superclass.vm
----------------------------------------------------------------------
diff --git a/cayenne-cgen/src/main/resources/templates/v1_2/client-datamap-superclass.vm b/cayenne-cgen/src/main/resources/templates/v1_2/client-datamap-superclass.vm
index 3c63a19..a690a0e 100644
--- a/cayenne-cgen/src/main/resources/templates/v1_2/client-datamap-superclass.vm
+++ b/cayenne-cgen/src/main/resources/templates/v1_2/client-datamap-superclass.vm
@@ -34,9 +34,9 @@
 ${importUtils.setPackage($superPackageName)}##
 #if(${superPackageName})${importUtils.addReservedType("${superPackageName}.${superClassName}")}#end##
 #if(${basePackageName})${importUtils.addType("${basePackageName}.${baseClassName}")}#end##
+${importUtils.addType('org.apache.cayenne.ObjectContext')}##
 #if( ${object.hasSelectQueries()} )
 ${importUtils.addType('java.util.List')}##
-${importUtils.addType('org.apache.cayenne.ObjectContext')}##
 ${importUtils.addType('org.apache.cayenne.query.MappedSelect')}##
 #foreach( $selectQuery in ${object.SelectQueries})
 ${importUtils.addType(${selectQuery.Root.ClientClassName})}##
@@ -67,7 +67,7 @@ public class ${superClassName} {
 #end
 
 #foreach( $selectQuery in ${object.SelectQueries})
-#set($rootClass = ${stringUtils.stripPackageName($selectQuery.Root.ClassName)})
+#set($rootClass = ${stringUtils.stripPackageName($selectQuery.Root.ClientClassName)})
     public List<$rootClass> perform${dataMapUtils.getQueryMethodName(${selectQuery})}(ObjectContext context#foreach( $parameter in ${dataMapUtils.getParameterNames(${selectQuery})}), ${stringUtils.stripPackageName(${dataMapUtils.getParameterType(${selectQuery}, ${parameter})})} ${parameter}#end) {
         MappedSelect<${rootClass}> query = MappedSelect.query(${stringUtils.capitalizedAsConstant(${selectQuery.Name})}_QUERYNAME, ${rootClass}.class);
 #foreach( $parameter in ${dataMapUtils.getParameterNames(${selectQuery})})

http://git-wip-us.apache.org/repos/asf/cayenne/blob/60b8acc4/cayenne-cgen/src/main/resources/templates/v1_2/client-singleclass.vm
----------------------------------------------------------------------
diff --git a/cayenne-cgen/src/main/resources/templates/v1_2/client-singleclass.vm b/cayenne-cgen/src/main/resources/templates/v1_2/client-singleclass.vm
new file mode 100644
index 0000000..7820bff
--- /dev/null
+++ b/cayenne-cgen/src/main/resources/templates/v1_2/client-singleclass.vm
@@ -0,0 +1,250 @@
+##   Licensed to the Apache Software Foundation (ASF) under one
+##  or more contributor license agreements.  See the NOTICE file
+##  distributed with this work for additional information
+##  regarding copyright ownership.  The ASF licenses this file
+##  to you under the Apache License, Version 2.0 (the
+##  "License"); you may not use this file except in compliance
+##  with the License.  You may obtain a copy of the License at
+##
+##    http://www.apache.org/licenses/LICENSE-2.0
+##
+##  Unless required by applicable law or agreed to in writing,
+##  software distributed under the License is distributed on an
+##  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+##  KIND, either express or implied.  See the License for the
+##  specific language governing permissions and limitations
+##  under the License.
+##
+##Terminology:
+##	Base class - super superclass of entity, ie, org.apache.cayenne.PersistentObject or MyBaseClass
+##  Super class - superclass of entity, ie,  org.apache.cayenne.art.auto._Artist
+##	Sub class - class of entity, ie, org.apache.cayenne.art.Artist
+##
+##  Classes available in template
+##    object (duplicated as 'objEntity') - the ObjEntity class: See org.apache.cayenne.map.ObjectEntity
+##    stringUtils - class for string "helper" functions: See org.apache.cayenne.gen.StringUtils
+##    entityUtils - class for entity "helper" functions: See org.apache.cayenne.gen.EntityUtils
+##    importUtils - class for import statement management: See org.apache.cayenne.gen.ImportUtils
+##    superClassName
+##    superPackageName
+##    subClassName
+##    subPackageName
+##    baseClassName
+##    basePackageName
+##
+##
+${importUtils.setPackage($subPackageName)}##
+${importUtils.addReservedType("${$subPackageName}.${subClassName}")}##
+${importUtils.addType("${basePackageName}.${baseClassName}")}##
+#if((${object.DeclaredAttributes} && !${object.DeclaredAttributes.isEmpty()}) || (${object.DeclaredRelationships} && !${object.DeclaredRelationships.isEmpty()}))
+${importUtils.addType('org.apache.cayenne.exp.Property')}##
+#end
+#foreach( $attr in ${object.DeclaredAttributes} )
+$importUtils.addType(${attr.Type})##
+#end
+#foreach( $rel in ${object.DeclaredRelationships} )
+$importUtils.addType(${rel.TargetEntity.ClientClassName})##
+#if(${rel.CollectionType}) 
+$importUtils.addType(${rel.CollectionType})##
+#end
+#end
+#if( ${entityUtils.hasToOneDeclaredRelationships()} )
+${importUtils.addType('org.apache.cayenne.ValueHolder')}##
+${importUtils.addType('org.apache.cayenne.util.PersistentObjectHolder')}##
+#end
+#if( ${entityUtils.hasToManyDeclaredRelationships()} )
+${importUtils.addType('org.apache.cayenne.util.PersistentObjectList')}##
+#end
+${importUtils.generate()}
+
+/**
+ * A generated persistent class mapped as "${object.name}" Cayenne entity. It is a good idea to
+ * avoid changing this class manually, since it will be overwritten next time code is
+ * regenerated. If you need to make any customizations, put them in a subclass.
+ */
+public#if("true" == "${object.getIsAbstract()}") abstract#end class ${subClassName} extends ${baseClassName} {
+
+    private static final long serialVersionUID = 1L;
+
+## Create ivars names
+#if( $createPropertyNames )
+#foreach( $attr in ${object.DeclaredAttributes} )
+    public static final String ${stringUtils.capitalizedAsConstant($attr.Name)}_PROPERTY = "${attr.Name}";
+#end
+#foreach( $rel in ${object.DeclaredRelationships} )
+    public static final String ${stringUtils.capitalizedAsConstant($rel.Name)}_PROPERTY = "${rel.Name}";
+#end
+
+#end
+## Create Properties
+#foreach( $attr in ${object.DeclaredAttributes} )
+    #set ( $type = "$importUtils.formatJavaType(${attr.Type}, false)" )
+    public static final Property<$type> ${stringUtils.capitalizedAsConstant($attr.Name)} = Property.create("${attr.Name}", ${stringUtils.stripGeneric($type)}.class);
+#end
+#foreach( $rel in ${object.DeclaredRelationships} )
+#if( $rel.ToMany )
+#if ( ${rel.CollectionType} == "java.util.Map")
+    #set( $type = "$importUtils.formatJavaType($rel.CollectionType)<$importUtils.formatJavaType($entityUtils.getMapKeyType($rel)), $importUtils.formatJavaType($rel.TargetEntity.ClientClassName)>" )
+    public static final Property<$type> ${stringUtils.capitalizedAsConstant($rel.Name)} = Property.create("${rel.Name}", ${stringUtils.stripGeneric($type)}.class);
+#else
+    #set( $type = "$importUtils.formatJavaType($rel.CollectionType)<$importUtils.formatJavaType($rel.TargetEntity.ClientClassName)>" )
+    public static final Property<$type> ${stringUtils.capitalizedAsConstant($rel.Name)} = Property.create("${rel.Name}", ${stringUtils.stripGeneric($type)}.class);
+#end
+#else
+    #set( $type = "$importUtils.formatJavaType(${rel.TargetEntity.ClientClassName})" )
+    public static final Property<$type> ${stringUtils.capitalizedAsConstant($rel.Name)} = Property.create("${rel.Name}", ${stringUtils.stripGeneric($type)}.class);
+#end
+#end
+
+## Create ivars
+#foreach( $attr in ${object.DeclaredAttributes} )
+    protected $importUtils.formatJavaType(${attr.Type}) ${attr.Name};
+#end
+#foreach( $rel in ${object.DeclaredRelationships} )
+#if( $rel.ToMany )
+#if ( ${rel.CollectionType} == "java.util.Map")
+    protected $importUtils.formatJavaType($rel.CollectionType)<$importUtils.formatJavaType($entityUtils.getMapKeyType($rel)), $importUtils.formatJavaType($rel.TargetEntity.ClientClassName)> ${rel.Name};
+#else
+    protected $importUtils.formatJavaType($rel.CollectionType)<$importUtils.formatJavaType($rel.TargetEntity.ClientClassName)> ${rel.Name};
+#end
+#else
+    protected ValueHolder ${rel.Name};
+#end
+#end
+## Create attribute set/get methods
+#foreach( $attr in ${object.DeclaredAttributes} )
+#if ( $importUtils.isBoolean(${attr.Type}) )
+
+    public boolean is${stringUtils.capitalized($attr.Name)}() {
+        if(objectContext != null) {
+            objectContext.prepareForAccess(this, "${attr.Name}", false);
+        }
+
+        return ${attr.Name};
+    }
+#else
+
+    public $importUtils.formatJavaType(${attr.Type}) get${stringUtils.capitalized($attr.Name)}() {
+        if(objectContext != null) {
+            objectContext.prepareForAccess(this, "${attr.Name}", false);
+        }
+
+        return ${attr.Name};
+    }
+#end
+#if ("true" != "${object.isReadOnly()}")
+
+    public void set${stringUtils.capitalized($attr.Name)}($importUtils.formatJavaType(${attr.Type}) $stringUtils.formatVariableName(${attr.Name})) {
+        if(objectContext != null) {
+            objectContext.prepareForAccess(this, "${attr.Name}", false);
+            objectContext.propertyChanged(this, "${attr.Name}", this.${stringUtils.formatVariableName($attr.Name)}, $stringUtils.formatVariableName(${attr.Name}));
+        }
+        
+        this.${stringUtils.formatVariableName($attr.Name)} = ${stringUtils.formatVariableName($attr.Name)};
+    }
+#end
+#end
+##
+##
+## Create list add/remove/get methods
+#foreach( $rel in ${object.DeclaredRelationships} )
+#if( $rel.ToMany )
+#if ( ${rel.CollectionType} == "java.util.Map")
+
+    public $importUtils.formatJavaType($rel.CollectionType)<$importUtils.formatJavaType($entityUtils.getMapKeyType($rel)), $importUtils.formatJavaType($rel.TargetEntity.ClientClassName)> get${stringUtils.capitalized($rel.Name)}() {
+#else
+
+    public $importUtils.formatJavaType($rel.CollectionType)<$importUtils.formatJavaType($rel.TargetEntity.ClientClassName)> get${stringUtils.capitalized($rel.Name)}() {
+#end
+        if(objectContext != null) {
+            objectContext.prepareForAccess(this, "${rel.Name}", true);
+        } else if (this.$rel.Name == null) {
+#if ( ${rel.CollectionType} == "java.util.Map")
+        	throw new RuntimeException("Map relationships cannot be accessed for transient objects");
+#else
+        	this.$rel.Name = new PersistentObjectList(this, "${rel.Name}");
+#end
+		}
+
+        return ${rel.Name};
+    }
+#if ( ! $rel.ReadOnly )
+#if ( ${rel.CollectionType} == "java.util.Map")
+
+	public void addTo${stringUtils.capitalized($rel.Name)}($importUtils.formatJavaType(${rel.TargetEntity.ClientClassName}) object) {
+        if(objectContext != null) {
+            objectContext.prepareForAccess(this, "${rel.Name}", true);
+        } else if (this.$rel.Name == null) {
+        	throw new RuntimeException("Map relationships cannot be accessed for transient objects");        
+        }
+
+        this.${rel.Name}.put(getMapKey("${rel.Name}", object), object);
+    }
+
+    public void removeFrom${stringUtils.capitalized($rel.Name)}($importUtils.formatJavaType(${rel.TargetEntity.ClientClassName}) object) {
+        if(objectContext != null) {
+            objectContext.prepareForAccess(this, "${rel.Name}", true);
+        } else if (this.$rel.Name == null) {
+        	throw new RuntimeException("Map relationships cannot be accessed for transient objects");        
+        }
+
+        this.${rel.Name}.remove(getMapKey("${rel.Name}", object));
+    }
+#else
+
+    public void addTo${stringUtils.capitalized($rel.Name)}($importUtils.formatJavaType(${rel.TargetEntity.ClientClassName}) object) {
+        if(objectContext != null) {
+            objectContext.prepareForAccess(this, "${rel.Name}", true);
+        } else if (this.$rel.Name == null) {
+        	this.$rel.Name = new PersistentObjectList(this, "${rel.Name}");
+		}
+
+        this.${rel.Name}.add(object);
+    }
+
+    public void removeFrom${stringUtils.capitalized($rel.Name)}($importUtils.formatJavaType(${rel.TargetEntity.ClientClassName}) object) {
+        if(objectContext != null) {
+            objectContext.prepareForAccess(this, "${rel.Name}", true);
+        } else if (this.$rel.Name == null) {
+        	this.$rel.Name = new PersistentObjectList(this, "${rel.Name}");
+		}
+
+        this.${rel.Name}.remove(object);
+    }
+#end
+#end
+#else
+
+    public $importUtils.formatJavaType(${rel.TargetEntity.ClientClassName}) get${stringUtils.capitalized($rel.Name)}() {
+        if(objectContext != null) {
+            objectContext.prepareForAccess(this, "${rel.Name}", true);
+        } else if (this.$rel.Name == null) {
+        	this.$rel.Name = new PersistentObjectHolder(this, "$rel.Name");
+		}
+
+        return ($importUtils.formatJavaType(${rel.TargetEntity.ClientClassName})) ${rel.Name}.getValue();
+    }
+#if ( !${object.isReadOnly()} && !$rel.ReadOnly )
+
+    public void set${stringUtils.capitalized($rel.Name)}(${importUtils.formatJavaType($rel.TargetEntity.ClientClassName)} $stringUtils.formatVariableName(${rel.Name})) {
+        if(objectContext != null) {
+            objectContext.prepareForAccess(this, "${rel.Name}", true);
+            objectContext.propertyChanged(this, "$rel.Name", this.${rel.Name}.getValueDirectly(), $stringUtils.formatVariableName(${rel.Name}));
+        } else if (this.$rel.Name == null) {
+        	this.$rel.Name = new PersistentObjectHolder(this, "$rel.Name");
+		}
+        
+        this.${stringUtils.formatVariableName($rel.Name)}.setValue(${stringUtils.formatVariableName($rel.Name)});
+    }
+#end
+#end
+#end
+##callback methods
+#foreach( $cbname in ${entityUtils.callbackNames})
+
+    protected void ${cbname}() {
+        //TODO: Implement ${cbname}
+    }
+#end
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/60b8acc4/cayenne-cgen/src/main/resources/templates/v1_2/client-subclass.vm
----------------------------------------------------------------------
diff --git a/cayenne-cgen/src/main/resources/templates/v1_2/client-subclass.vm b/cayenne-cgen/src/main/resources/templates/v1_2/client-subclass.vm
index d28de85..9964a8e 100644
--- a/cayenne-cgen/src/main/resources/templates/v1_2/client-subclass.vm
+++ b/cayenne-cgen/src/main/resources/templates/v1_2/client-subclass.vm
@@ -44,7 +44,7 @@ ${importUtils.generate()}
  */
 public#if("true" == "${object.getIsAbstract()}") abstract#end class ${subClassName} extends ${superClassName} {
 
-     private static final long serialVersionUID = 1L; 
+    private static final long serialVersionUID = 1L;
      
 ##callback methods
 #foreach( $cbname in ${entityUtils.callbackNames})

http://git-wip-us.apache.org/repos/asf/cayenne/blob/60b8acc4/cayenne-cgen/src/main/resources/templates/v1_2/client-superclass.vm
----------------------------------------------------------------------
diff --git a/cayenne-cgen/src/main/resources/templates/v1_2/client-superclass.vm b/cayenne-cgen/src/main/resources/templates/v1_2/client-superclass.vm
index f8c9cbe..bec0fc5 100644
--- a/cayenne-cgen/src/main/resources/templates/v1_2/client-superclass.vm
+++ b/cayenne-cgen/src/main/resources/templates/v1_2/client-superclass.vm
@@ -89,7 +89,7 @@ public abstract class ${superClassName} extends ${baseClassName} {
     public static final Property<$type> ${stringUtils.capitalizedAsConstant($rel.Name)} = Property.create("${rel.Name}", ${stringUtils.stripGeneric($type)}.class);
 #end
 #else
-    #set( $type = "$importUtils.formatJavaType(${rel.TargetEntity.ClassName})" )
+    #set( $type = "$importUtils.formatJavaType(${rel.TargetEntity.ClientClassName})" )
     public static final Property<$type> ${stringUtils.capitalizedAsConstant($rel.Name)} = Property.create("${rel.Name}", ${stringUtils.stripGeneric($type)}.class);
 #end
 #end
@@ -109,10 +109,10 @@ public abstract class ${superClassName} extends ${baseClassName} {
     protected ValueHolder ${rel.Name};
 #end
 #end
-
 ## Create attribute set/get methods
 #foreach( $attr in ${object.DeclaredAttributes} )
 #if ( $importUtils.isBoolean(${attr.Type}) )
+
     public boolean is${stringUtils.capitalized($attr.Name)}() {
         if(objectContext != null) {
             objectContext.prepareForAccess(this, "${attr.Name}", false);
@@ -120,7 +120,8 @@ public abstract class ${superClassName} extends ${baseClassName} {
 
         return ${attr.Name};
     }
-#else 
+#else
+
     public $importUtils.formatJavaType(${attr.Type}) get${stringUtils.capitalized($attr.Name)}() {
         if(objectContext != null) {
             objectContext.prepareForAccess(this, "${attr.Name}", false);
@@ -130,21 +131,16 @@ public abstract class ${superClassName} extends ${baseClassName} {
     }
 #end
 #if ("true" != "${object.isReadOnly()}")
+
     public void set${stringUtils.capitalized($attr.Name)}($importUtils.formatJavaType(${attr.Type}) $stringUtils.formatVariableName(${attr.Name})) {
         if(objectContext != null) {
             objectContext.prepareForAccess(this, "${attr.Name}", false);
-        }
-
-        Object oldValue = this.${stringUtils.formatVariableName($attr.Name)};
-        // notify objectContext about simple property change
-        if(objectContext != null) {
-            objectContext.propertyChanged(this, "${attr.Name}", oldValue, $stringUtils.formatVariableName(${attr.Name}));
+            objectContext.propertyChanged(this, "${attr.Name}", this.${stringUtils.formatVariableName($attr.Name)}, $stringUtils.formatVariableName(${attr.Name}));
         }
         
         this.${stringUtils.formatVariableName($attr.Name)} = ${stringUtils.formatVariableName($attr.Name)};
     }
 #end
-
 #end
 ##
 ##
@@ -152,8 +148,10 @@ public abstract class ${superClassName} extends ${baseClassName} {
 #foreach( $rel in ${object.DeclaredRelationships} )
 #if( $rel.ToMany )
 #if ( ${rel.CollectionType} == "java.util.Map")
+
     public $importUtils.formatJavaType($rel.CollectionType)<$importUtils.formatJavaType($entityUtils.getMapKeyType($rel)), $importUtils.formatJavaType($rel.TargetEntity.ClientClassName)> get${stringUtils.capitalized($rel.Name)}() {
 #else
+
     public $importUtils.formatJavaType($rel.CollectionType)<$importUtils.formatJavaType($rel.TargetEntity.ClientClassName)> get${stringUtils.capitalized($rel.Name)}() {
 #end
         if(objectContext != null) {
@@ -170,6 +168,7 @@ public abstract class ${superClassName} extends ${baseClassName} {
     }
 #if ( ! $rel.ReadOnly )
 #if ( ${rel.CollectionType} == "java.util.Map")
+
 	public void addTo${stringUtils.capitalized($rel.Name)}($importUtils.formatJavaType(${rel.TargetEntity.ClientClassName}) object) {
         if(objectContext != null) {
             objectContext.prepareForAccess(this, "${rel.Name}", true);
@@ -179,6 +178,7 @@ public abstract class ${superClassName} extends ${baseClassName} {
 
         this.${rel.Name}.put(getMapKey("${rel.Name}", object), object);
     }
+
     public void removeFrom${stringUtils.capitalized($rel.Name)}($importUtils.formatJavaType(${rel.TargetEntity.ClientClassName}) object) {
         if(objectContext != null) {
             objectContext.prepareForAccess(this, "${rel.Name}", true);
@@ -189,6 +189,7 @@ public abstract class ${superClassName} extends ${baseClassName} {
         this.${rel.Name}.remove(getMapKey("${rel.Name}", object));
     }
 #else
+
     public void addTo${stringUtils.capitalized($rel.Name)}($importUtils.formatJavaType(${rel.TargetEntity.ClientClassName}) object) {
         if(objectContext != null) {
             objectContext.prepareForAccess(this, "${rel.Name}", true);
@@ -198,6 +199,7 @@ public abstract class ${superClassName} extends ${baseClassName} {
 
         this.${rel.Name}.add(object);
     }
+
     public void removeFrom${stringUtils.capitalized($rel.Name)}($importUtils.formatJavaType(${rel.TargetEntity.ClientClassName}) object) {
         if(objectContext != null) {
             objectContext.prepareForAccess(this, "${rel.Name}", true);
@@ -210,6 +212,7 @@ public abstract class ${superClassName} extends ${baseClassName} {
 #end
 #end
 #else
+
     public $importUtils.formatJavaType(${rel.TargetEntity.ClientClassName}) get${stringUtils.capitalized($rel.Name)}() {
         if(objectContext != null) {
             objectContext.prepareForAccess(this, "${rel.Name}", true);
@@ -220,29 +223,23 @@ public abstract class ${superClassName} extends ${baseClassName} {
         return ($importUtils.formatJavaType(${rel.TargetEntity.ClientClassName})) ${rel.Name}.getValue();
     }
 #if ( !${object.isReadOnly()} && !$rel.ReadOnly )
+
     public void set${stringUtils.capitalized($rel.Name)}(${importUtils.formatJavaType($rel.TargetEntity.ClientClassName)} $stringUtils.formatVariableName(${rel.Name})) {
         if(objectContext != null) {
             objectContext.prepareForAccess(this, "${rel.Name}", true);
+            objectContext.propertyChanged(this, "$rel.Name", this.${rel.Name}.getValueDirectly(), $stringUtils.formatVariableName(${rel.Name}));
         } else if (this.$rel.Name == null) {
         	this.$rel.Name = new PersistentObjectHolder(this, "$rel.Name");
 		}
-
-        // note how we notify ObjectContext of change BEFORE the object is actually
-        // changed... this is needed to take a valid current snapshot
-        Object oldValue = this.${rel.Name}.getValueDirectly();
-        if (objectContext != null) {
-        	objectContext.propertyChanged(this, "$rel.Name", oldValue, $stringUtils.formatVariableName(${rel.Name}));
-        }
         
         this.${stringUtils.formatVariableName($rel.Name)}.setValue(${stringUtils.formatVariableName($rel.Name)});
     }
 #end
 #end
-
 #end
 ##callback methods
 #foreach( $cbname in ${entityUtils.callbackNames})
-    protected abstract void ${cbname}();
 
+    protected abstract void ${cbname}();
 #end
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/60b8acc4/cayenne-cgen/src/main/resources/templates/v1_2/datamap-singleclass.vm
----------------------------------------------------------------------
diff --git a/cayenne-cgen/src/main/resources/templates/v1_2/datamap-singleclass.vm b/cayenne-cgen/src/main/resources/templates/v1_2/datamap-singleclass.vm
index 52fcf17..d0b7a7b 100644
--- a/cayenne-cgen/src/main/resources/templates/v1_2/datamap-singleclass.vm
+++ b/cayenne-cgen/src/main/resources/templates/v1_2/datamap-singleclass.vm
@@ -36,15 +36,19 @@
 ${importUtils.setPackage($subPackageName)}##
 ${importUtils.addReservedType("${subPackageName}.${subClassName}")}##
 ${importUtils.addType("${basePackageName}.${baseClassName}")}##
-${importUtils.addType('java.util.List')}##
 ${importUtils.addType('org.apache.cayenne.ObjectContext')}##
+#if( ${object.hasSelectQueries()} )
+${importUtils.addType('java.util.List')}##
 ${importUtils.addType('org.apache.cayenne.query.MappedSelect')}##
 #foreach( $selectQuery in ${object.SelectQueries})
 ${importUtils.addType(${selectQuery.Root.ClassName})}##
+#if(${dataMapUtils.isValidParameterNames($selectQuery)})
 #foreach( $parameter in ${dataMapUtils.getParameterNames(${selectQuery})})
 ${importUtils.addType(${dataMapUtils.getParameterType(${selectQuery}, ${parameter})})}##
 #end##
 #end##
+#end##
+#end##
 #if($object.hasExecQueries())##
 ${importUtils.addType('java.util.Map')}##
 ${importUtils.addType('org.apache.cayenne.QueryResult')}##
@@ -76,10 +80,10 @@ public class ${subClassName} {
         }
         return instance;
     }
-
 #foreach( $selectQuery in ${object.SelectQueries})
 #if(${dataMapUtils.isValidParameterNames($selectQuery)})
-    #set($rootClass = ${stringUtils.stripPackageName($selectQuery.Root.ClassName)})
+#set($rootClass = ${stringUtils.stripPackageName($selectQuery.Root.ClassName)})
+
     public List<$rootClass> perform${dataMapUtils.getQueryMethodName(${selectQuery})}(ObjectContext context#foreach( $parameter in ${dataMapUtils.getParameterNames(${selectQuery})}), ${stringUtils.stripPackageName(${dataMapUtils.getParameterType(${selectQuery}, ${parameter})})} ${parameter}#end) {
         MappedSelect<${rootClass}> query = MappedSelect.query(${stringUtils.capitalizedAsConstant(${selectQuery.Name})}_QUERYNAME, ${rootClass}.class);
 #foreach( $parameter in ${dataMapUtils.getParameterNames(${selectQuery})})
@@ -87,14 +91,14 @@ public class ${subClassName} {
 #end
         return query.select(context);
     }
-
 #end
 #end
 #foreach( $execQuery in ${object.ExecQueries})
+
     public QueryResult<?> perform${dataMapUtils.getQueryMethodName(${execQuery})}(ObjectContext context, Map<String, ?> parameters) {
         MappedExec query = MappedExec.query(${stringUtils.capitalizedAsConstant(${execQuery.Name})}_QUERYNAME).params(parameters);
         return query.execute(context);
     }
-
 #end
+
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/60b8acc4/cayenne-cgen/src/main/resources/templates/v1_2/datamap-superclass.vm
----------------------------------------------------------------------
diff --git a/cayenne-cgen/src/main/resources/templates/v1_2/datamap-superclass.vm b/cayenne-cgen/src/main/resources/templates/v1_2/datamap-superclass.vm
index bea1a56..0054524 100644
--- a/cayenne-cgen/src/main/resources/templates/v1_2/datamap-superclass.vm
+++ b/cayenne-cgen/src/main/resources/templates/v1_2/datamap-superclass.vm
@@ -34,9 +34,9 @@
 ${importUtils.setPackage($superPackageName)}##
 #if(${superPackageName})${importUtils.addReservedType("${superPackageName}.${superClassName}")}#end##
 #if(${basePackageName})${importUtils.addType("${basePackageName}.${baseClassName}")}#end##
+${importUtils.addType('org.apache.cayenne.ObjectContext')}##
 #if( ${object.hasSelectQueries()} )
 ${importUtils.addType('java.util.List')}##
-${importUtils.addType('org.apache.cayenne.ObjectContext')}##
 ${importUtils.addType('org.apache.cayenne.query.MappedSelect')}##
 #foreach( $selectQuery in ${object.SelectQueries})
 ${importUtils.addType(${selectQuery.Root.ClassName})}##
@@ -68,9 +68,9 @@ public class ${superClassName} {
 #end
 #end
 #foreach( $selectQuery in ${object.SelectQueries})
-
 #if(${dataMapUtils.isValidParameterNames($selectQuery)})
-    #set($rootClass = ${stringUtils.stripPackageName($selectQuery.Root.ClassName)})
+#set($rootClass = ${stringUtils.stripPackageName($selectQuery.Root.ClassName)})
+
     public List<$rootClass> perform${dataMapUtils.getQueryMethodName(${selectQuery})}(ObjectContext context#foreach( $parameter in ${dataMapUtils.getParameterNames(${selectQuery})}), ${stringUtils.stripPackageName(${dataMapUtils.getParameterType(${selectQuery}, ${parameter})})} ${parameter}#end) {
         MappedSelect<${rootClass}> query = MappedSelect.query(${stringUtils.capitalizedAsConstant(${selectQuery.Name})}_QUERYNAME, ${rootClass}.class);
 #foreach( $parameter in ${dataMapUtils.getParameterNames(${selectQuery})})
@@ -78,14 +78,14 @@ public class ${superClassName} {
 #end
         return query.select(context);
     }
-
 #end
 #end
 #foreach( $execQuery in ${object.ExecQueries})
+
     public QueryResult<?> perform${dataMapUtils.getQueryMethodName(${execQuery})}(ObjectContext context, Map<String, ?> parameters) {
         MappedExec query = MappedExec.query(${stringUtils.capitalizedAsConstant(${execQuery.Name})}_QUERYNAME).params(parameters);
         return query.execute(context);
     }
-
 #end
+
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/60b8acc4/docs/doc/src/main/resources/RELEASE-NOTES.txt
----------------------------------------------------------------------
diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt b/docs/doc/src/main/resources/RELEASE-NOTES.txt
index 4192d30..9674ad9 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -21,6 +21,7 @@ CAY-2312 Modeler: Undo does not work for checkboxes
 CAY-2318 Modeler: Query. Exception after Undo clicking
 CAY-2319 Modeler: Embeddable > Attributes. Undo does not cancel pasted objects
 CAY-2323 Modeler: Graph. No warning while saving the image with existing name
+CAY-2331 cgen: broken templates for data map
 
 ----------------------------------
 Release: 4.0.B1