Chapter 6. Controlling POJO code generation

When using <hbm2java> or the eclipse plugin to generate POJO java code you have the possibility to control certain aspects of the code generation. This is primarily done through the <meta> tag in the mapping files. The following section describes the possible meta tags and their use.

6.1. The <meta> attribute

The <meta> tag is a simple way of annotating the hbm.xml with information, so tools have a natural place to store/read information that is not directly related to the Hibernate core.

You can use the <meta> tag to e.g. tell hbm2java to only generate "protected" setters, have classes always implement a certain set of interfaces or even have them extend a certain base class and even more.

The following example shows how to use various <meta> attributes and the resulting java code.

<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>

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;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

/** 
 *         Javadoc for the Person class
 *         @author Frodo
 */
public class Person implements Serializable, IAuditable {

    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;
    }

    protected void setId(java.lang.Long id) {
        this.id = id;
    }

    /** 
     * The name of the person
     */
    public java.lang.String getName() {
        return this.name;
    }

    public void setName(java.lang.String name) {
        this.name = name;
    }

}

Table 6.1. Supported meta tags

AttributeDescription
class-descriptioninserted into the javadoc for classes
field-descriptioninserted into the javadoc for fields/properties
interfaceIf true an interface is generated instead of an class.
implementsinterface the class should implement
extendsclass the class should extend (ignored for subclasses)
generated-classoverrule the name of the actual class generated
scope-classscope for class
scope-setscope for setter method
scope-getscope for getter method
scope-fieldscope for actual field
default-valuedefault initializatioin value for a field
use-in-tostringinclude this property in the toString()
use-in-equalsinclude this property in the equals() and hashCode() method. If no use-in-equals is specificed, no equals/hashcode will be generated.
gen-propertyproperty will not be generated if false (use with care)
property-typeOverrides the default type of property. Use this with any tag's to specify the concrete type instead of just Object.
class-codeExtra code that will inserted at the end of the class
extra-importExtra import that will inserted at the end of all other imports

Attributes declared via the <meta> tag are per default "inherited" inside an hbm.xml file.

What does that mean? It means that if you e.g want to have all your classes implement IAuditable then you just add an <meta attribute="implements">IAuditable</meta> in the top of the hbm.xml file, just after <hibernate-mapping>. Now all classes defined in that hbm.xml file will implement IAuditable!

Note: This applies to all <meta>-tags. Thus it can also e.g. be used to specify that all fields should be declare protected, instead of the default private. This is done by adding <meta attribute="scope-field">protected</meta> at e.g. just under the <class> tag and all fields of that class will be protected.

To avoid having a <meta>-tag inherited then you can simply specify inherit="false" for the attribute, e.g. <meta attribute="scope-class" inherit="false">public abstract</meta> will restrict the "class-scope" to the current class, not the subclasses.

6.1.1. Recomendations

The following are some good practices when using <meta> attributes.

6.1.1.1. Dangers of a class level use-in-string and use-in-equals 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="Person">
    <meta attribute="use-in-tostring">true</meta>
    <meta attribute="use-in-equals">true</meta>
    ...
  </class>
</hibernate-mapping>

and for Event.hbm file:

<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>

Then <hbm2java> will assume you want 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 association will include the other part (if at all) in the toString()/equals() 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

We recomend instead to add the meta attributes at the property level:

<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>

and now for Person:

<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>

6.1.1.2. Be aware of putting at class scope level <meta> attribute use-in-equals

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().

6.1.2. Advanced <meta> attribute examples

This section shows an example for using meta attributes (including 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 POJO.

6.1.2.1. Generate pre/post-conditions for methods

With an <meta attribute="class-code">, you can add addional methods on a given class, nevertheless such <meta> attribute can not be used at property 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}, respecting the whole path, for example: ${hbm.template.path}/pojo/PojoPropertyAccessor.ftl

The contents of the file is something like this:

<#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>

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.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>

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="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>

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 name 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="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>

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 = 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;
    }
}