JBoss.orgCommunity Documentation
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;
}
}
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
!
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>
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.
Here you can get more in-depth explanation on the subject of equals() and hashcode().
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) && (firstName.length() == 0) ) {
throw new IllegalArgumentException("firstName can not be an empty String");
}
</meta>
</property>
</class>
</hibernate-mapping>
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;
}
}
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.