JBoss.orgCommunity Documentation

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.

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

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.

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

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>

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.

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) &amp;&amp; (firstName.length() == 0) ) {
        throw new IllegalArgumentException("firstName can not be an empty String");
      }
      </meta>
  </property>
</class>
</hibernate-mapping>

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) &amp;&amp; (name.length() == 0)) {
            throw new IllegalArgumentException("name can not be an empty String");
        }
        this.name = name;
    }
}

To find additional information about Hibernate Tools we suggest that you visit our website. If you have questions, you are always welcome in our forum.