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 the <hbm2java> tag or the Eclipse plugin to generate POJO Java code you have the ability to control certain aspects of the code generation process. 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 file with information, so tools have a natural place to store and read information that is not directly related to the Hibernate core.

As an example, you can use the <meta> tag to tell the <hbm2java> tag to only generate "protected" setters, have classes always implement a certain set of interfaces, have them extend a certain base class and 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 file will produce something like the following (the code has been abbreviated for clarity). 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 "inherited" inside an hbm.xml file by default.

What does that mean? As an example if you want to have all your classes implement IAuditable then you just add <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 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> just under the <class> tag, and all fields of that class will be protected.

To avoid having a <meta> tag inherited then you can specify inherit = "false" for the attribute. For example <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 to employ when using <meta> attributes.

In the following example we have two entities with a bi-directional association between them and define the use-in-string and use-in-equals meta attributes at the class scope level the meta attributes:

<hibernate-mapping>
  <class name="Person">
    <meta attribute="use-in-tostring">true</meta>
    <meta attribute="use-in-equals">true</meta>
    ...
  </class>
</hibernate-mapping>

Here is the 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>

In this situation the <hbm2java> tag will assume you want to include all properties and collections in the toString() and equals() methods. 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() and equals() methods. Therefore it is not a good practice to define these meta attributes at the class scope, unless you are defining a class without bi-directional associations.

Instead it is recommended that the meta attributes are defined at the property level, like so:

<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 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 user specific attributes) together with the code generation features in Hibernate Tools™.

The example shown below automatically inserts some pre and post conditions into the getter and setter methods of the generated POJO.

With <meta attribute="class-code"> you can add additional methods on a given class. However, such <meta> attributes 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 POJOs. If you look inside the hibernate-tools.jar archive, you can find the template pojo/PojoPropertyAccessor.ftl.

As its name indicates, this file is used to generate property accessors for POJOs.

Extract the PojoPropertyAccessor.ftl file into a local folder e.g. ${hbm.template.path}, respecting the whole path, for example: ${hbm.template.path}/pojo/PojoPropertyAccessor.ftl.

The contents of the file will be 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 pre and post conditions on our set method generation just by 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, and their contents will be generated into the body of the relevant set method.

As an example let us add a pre-condition for the name property which will prevent the Person class from having an empty name. To achieve this we have to modify the Person.hbm.xml file like so:

<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 either Eclipse or Ant, as long as you remember to set or fill in the templatepath setting. For Ant we configure the <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 file Person.java in ${hbm2java.dest.dir}:

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

In conclusion, this document is intended to introduce you to Hibernate plugin specific features related to tools both for the Eclipse and Ant tasks.

In Chapter 4, Eclipse Plugins you've learned about a set of wizards for creating Mapping files, Configuration files, Console Configurations, become familiar with Mapping and Configuration files editors, tooling for organizing and controlling Reverse Engineering, Hibernate Console and Mapping diagrams.

The rest chapters have explored the use of 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.