From hibernate-commits at lists.jboss.org Thu Oct 19 11:50:41 2006 Content-Type: multipart/mixed; boundary="===============6915762325712960006==" MIME-Version: 1.0 From: hibernate-commits at lists.jboss.org To: hibernate-commits at lists.jboss.org Subject: [hibernate-commits] Hibernate SVN: r10620 - trunk/HibernateExt/tools/doc/reference/en/modules Date: Thu, 19 Oct 2006 11:50:41 -0400 Message-ID: --===============6915762325712960006== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Author: max.andersen(a)jboss.com Date: 2006-10-19 11:50:37 -0400 (Thu, 19 Oct 2006) New Revision: 10620 Modified: trunk/HibernateExt/tools/doc/reference/en/modules/ant.xml trunk/HibernateExt/tools/doc/reference/en/modules/codegen.xml Log: HBX-769 Improved Reference Manual Documentation (meta attribute advanced e= xamples) + various fixes to doc Modified: trunk/HibernateExt/tools/doc/reference/en/modules/ant.xml =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/HibernateExt/tools/doc/reference/en/modules/ant.xml 2006-10-19 14= :15:34 UTC (rev 10619) +++ trunk/HibernateExt/tools/doc/reference/en/modules/ant.xml 2006-10-19 15= :50:37 UTC (rev 10620) @@ -22,8 +22,9 @@ e.g. Hibernate 3.2 jar's with e.g. an Hibernate 3.1 project since the output generated will work with previous Hibernate 3 versions. = - -
+
+ = +
The <literal><hibernatetool></literal> ant Task = To use the ant tasks you need to have the hibernatetool task @@ -825,5 +826,4 @@
- Modified: trunk/HibernateExt/tools/doc/reference/en/modules/codegen.xml =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/HibernateExt/tools/doc/reference/en/modules/codegen.xml 2006-10-1= 9 14:15:34 UTC (rev 10619) +++ trunk/HibernateExt/tools/doc/reference/en/modules/codegen.xml 2006-10-1= 9 15:50:37 UTC (rev 10620) @@ -1,4 +1,4 @@ - + Controlling POJO code generation = @@ -24,26 +24,26 @@ The following example shows how to use various <meta> attributes and the resulting java code. = - - + <class name=3D"Person"> + <meta attribute=3D"class-description"> Javadoc for the Person class @author Frodo - - IAuditable - - protected - - - - The name of the person - -]]> + </meta> + <meta attribute=3D"implements">IAuditable</meta> + <id name=3D"id" type=3D"long"> + <meta attribute=3D"scope-set">protected</meta> + <generator class=3D"increment"/> + </id> + <property name=3D"name" type=3D"string"> + <meta attribute=3D"field-description">The name of the person= </meta> + </property> +</class> = The above hbm.xml will produce something like the following (code shortened for better understanding). Notice the Javadoc comment and the protected set methods: = - // default package = import java.io.Serializable; import org.apache.commons.lang.builder.EqualsBuilder; @@ -86,7 +86,7 @@ this.name =3D name; } = -}]]> +} = Supported meta tags @@ -244,5 +244,294 @@ attribute, e.g. <meta attribute=3D"scope-class" inherit=3D"false">public abstract</meta> will restr= ict the "class-scope" to the current class, not the subclasses. + +
+ Recomendations + + The following are some good practices when using + <meta> attributes. + +
+ Dangers of a class level <literal>use-in-string and + use-in-equals</literal> meta attributes when having bi-directional + associations + + If we have two entities with a bi-directional association + between them and define at class scope level the meta attributes: + use-in-string, use-in-equals: + + <hibernate-mapping> + <class name=3D"Person"> + <meta attribute=3D"use-in-tostring">true</meta> + <meta attribute=3D"use-in-equals">true</meta> + ... + </class> +</hibernate-mapping> + + and for Event.hbm file: + + <hibernate-mapping> = + <class name=3D"events.Event" table=3D"EVENTS"> + <meta attribute=3D"use-in-tostring">true</meta> + <meta attribute=3D"use-in-equals">true</meta> = = + <id name=3D"id" column=3D"EVENT_ID"> + <generator class=3D"native"/> + </id> + <property name=3D"date" type=3D"timestamp" column=3D"EVENT_DATE"/&g= t; + <property name=3D"title"/> + <set name=3D"participants" table=3D"PERSON_EVENT" inverse=3D"true"&= gt; + <key column=3D"EVENT_ID"/> + <many-to-many column=3D"PERSON_ID" class=3D"events.Person"/> + </set> = + </class> +</hibernate-mapping> + + Then <hbm2java> will assume you wan= t to + include all properties and collections in the + toString()/equals() methods and this can result= in + infinite recursive calls. + + To remedy this you have to decide which side of the associat= ion + will include the other part (if at all) in the + toString()/equals() methods. Therefore it is no= t a + good practice to put at class scope such meta attributes, unless y= ou + are defining a class without bi-directional associations + + We recomend instead to add the meta + attributes at the property level: + + <hibernate-mapping> = + <class name=3D"events.Event" table=3D"EVENTS"> = + <id name=3D"id" column=3D"EVENT_ID"> + <meta attribute=3D"use-in-tostring">true</meta> + <generator class=3D"native"/> + </id> + <property name=3D"date" type=3D"timestamp" column=3D"EVENT_DATE"/&g= t; + <property name=3D"title"> + <meta attribute=3D"use-in-tostring">true</meta> + <meta attribute=3D"use-in-equals">true</meta> = + </property> + <set name=3D"participants" table=3D"PERSON_EVENT" inverse=3D"true"&= gt; + <key column=3D"EVENT_ID"/> + <many-to-many column=3D"PERSON_ID" class=3D"events.Person"/> + </set> = + </class> +</hibernate-mapping> + + and now for Person: + + <hibernate-mapping> + <class name=3D"Person"> + <meta attribute=3D"class-description"> + Javadoc for the Person class + @author Frodo + </meta> + <meta attribute=3D"implements">IAuditable</meta> + <id name=3D"id" type=3D"long"> + <meta attribute=3D"scope-set">protected</meta> + <meta attribute=3D"use-in-tostring">true</meta> = + <generator class=3D"increment"/> + </id> + <property name=3D"name" type=3D"string"> + <meta attribute=3D"field-description">The name of the person= </meta> + <meta attribute=3D"use-in-tostring">true</meta> + </property> + </class> +</hibernate-mapping> +
+ +
+ Be aware of putting at class scope level + <literal><meta></literal> attribute + <literal>use-in-equals</literal> + + For equal()/hashCode() method generation,= you + have to take into account that the attributes that participate on = such + method definition, should take into account only attributes with + business meaning (the name, social security number, etc, but no + generated id's, for example). + + This is important because Java's hashbased collections, such= as + java.util.Set relies on equals() and hashcode() to be correct and = not + change for objects in the set; this can be a problem if the id gets + assigned for an object after you inserted it into a set. + + Therefore automatically configuration the generation of + equals()/hashCode() methods specifying at class + scope level the <meta> attribute + use-in-equals could be a dangerous decision that + could produce non expected side-effect. + + See http://www.hibernate.org/109.html for an more in-depth + explanation on the subject of equals() and hashcode(). +
+
+ +
+ Advanced <literal><meta></literal> attribute + examples + + This section shows an example for using meta attributes (inclu= ding + userspecific attributes) together with the code generation features = in + Hibernate Tools. + + The usecase being implemented is to automatically insert some = pre- + and post-conditions into the getter and setters of the generated POJ= O. + + +
+ Generate pre/post-conditions for methods + + With an <meta attribute=3D"class-code">, + you can add addional methods on a given class, nevertheless such + <meta> attribute can not be used at prope= rty + scope level and Hibernatetools does not provide such + <meta> attributes. + + A possibly solution for this is to modify the freemarker + templates responsable for generating the POJO's. If you look inside + hibernate-tools.jar, you can find the template: + pojo/PojoPropertyAccessor.ftl + + This file is as the named indicates used to generate property + accessors for pojo's. + + Extract the PojoPropertyAccessor.ftl into= a + local folder i.e. ${hbm.template.path}, respect= ing + the whole path, for example: + ${hbm.template.path}/pojo/PojoPropertyAccessor.ftl + + The contents of the file is something like this: + + <#foreach property in pojo.getAllPropertiesIter= ator()> + ${pojo.getPropertyGetModifiers(property)} ${pojo.getJavaTypeName(prope= rty, jdk5)} ${pojo.getGetterSignature(property)}() { + return this.${property.name}; + } + = + ${pojo.getPropertySetModifiers(property)} void set${pojo.getPropertyNa= me(property)}(${pojo.getJavaTypeName(property, jdk5)} ${property.name}) { + this.${property.name} =3D ${property.name}; + } +</#foreach> + + We can add conditionally pre/post-conditions on our + set method generation just adding a little + Freemarker syntax to the above source code: + + <#foreach property in pojo.getAllPropertiesIter= ator()> + ${pojo.getPropertyGetModifiers(property)} ${pojo.getJavaTypeName(prope= rty, jdk5)} ${pojo.getGetterSignature(property)}() { + return this.${property.name}; + } + = + ${pojo.getPropertySetModifiers(property)} void set${pojo.getPropertyNa= me(property)}(${pojo.getJavaTypeName(property, jdk5)} ${property.name}) { + <#if pojo.hasMetaAttribute(property, "pre-cond")> = + ${c2j.getMetaAsString(property, "pre-cond","\n")} = + </#if> = + this.${property.name} =3D ${property.name}; + <#if pojo.hasMetaAttribute(property, "post-cond")> = + ${c2j.getMetaAsString(property, "post-cond","\n")} = + </#if> = +} +</#foreach> + + + Now if in any *hbm.xml file we define the + <meta> attributes: + pre-cond or post-cond, their + contents will be generated into the body of the relevant + set method. + + As an examlpe let us add a pre-condition for property + name preventing no Person can + have an empty name. So we have to modify the + Person.hbm.xml file like this: + + <hibernate-mapping> + <class name=3D"Person"> + <id name=3D"id" type=3D"long"> = + <generator class=3D"increment"/> + </id> + <property name=3D"firstName" type=3D"string"> + <meta attribute=3D"pre-cond"><![CDATA[ + if ((firstName !=3D null) && (firstName.length() =3D=3D 0) )= { + throw new IllegalArgumentException("firstName can not be an empty = String"); + }]]> + </meta> + </property> +</class> +</hibernate-mapping> + + Notes: i) If you don't use <[[CDATA[]]> + you have to scape the & symbol, i.e.: &amp; ii). Note that= we + are referring to "firstName" directly and this is the parameter na= me + not the actual field name. If you want to refer the field you have= to + use "this.firstName" instead. + + Finally we have to generate the Person.java + class, for this we can use both Eclipse and Ant as long as you + remember to set or fill in the templatepath setting. For Ant we + configure <hibernatetool> task via + the templatepath attribute as in: + + + <target name=3D"hbm2java"> + <taskdef name=3D"hibernatetool" + classname=3D"org.hibernate.tool.ant.HibernateToolTask" + classpathref=3D"lib.classpath"/> + <hibernatetool destdir=3D"${hbm2java.dest.dir}" + templatepath=3D"${hbm.template.path}"> + <classpath> + <path refid=3D"pojo.classpath"/> + </classpath> = + <configuration> + <fileset dir=3D"${hbm2java.src.dir}"> + <include name=3D"**/*.hbm.xml"/> + </fileset> + </configuration> + <hbm2java/> + </hibernatetool> + </target> + + Invoking the target <hbm2java> will + generate on the ${hbm2java.dest.dir} the file: + Person.java: + + // default package +import java.io.Serializable; +public class Person implements Serializable { + + public Long id; + + public String name; + + public Person(java.lang.String name) { + this.name =3D name; + } + + public Person() { + } + + public java.lang.Long getId() { + return this.id; + } + + public void setId(java.lang.Long id) { + this.id =3D id; + } + = + public java.lang.String getName() { + return this.name; + } + + public void setName(java.lang.String name) { + if ((name !=3D null) && (name.length() =3D=3D 0)) { + throw new IllegalArgumentException("name can not be an empty S= tring"); + } + this.name =3D name; + } +} + + +
+
\ No newline at end of file --===============6915762325712960006==--