Product SiteDocumentation Site

Chapter 7. Controlling POJO code generation

7.1. The <meta> attribute
7.1.1. Recommendations
7.1.2. Advanced <meta> attribute examples
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 with the <meta> tag in the mapping files. The following section describes the possible <meta> tags and their use.

7.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 7.1. Supported meta tags
Attribute Description
class-description inserted into the javadoc for classes
field-description inserted into the javadoc for fields/properties
interface If true, an interface is generated instead of an class.
implements interface the class should implement
extends class that the current class should extend (ignored for subclasses)
generated-class overrule the name of the actual class generated
scope-class scope for class
scope-set scope for setter method
scope-get scope for getter method
scope-field scope for actual field
default-value default initialization value for a field
use-in-tostring include this property in the toString()
use-in-equals include this property in the equals() and hashCode() method. If no use-in-equals is specified, no equals/hashcode will be generated.
gen-property property will not be generated if false (use with care)
property-type Overrides the default type of property. Use this with any tag's to specify the concrete type instead of just Object.
class-code Extra code that will inserted at the end of the class
extra-import Extra 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.

7.1.1. Recommendations

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

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

7.1.1.2. Be aware of putting at class scope level <meta> attributeuse-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 of 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.
On www.hibernate.org you can get more in-depth explanation on the subject of equals() and hashcode().

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

7.1.2.1. Generate pre/post-conditions for methods

With a <meta attribute="class-code">, you can add additional methods on a given class, nevertheless such <meta> attribute can not be used at a property scope level and Hibernate Tools does not provide such ><meta> attributes.
A possible solution for this is to modify the freemarker templates responsible 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 name 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 example let us add a pre-condition for property name preventing no Person can have an empty name. Hence 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">
      if ((firstName != null) && (firstName.length() == 0) ) {
        throw new IllegalArgumentException("firstName can not be an empty String");
      }
      </meta>
  </property>
</class>
</hibernate-mapping>

Note:

I) To escape the & symbol we put &. You can use <![CDATA[]]> instead.
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;
    }
    }
In conclusion, this document is intended to introduce you to Hibernate plugin specific features related to tools bath for the Eclipse and Ant tasks.
In the Eclipse Plugins chapter you've learnt about a set of wizards for creating Mapping files, Configuration file, Console Configuration, got familiar with Mapping and Configuration files editors, tooling for organizing and controlling Reverse Engineering, Hibernate Console and Mapping diagram as well.
The rest chapters have shown the aspects of using the Hibernate Tools via Ant tasks.
Please, visit JBoss Tools Users Forum to leave questions or/and suggestions on the topic. Your feedback is always appreciated.