[hibernate-commits] Hibernate SVN: r10620 - trunk/HibernateExt/tools/doc/reference/en/modules
hibernate-commits at lists.jboss.org
hibernate-commits at lists.jboss.org
Thu Oct 19 11:50:41 EDT 2006
Author: max.andersen at 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 examples)
+ various fixes to doc
Modified: trunk/HibernateExt/tools/doc/reference/en/modules/ant.xml
===================================================================
--- 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.
</para>
-
- <section>
+ </section>
+
+ <section>
<title>The <literal><hibernatetool></literal> ant Task</title>
<para>To use the ant tasks you need to have the hibernatetool task
@@ -825,5 +826,4 @@
</section>
</section>
</section>
- </section>
</chapter>
Modified: trunk/HibernateExt/tools/doc/reference/en/modules/codegen.xml
===================================================================
--- trunk/HibernateExt/tools/doc/reference/en/modules/codegen.xml 2006-10-19 14:15:34 UTC (rev 10619)
+++ trunk/HibernateExt/tools/doc/reference/en/modules/codegen.xml 2006-10-19 15:50:37 UTC (rev 10620)
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<chapter id="codegen">
<title>Controlling POJO code generation</title>
@@ -24,26 +24,26 @@
<para>The following example shows how to use various <meta>
attributes and the resulting java code.</para>
- <programlisting><![CDATA[<class name="Person">
- <meta attribute="class-description">
+ <programlisting><class name="Person">
+ <meta attribute="class-description">
Javadoc for the Person class
@author Frodo
- </meta>
- <meta attribute="implements">IAuditable</meta>
- <id name="id" type="long">
- <meta attribute="scope-set">protected</meta>
- <generator class="increment"/>
- </id>
- <property name="name" type="string">
- <meta attribute="field-description">The name of the person</meta>
- </property>
-</class>]]></programlisting>
+ </meta>
+ <meta attribute="implements">IAuditable</meta>
+ <id name="id" type="long">
+ <meta attribute="scope-set">protected</meta>
+ <generator class="increment"/>
+ </id>
+ <property name="name" type="string">
+ <meta attribute="field-description">The name of the person</meta>
+ </property>
+</class></programlisting>
<para>The above hbm.xml will produce something like the following (code
shortened for better understanding). Notice the Javadoc comment and the
protected set methods:</para>
- <programlisting><![CDATA[// default package
+ <programlisting>// default package
import java.io.Serializable;
import org.apache.commons.lang.builder.EqualsBuilder;
@@ -86,7 +86,7 @@
this.name = name;
}
-}]]></programlisting>
+}</programlisting>
<table frame="topbot">
<title>Supported meta tags</title>
@@ -244,5 +244,294 @@
attribute, e.g. <literal><meta attribute="scope-class"
inherit="false">public abstract</meta></literal> will restrict
the "class-scope" to the current class, not the subclasses.</para>
+
+ <section>
+ <title>Recomendations</title>
+
+ <para>The following are some good practices when using
+ <literal><meta></literal> attributes.</para>
+
+ <section>
+ <title>Dangers of a class level <literal>use-in-string and
+ use-in-equals</literal> meta attributes when having bi-directional
+ associations</title>
+
+ <para>If we have two entities with a bi-directional association
+ between them and define at class scope level the meta attributes:
+ <literal>use-in-string, use-in-equals</literal>:</para>
+
+ <programlisting><hibernate-mapping>
+ <class name="Person">
+ <meta attribute="use-in-tostring">true</meta>
+ <meta attribute="use-in-equals">true</meta>
+ ...
+ </class>
+</hibernate-mapping></programlisting>
+
+ <para>and for <literal>Event.hbm</literal> file:</para>
+
+ <programlisting><hibernate-mapping>
+ <class name="events.Event" table="EVENTS">
+ <meta attribute="use-in-tostring">true</meta>
+ <meta attribute="use-in-equals">true</meta>
+ <id name="id" column="EVENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="date" type="timestamp" column="EVENT_DATE"/>
+ <property name="title"/>
+ <set name="participants" table="PERSON_EVENT" inverse="true">
+ <key column="EVENT_ID"/>
+ <many-to-many column="PERSON_ID" class="events.Person"/>
+ </set>
+ </class>
+</hibernate-mapping></programlisting>
+
+ <para>Then <literal><hbm2java></literal> will assume you want to
+ include all properties and collections in the
+ <literal>toString()/equals()</literal> methods and this can result in
+ infinite recursive calls.</para>
+
+ <para>To remedy this you have to decide which side of the association
+ will include the other part (if at all) in the
+ <literal>toString()/equals()</literal> methods. Therefore it is not a
+ good practice to put at class scope such meta attributes, unless you
+ are defining a class without bi-directional associations</para>
+
+ <para>We recomend instead to add the <literal>meta</literal>
+ attributes at the property level:</para>
+
+ <programlisting><hibernate-mapping>
+ <class name="events.Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <meta attribute="use-in-tostring">true</meta>
+ <generator class="native"/>
+ </id>
+ <property name="date" type="timestamp" column="EVENT_DATE"/>
+ <property name="title">
+ <meta attribute="use-in-tostring">true</meta>
+ <meta attribute="use-in-equals">true</meta>
+ </property>
+ <set name="participants" table="PERSON_EVENT" inverse="true">
+ <key column="EVENT_ID"/>
+ <many-to-many column="PERSON_ID" class="events.Person"/>
+ </set>
+ </class>
+</hibernate-mapping></programlisting>
+
+ <para>and now for <literal>Person</literal>:</para>
+
+ <programlisting><hibernate-mapping>
+ <class name="Person">
+ <meta attribute="class-description">
+ Javadoc for the Person class
+ @author Frodo
+ </meta>
+ <meta attribute="implements">IAuditable</meta>
+ <id name="id" type="long">
+ <meta attribute="scope-set">protected</meta>
+ <meta attribute="use-in-tostring">true</meta>
+ <generator class="increment"/>
+ </id>
+ <property name="name" type="string">
+ <meta attribute="field-description">The name of the person</meta>
+ <meta attribute="use-in-tostring">true</meta>
+ </property>
+ </class>
+</hibernate-mapping></programlisting>
+ </section>
+
+ <section>
+ <title>Be aware of putting at class scope level
+ <literal><meta></literal> attribute
+ <literal>use-in-equals</literal></title>
+
+ <para>For <literal>equal()/hashCode()</literal> 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).</para>
+
+ <para>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.</para>
+
+ <para>Therefore automatically configuration the generation of
+ <literal>equals()/hashCode()</literal> methods specifying at class
+ scope level the <literal><meta></literal> attribute
+ <literal>use-in-equals</literal> could be a dangerous decision that
+ could produce non expected side-effect.</para>
+
+ <para>See http://www.hibernate.org/109.html for an more in-depth
+ explanation on the subject of equals() and hashcode().</para>
+ </section>
+ </section>
+
+ <section>
+ <title>Advanced <literal><meta></literal> attribute
+ examples</title>
+
+ <para>This section shows an example for using meta attributes (including
+ userspecific attributes) together with the code generation features in
+ Hibernate Tools.</para>
+
+ <para>The usecase being implemented is to automatically insert some pre-
+ and post-conditions into the getter and setters of the generated POJO.
+ </para>
+
+ <section>
+ <title>Generate pre/post-conditions for methods</title>
+
+ <para>With an <literal><meta attribute="class-code"></literal>,
+ you can add addional methods on a given class, nevertheless such
+ <literal><meta></literal> attribute can not be used at property
+ scope level and Hibernatetools does not provide such
+ <literal><meta></literal> attributes.</para>
+
+ <para>A possibly solution for this is to modify the freemarker
+ templates responsable for generating the POJO's. If you look inside
+ <literal>hibernate-tools.jar</literal>, you can find the template:
+ <literal>pojo/PojoPropertyAccessor.ftl</literal></para>
+
+ <para>This file is as the named indicates used to generate property
+ accessors for pojo's.</para>
+
+ <para>Extract the <literal>PojoPropertyAccessor.ftl</literal> into a
+ local folder i.e. <literal>${hbm.template.path}</literal>, respecting
+ the whole path, for example:
+ <literal>${hbm.template.path}/pojo/PojoPropertyAccessor.ftl</literal></para>
+
+ <para>The contents of the file is something like this:</para>
+
+ <programlisting><#foreach property in pojo.getAllPropertiesIterator()>
+ ${pojo.getPropertyGetModifiers(property)} ${pojo.getJavaTypeName(property, jdk5)} ${pojo.getGetterSignature(property)}() {
+ return this.${property.name};
+ }
+
+ ${pojo.getPropertySetModifiers(property)} void set${pojo.getPropertyName(property)}(${pojo.getJavaTypeName(property, jdk5)} ${property.name}) {
+ this.${property.name} = ${property.name};
+ }
+</#foreach></programlisting>
+
+ <para>We can add conditionally pre/post-conditions on our
+ <literal>set</literal> method generation just adding a little
+ Freemarker syntax to the above source code:</para>
+
+ <programlisting><#foreach property in pojo.getAllPropertiesIterator()>
+ ${pojo.getPropertyGetModifiers(property)} ${pojo.getJavaTypeName(property, jdk5)} ${pojo.getGetterSignature(property)}() {
+ return this.${property.name};
+ }
+
+ ${pojo.getPropertySetModifiers(property)} void set${pojo.getPropertyName(property)}(${pojo.getJavaTypeName(property, jdk5)} ${property.name}) {
+ <#if pojo.hasMetaAttribute(property, "pre-cond")>
+ ${c2j.getMetaAsString(property, "pre-cond","\n")}
+ </#if>
+ this.${property.name} = ${property.name};
+ <#if pojo.hasMetaAttribute(property, "post-cond")>
+ ${c2j.getMetaAsString(property, "post-cond","\n")}
+ </#if>
+}
+</#foreach>
+</programlisting>
+
+ <para>Now if in any <literal>*hbm.xml</literal> file we define the
+ <literal><meta></literal> attributes:
+ <literal>pre-cond</literal> or <literal>post-cond</literal>, their
+ contents will be generated into the body of the relevant
+ <literal>set</literal> method.</para>
+
+ <para>As an examlpe let us add a pre-condition for property
+ <literal>name</literal> preventing no <literal>Person</literal> can
+ have an empty name. So we have to modify the
+ <literal>Person.hbm.xml</literal> file like this:</para>
+
+ <programlisting><hibernate-mapping>
+ <class name="Person">
+ <id name="id" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="firstName" type="string">
+ <meta attribute="pre-cond"><![CDATA[
+ if ((firstName != null) && (firstName.length() == 0) ) {
+ throw new IllegalArgumentException("firstName can not be an empty String");
+ }]]>
+ </meta>
+ </property>
+</class>
+</hibernate-mapping></programlisting>
+
+ <para>Notes: i) If you don't use <literal><[[CDATA[]]></literal>
+ you have to scape the & symbol, i.e.: &amp; ii). Note that we
+ are referring to "firstName" directly and this is the parameter name
+ not the actual field name. If you want to refer the field you have to
+ use "this.firstName" instead. </para>
+
+ <para>Finally we have to generate the <literal>Person.java</literal>
+ 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 <literal><hibernatetool></literal> task via
+ <literal>the templatepath</literal> attribute as in:</para>
+
+ <programlisting>
+ <target name="hbm2java">
+ <taskdef name="hibernatetool"
+ classname="org.hibernate.tool.ant.HibernateToolTask"
+ classpathref="lib.classpath"/>
+ <hibernatetool destdir="${hbm2java.dest.dir}"
+ templatepath="${hbm.template.path}">
+ <classpath>
+ <path refid="pojo.classpath"/>
+ </classpath>
+ <configuration>
+ <fileset dir="${hbm2java.src.dir}">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </configuration>
+ <hbm2java/>
+ </hibernatetool>
+ </target></programlisting>
+
+ <para>Invoking the target <literal><hbm2java></literal> will
+ generate on the <literal>${hbm2java.dest.dir}</literal> the file:
+ <literal>Person.java</literal>:</para>
+
+ <programlisting>// 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 = name;
+ }
+
+ public Person() {
+ }
+
+ public java.lang.Long getId() {
+ return this.id;
+ }
+
+ public void setId(java.lang.Long id) {
+ this.id = id;
+ }
+
+ public java.lang.String getName() {
+ return this.name;
+ }
+
+ public void setName(java.lang.String name) {
+ if ((name != null) && (name.length() == 0)) {
+ throw new IllegalArgumentException("name can not be an empty String");
+ }
+ this.name = name;
+ }
+}</programlisting>
+
+ <para></para>
+ </section>
+ </section>
</section>
</chapter>
\ No newline at end of file
More information about the hibernate-commits
mailing list