HyperJaxb Java Net
HyperJaxb Java Net
JAXB
Aleksei Valikov
1. Introduction
Primary goal of this development is to provide JAXB objects with a relational persistence layer. To achieve this
goal, we have developed an add-on that allows combining Sun's reference implementation of JAXB (XJC) with
Hibernate, a well-known object/relational mapping tool.
To map between relational tables and columns and Java objects, Hibernate requires an explicit definition of
mapping, usually in a form of XML file. Having this mapping, Hibernate can save objects in a relational data-
base, load them back, query for them etc. Consequently, to persist JAXB objects with Hibernate, we need to
produce object/relational mapping for these objects.
Instead of defining mappings in XML files, many Hibernate users prefer using XDoclet technology that allows
to specify mapping properties directly in the source code of the object classes. In this case, source code is an-
notated with special tags called "xdoclets". XDoclet parses source code and generates required artifacts (like
Hibernate mapping). XDoclet allows developers to remain source-centric instead of supporting several related
but separated artifacts (source code and mapping definitions) in parallel.
XDoclet is a perfect point to bring JAXB and Hibernate together. On the one hand, JAXB implementations
automatically generate source code of Java objects based on the XML Schema. On the other hand, XDoclet
automatically generates object/relational mapping for Hibernate based on the annotated source code. The only
missing fragment in this puzzle is automatic generation of xdoclets in JAXB objects. This missing fragment is
implemented by the HyperJAXB package.
HyperJAXB extends the code generated by Sun's reference implementation of JAXB with Hibernate xdoclet
annotations. Annotated sources of JAXB objects are processed into object/relational mapping for Hibernate.
Hibernate also provides tools to produce database schema for the target database from the generated mapping.
A combination of JAXB RI, HyperJAXB and Hibernate tools allows automatically generating the following ar-
tifacts:
2.1. Basics
In order to make JAXB objects persistable, we need to generate adequate object/relational mapping annotations
1
HyperJAXB - Relational persistence for JAXB
for all the stateful elements of the classes. This effectively means that we have to map all the properties of the
generated implementation classes.
Depending on the cardinality, properties may be single or collection properties. Collection properties are those
returning instances of java.util.Set, java.util.Map, java.util.List and so on.
Collections may be homogeneous (all elements have the same type) and heterogeneous (types of elements may
differ). Although both options are used in JAXB, Hibernate generally discourages usage of heterogeneous col-
lections. Therefore we introduce additional constructions to homogenize heterogeneous collections.
Collections or single properties may store simple or complex values. HyperJAXB supports five basic categories
of values:
• simple values - primitives (int, float, boolean and so on) and basic object types (java.lang.String,
java.lang.Boolean, java.math.BigDecimal etc.);
• "any" type;
• DOM elements;
The task of the add-on is to generate mapping definitions for properties of all of the types as well as other con-
structs which are required for the mapping to work.
Hibernate requires persistent classes to be explicitly declared. To achieve this, generated implementation
classes are annotated with @hibernate.class xdoclet. Name of the database table is explicitly specified in the
table attribute of this xdoclet.
Table name is derived from the name of the implemented interface. Derivation algorithm ensures that generated
name is unique across all classes of all packages. In future we plan to make name of the table customizable.
Since different packages may contain classes with equal names, auto-import must be turned off to avoid name
collission. This is achieved with auto-import="false" in @hibernate.mapping class-level xdoclet.
// ...
package de.baw.nokis.impl;
/**
* @hibernate.mapping auto-import="false"
* @hibernate.class table="RespParty"
*/
public class RespPartyImpl implements de.baw.nokis.RespParty // ...
{
// ...
}
XJC generates implementation classes for top-level elements as subclasses of implementation classes of their
complex types. HyperJAXB follows "table-per-class" strategy in polymorphic mapping. Accordingly, imple-
mentation classes for top level elements are annotated with @hibernate.joined-subclass and
2
HyperJAXB - Relational persistence for JAXB
// ...
package de.baw.nokis.impl;
/**
* @hibernate.joined-subclass table="Party"
* @hibernate.joined-subclass-key column="parentid"
*/
public class PartyImpl extends de.baw.nokis.impl.RespPartyImpl
implements de.baw.nokis.Party // ...
{
// ...
}
2.2.1.1. Customization
You may customize @hibernate.class and @hibernate.joined-subclass xdoclets by providing your own
annotations in the binding customization. Consider the following example:
<xs:complexType name="MD_Metadata">
<xs:annotation>
<xs:appinfo>
<jaxb:class>
<jaxb:javadoc>
@hyperjaxb.hibernate.class table="Metadata"
</jaxb:javadoc>
</jaxb:class>
</xs:appinfo>
</xs:annotation>
<!-- ... -->
</xs:complexType>
These customization will map metadata element onto the MetadataElement table and MD_Metadata complex
type onto the Metadata table.
/**
* @hibernate.mapping auto-import="false"
* @hibernate.class table="Metadata"
*/
public class MDMetadataImpl implements MDMetadata // ...
{ // ... }
/**
* @hibernate.joined-subclass table="MetadataElement"
* @hibernate.joined-subclass-key column="parentid"
*/
public class MetadataImpl extends MDMetadataImpl // ...
{ // ... }
This trick is especially helpful, if your classes have names prohibited in your database (like Table or User).
Please note that xdoclets you provide in customizing annotations replace xdoclets generated by HyperJAXB by
default completely.
3
HyperJAXB - Relational persistence for JAXB
<xsd:complexType name="EntryType">
<xsd:annotation>
<xsd:appinfo>
<jaxb:class>
<jaxb:javadoc>
@hyperjaxb.hibernate.cache usage="read-write"
</jaxb:javadoc>
</jaxb:class>
</xsd:appinfo>
</xsd:annotation>
<!-- ... -->
</xsd:complexType>
Hibernate requires that each persistent class declares a primary key column of the database table. To achieve
this, each implementing class (except for the subclasses) is extended with an identifier property. This property
is defined by the string idInternal field and a getter/setter pair getIdInternal()/setIdInternal(String an-
Id). Identifier is a 32-character string assigned by the Hibernate when object is saved into the database for the
first time. To allow Hibernate distinguish new instances, unsaved-value="null" attribute is generated as well.
// ...
package de.baw.nokis.impl;
// ..
public class ContactImpl implements de.baw.nokis.Contact // ...
{
private java.lang.String idInternal;
/**
* @hibernate.id type="string" unsaved-value="null" length="32" generator-class="uuid.hex"
*/
public java.lang.String getIdInternal()
{
return idinternal;
}
// ...
}
<xsd:complexType name="EntryType">
<xsd:sequence><!-- ... --></xsd:sequence>
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<jaxb:property>
<jaxb:javadoc>
@hyperjaxb.hibernate.id unsaved-value="null" generator-class="uuid.hex"
4
HyperJAXB - Relational persistence for JAXB
</jaxb:javadoc>
</jaxb:property>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
If you use an id generator that needs to be parametrized, include the required parameters in
@hyperjaxb.hibernate.generator-param.
/**
* @hibernate.property
*/
public java.lang.String getPostCode()
{
return _PostCode;
}
To customize simple single property, include a @hyperjaxb.hibernate.property xdoclet into the property
javadoc (from the simple test):
<xsd:complexType name="EntryType">
<!-- ... -->
<xsd:sequence>
<!-- ... -->
<xsd:element name="value" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<jaxb:property>
<jaxb:javadoc>
@hyperjaxb.hibernate.property length="12"
</jaxb:javadoc>
</jaxb:property>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<!-- ... -->
</xsd:sequence>
<!-- ... -->
</xsd:complexType>
/**
* @hibernate.property type="net.sf.hibernate.type.BinaryType"
*/
public byte[] getValue()
5
HyperJAXB - Relational persistence for JAXB
{
return _Value;
}
<xsd:annotation>
<xsd:appinfo>
<jaxb:globalBindings typesafeEnumBase="test:SexType"/>
</xsd:appinfo>
</xsd:annotation>
<xsd:simpleType name="SexType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Male"/>
<xsd:enumeration value="Female"/>
</xsd:restriction>
</xsd:simpleType>
For the enumerated simple type SexType from the fragment above JAXB generates the following class:
// ...
package test;
/**
* Java content class for SexType.
* <p>The following schema fragment specifies the expected content contained within this java content
* <p>
* <pre>
* <restriction base="{http://www.w3.org/2001/XMLSchema}string">
* <enumeration value="Male"/>
* <enumeration value="Female"/>
* </restriction>
* </pre>
*/
public class SexType implements java.io.Serializable
{
private final static java.util.Map valueMap = new java.util.HashMap();
public final static java.lang.String _MALE = com.sun.xml.bind.DatatypeConverterImpl.installHook("M
public final static test.SexType MALE = new test.SexType(_MALE);
public final static java.lang.String _FEMALE = com.sun.xml.bind.DatatypeConverterImpl.installHook(
public final static test.SexType FEMALE = new test.SexType(_FEMALE);
private final java.lang.String lexicalValue;
private final java.lang.String value;
protected SexType(java.lang.String v) {
value = v;
lexicalValue = v;
valueMap.put(v, this);
}
6
HyperJAXB - Relational persistence for JAXB
return super.equals(o);
}
HyperJAXB supports persistance of typesafe enums with the help of user types (see
net.sf.hibernate.UserType). Each of the typesafe enum classes is appended with an inner static class Type:
// ...
package test;
// ...
7
HyperJAXB - Relational persistence for JAXB
/**
* @hibernate.property type="test.SexType$Type"
*/
public test.SexType getSex()
{
return _Sex;
}
For xjc:dom-annotated constructs, XJC generates properties with types from DOM hierarchy.
HyperJAXB supports persistance of DOM properties with the help of a user type
de.fzi.dbs.jaxb.addon.hibernate.ElementType. This special user type serializes DOM content when sav-
ing and parses the XML back when loading data from the database.
/**
* @hibernate.property type="de.fzi.dbs.jaxb.addon.hibernate.ElementType"
*/
public org.w3c.dom.Element getMath()
{
return _Math;
}
<xsd:complexType name="RootType">
<xsd:sequence>
<xsd:any processContents="strict"/>
</xsd:sequence>
</xsd:complexType>
The element root may contain any other declared element. To map "any" construct, the corresponding property
is annotated with @hibernate.any and @hibernate.any-column xdoclets:
/**
* @hibernate.any id-type="string" cascade="all"
* @hibernate.any-column name="Any_class"
* @hibernate.any-column name="Any_id" length="32"
*/
public java.lang.Object getAny()
{
return _Any;
}
8
HyperJAXB - Relational persistence for JAXB
Complex properties are essentially one-to-one associations which may be implemented in Hibernate with
primary key or foreign key constraints. Primary key associations require related objects to have equal identifi-
ers. This does not suit well our identifier model (each object has its own globally unique identifier). Therefore,
one-to-one associations are implemented with foreign keys. This corresponds to the many-to-one mapping:
/**
* @hibernate.many-to-one cascade="all"
* class="de.baw.nokis.impl.RespPartyRecImpl"
*/
public de.baw.nokis.RespPartyRec getRespPartyRec()
{
return _RespPartyRec;
}
At the same time Hibernate checks identity of collection properties by object identity. Proxied field makes Hi-
bernate think that a new collection was assigned to the property. This results in full rewriting of the collection
when object is saved or loaded.
To solve this and few other problems, we generate a supporting inner property for each of the collection proper-
ties. Main property is exposed as proxy of the inner property. Inner property has both getter and setter accessing
the field directly and may be persisted by Hibernate. For example, the value collection property in the listing
above will be augmented as follows:
9
HyperJAXB - Relational persistence for JAXB
In the code above, _ValueInternal is the real field that will hold the real collection object. Accessors get-
ValueInternal() and setValueInternal(...) provide direct access to this field and may be used by Hibern-
ate to persist the collection. The field _Value holds a JAXB proxy of the inner class ValueInternalProxyList,
which provides access to the _ValueInternal field.
Supporting inner properties are also used to homogenize heterogeneous collections (this usage is discussed
later).
/**
* @hibernate.list table="ContactImpl_FaxNumInternal" cascade="all" where="FaxNumInternal_index is not
* @hibernate.collection-key column="ContactImpl_id"
* @hibernate.collection-index column="FaxNumInternal_index"
* @hibernate.collection-element column="value" type="java.lang.String"
*/
public java.util.List getFaxNumInternal()
{
return _FaxNumInternal;
}
Collection key column is given the name classname_id, index column is propertyname_index, value column
is always named value. Note also where="propertyname_index is not null" condition which allows distin-
guishing values belonging to different collections properties.
/**
* @hibernate.list table="RootType_SexesInternal" cascade="all" where="SexesInternal_index is not null
10
HyperJAXB - Relational persistence for JAXB
* @hibernate.collection-key column="RootTypeImpl_id"
* @hibernate.collection-index column="SexesInternal_index"
* @hibernate.collection-element column="value" type="test.SexType$Type"
*/
public java.util.List getSexesInternal()
{
return _SexesInternal;
}
<xsd:complexType name="RootType">
<xsd:sequence>
<!-- ... -->
<xsd:element name="contents" maxOccurs="unbounded">
<xsd:annotation>
<xsd:appinfo>
<xjc:dom/>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
Augmented code:
/**
* @hibernate.list table="RootType_ContentsInternal" cascade="all" where="ContentsInternal_index is no
* @hibernate.collection-key column="RootTypeImpl_id"
* @hibernate.collection-index column="ContentsInternal_index"
* @hibernate.collection-element column="value" type="de.fzi.dbs.jaxb.addon.hibernate.ElementType"
*/
public java.util.List getContentsInternal()
{
return _ContentsInternal;
}
<xsd:complexType name="GroupType">
<xsd:complexContent>
<xsd:extension base="test:Principal">
<xsd:sequence>
<xsd:any maxOccurs="unbounded" processContents="strict"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
Augmented code:
/**
* @hibernate.list table="GroupType_AnyInternal" cascade="all" where="AnyInternal_index is not null"
* @hibernate.collection-key column="GroupTypeImpl_id"
11
HyperJAXB - Relational persistence for JAXB
* @hibernate.collection-index column="AnyInternal_index"
* @hibernate.many-to-any id-type="string"
* @hibernate.many-to-any-column name="AnyInternal_class"
* @hibernate.many-to-any-column name="AnyInternal_id" length="32"
*/
public java.util.List getAnyInternal()
{
return _AnyInternal;
}
/**
* @hibernate.list cascade="all" where="CntOnlineResInternal_index is not null"
* @hibernate.collection-key column="ContactImpl_id"
* @hibernate.collection-index column="CntOnlineResInternal_index"
* @hibernate.collection-one-to-many class="de.baw.nokis.impl.OnLineResImpl"
*/
public java.util.List getCntOnlineResInternal()
{
return _CntOnlineResInternal;
}
Collection key column is given the name classname_id, index column is named propertyname_index.
<xsd:complexType name="RootType">
<xsd:sequence maxOccurs="unbounded">
<xsd:element name="element1" type="test:ElementType"/>
<xsd:element name="element2" type="test:ElementType"/>
</xsd:sequence>
</xsd:complexType>
The getter getElement1AndElement2() exposes a heterogeneous collection. Although Hibernate can map het-
erogeneous collection properties like this one using many-to-any mapping, this approach is not recommended.
However, heterogeneous collections may be homogenized by introducing a new aggregating type:
12
HyperJAXB - Relational persistence for JAXB
This type has one property per type allowed in the heterogeneous collection. It is obvious that for any hetero-
geneous collection storing Element1 or Element2 instances there is an equivalent homogeneous collection stor-
ing instances of Element1AndElement2. This approach is used in HyperJAXB to generate supporting inner
properties as homogeneous collections. Inner collection is automatically converted into a heterogeneous collec-
tion when it is exposed by the proxy list class.
• @hyperjaxb.hibernate.list
• @hyperjaxb.hibernate.collection-cache
• @hyperjaxb.hibernate.collection-jcs-cache
• @hyperjaxb.hibernate.collection-key
• @hyperjaxb.hibernate.collection-key-column
• @hyperjaxb.hibernate.collection-index
• @hyperjaxb.hibernate.collection-element
• @hyperjaxb.hibernate.collection-composite-element
• @hyperjaxb.hibernate.collection-one-to-many
• @hyperjaxb.hibernate.index-many-to-many
• @hyperjaxb.hibernate.many-to-many
• @hyperjaxb.hibernate.many-to-any
• @hyperjaxb.hibernate.many-to-any-column
Please note that you will need to take care of table names, class names, element names etc. by yourself.
As an example, we will demonstrate customization of the Address collection withing User complex type:
<xs:complexType name="User">
<xs:sequence>
<xs:element name="Address" type="pp:Address" maxOccurs="unbounded">
<xs:annotation>
<xs:appinfo>
<jaxb:property>
<jaxb:javadoc>
@hyperjaxb.hibernate.list
table="address" cascade="all" where="address_id is not null"
@hyperjaxb.hibernate.collection-key
column="user_id"
@hyperjaxb.hibernate.collection-index
column="address_id"
13
HyperJAXB - Relational persistence for JAXB
@hyperjaxb.hibernate.collection-one-to-many
cascade="all"
class="com.pps.schema.impl.AddressImpl"
</jaxb:javadoc>
</jaxb:property>
</xs:appinfo>
</xs:annotation>
</xs:element>
<!-- ... -->
</xs:sequence>
<!-- ... -->
</xs:complexType>
/**
* @hibernate.list table="address" where="address_id is not null" cascade="all"
* @hibernate.collection-key column="user_id"
* @hibernate.collection-index column="address_id"
* @hibernate.collection-one-to-many class="com.pps.schema.impl.AddressImpl" cascade="all"
*/
public java.util.List getAddressInternal() {
return _AddressInternal;
}
<list
name="addressInternal"
table="address"
lazy="false"
inverse="false"
cascade="all"
where="address_id is not null">
<key column="user_id"/>
<index column="address_id"/>
<one-to-many class="com.pps.schema.impl.AddressImpl"/>
</list>
You may instruct HyperJAXB to ignore the certain property. To do this, mark this property with
@hyperjaxb.ignore (from simple test):
<xsd:complexType name="ElementType">
<xsd:sequence>
<!-- ... -->
<xsd:element name="ignored" type="xsd:string" minOccurs="0">
<xsd:annotation>
<xsd:appinfo>
<jaxb:property>
<jaxb:javadoc>
@hyperjaxb.ignore
</jaxb:javadoc>
</jaxb:property>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<!-- ... -->
</xsd:sequence>
</xsd:complexType>
14
HyperJAXB - Relational persistence for JAXB
HyperJAXB will generate no xdoclets for the ignored properties. Use this customization if you don't want a cer-
tain property to be persisted.
To use HyperJAXB with XJC you need to include HyperJAXB library (hyperjaxb.jar) into the class path and
turn on the add-on. Add-on is turned on using arg elements. A sample build file fragment is presented below:
<target name="generate.sources">
<taskdef name="xjc"
classname="com.sun.tools.xjc.XJCTask"
classpathref="xjc.lib.path"/>
<mkdir dir="${generated.sources}"/>
<xjc target="${generated.sources}">
<arg line="-nv"/>
<arg line="-extension"/>
<arg line="-Xhibernate-xdoclets"/>
<binding dir="${basedir}">
<include name="binding/*.xml"/>
</binding>
<schema dir="${basedir}">
<include name="schema/*.xsd"/>
</schema>
</xjc>
</target>
It is assumed that xjc.lib.path contains all the libraries required by XJC as well as hyperjaxb.jar and hi-
bernate2.jar.
After this step the generated.sources folder should contain generated sources annotated with Hibernate
xdoclets.
Building generated classes is not much different from building normal Java classes. However, sometimes it
makes sense to keep generated sources separate. If this is the case you'll need to build both generated and
manually written sources together using a target like:
15
HyperJAXB - Relational persistence for JAXB
<fileset dir="${generated.sources}">
<exclude name="**/*.java"/>
</fileset>
</copy>
</target>
In this target, compile.lib.path must include XJC libraries (HyperJAXB library is not required). We also
need to copy resources with the copy task.
Hibernate mapping is generated using the Hibernate XDoclet module. You'll first need to define the task and
then use it to produce the mapping. Here is the sample ant target:
<taskdef name="hibernatedoclet"
classname="xdoclet.modules.hibernate.HibernateDocletTask"
classpathref="hibernatedoclet.lib.path"/>
<tstamp>
<format property="TODAY" pattern="dd-MM-yy"/>
</tstamp>
<mkdir dir="${hibernate.mapping}"/>
<hibernatedoclet
destdir="${hibernate.mapping}"
mergedir="${generated.sources}"
excludedtags="@version,@author,@todo,@see,@throws"
addedtags="@xdoclet-generated at ${TODAY},@copyright The XDoclet Team,@author XDoclet"
force="false"
verbose="false">
<fileset dir="${generated.sources}">
<include name="**/*.java"/>
<exclude name="**/runtime/*.java"/>
</fileset>
<hibernate version="2.0"/>
</hibernatedoclet>
</target>
The path hibernatedoclet.lib.path in this target must include the whole set of Hibernate XDoclet libraries.
Please consult the Hibernate documentation for a detailed listing.
After this step you should receive a number of Hibernate mapping files (???.hbm.xml) in your hibernate dir-
ectory.
After the Hibernate mapping is generated it may be used to produce a database schema for the target database.
<taskdef name="schemaexport"
classname="net.sf.hibernate.tool.hbm2ddl.SchemaExportTask"
16
HyperJAXB - Relational persistence for JAXB
classpathref="schemaexport.lib.path"/>
<mkdir dir="${database}"/>
<schemaexport
properties="${hibernate.properties}"
quiet="yes"
text="no"
drop="no"
delimiter=";"
output="${database}/schema.sql">
<fileset dir="${hibernate.mapping}">
<include name="**/*.hbm.xml"/>
</fileset>
</schemaexport>
</target>
This target requires Hibernate properties defined in hibernate.properties file and ???.hbm.xml mapping
files in hibernate directory. Output is the database schema in database/schema.sql. If text attribute is set to
no (not text only), this task will also initialize the database defined in Hibernate properties.
4.1. Prerequisites
Before you can use Hibernate to persist your objects, you'll need to set up a session factory. This basically
means that you'll need to load Hibernate properties and mapping files. The following code illustrates the factory
setup process.
/**
* Test setup.
*
* @throws Exception In case of setup problems.
*/
public void setUp() throws Exception
{
super.setUp();
final Configuration cfg = new Configuration();
sessionFactory = cfg.buildSessionFactory();
}
17
HyperJAXB - Relational persistence for JAXB
}
final File[] files = directory.listFiles();
for (int index = 0; index < files.length; index++)
{
final File file = files[index];
if (recurse && file.isDirectory())
{
extendedConfiguration = addDirectory(extendedConfiguration,
file, recurse, filenameFilter);
}
else if (file.isFile() &&
filenameFilter.accept(directory, file.getName()))
{
extendedConfiguration = extendedConfiguration.addFile(file);
}
}
return configuration;
}
/**
* Returns the directory containing Hibernate mapping.
* @return Directory containing Hibernate mapping.
*/
public File getHibernateDirectory()
{
return new File("hibernate");
}
/**
* Returns Hibernate properties file.
* @return Hibernate properties file.
*/
public File getHibernatePropertiesFile()
{
return new File(getHibernateDirectory(), "hibernate.properties");
}
The code constructs a new factory configuration, loads properties from the hibernate.properties file and
adds all the mapping files (*.hbm.xml) recursively processing the hibernate directory and all its sub-
directories. The loaded configuration is then used to produce a Hibernate session factory.
For the instances of unmarshaller, marshaller and validator used to work with JAXB objects we first need to
obtain a JAXB context:
unmarshaller = context.createUnmarshaller();
marshaller = context.createMarshaller();
validator = context.createValidator();
Instead of hardcoding the package name you could look it up dynamically using one of the generated classes:
Having configured session factory and unmarshaller, you may now unmarshall the XML content into an object
and save this object into the database:
18
HyperJAXB - Relational persistence for JAXB
The code above assumes document is a DOM document you want to unmarshall to receive an instance of a re-
sponsible party object (a RespParty instance).
The loading and marshalling process is exactly the reverse of unmarshalling and saving. You look up the object
using its class and identifier (note that you'll need the implementation class, not the interface class to look up)
and utilize a marshaller to serialize it to DOM, SAX or any othe supported representation of XML.
The document loadedDocument should be identical to the document that we've unmarshalled and saved in the
previous section (modulo whitespace).
5. Project template
The easiest way to setup your project with HyperJAXB, is to use the prepared project template, included in the
distribution (dist/template.zip). For a basic setup you will only need to extract the template.zip into a new
directory and place the schema into the schema folder.
• classes (generated during the build) - directory containing the compiled classes;
• dist - distribution directory will contain the compiled and packed classes in a jar file;
• generated.source (generated during the build) - directory containing sources generated by JAXB and Hy-
perJAXB;
• lib - library directory (libraries are placed in their own subdirectories, ex. commons-lang.jar in lib/
19
HyperJAXB - Relational persistence for JAXB
jakarta-commons directory);
• schema - schema directory (the build assumes that schema files have *.xsd extension;
To configure your HyperJAXB project, you'll need to edit the following files:
• build.xml
• text property - set to yes if you only want to generate database schema DDL without exporting it into
the database (default), specify no if you want to export the schema into the database;
• init - initializes before the build (does noting in the template build);
• clean - cleans before the build (in the template build, deletes classes, generated.sources, hibernate/map-
ping and database directories);
• generate.sources - invokes XJC and HyperJAXB to generate annotated sources in the gener-
ated.sources directory;
• compile (depends on the generate.sources target) - compiles generated and static (those from the src dir-
ectory) sources into the classes directory;
• jar (depends on the compile target) - packs the compiled classes and places the generated jar file into the
dist directory;
• generate.hibernate.mapping (depends on the jar target) - generates Hibernate mapping in the hibern-
ate/mapping directory;
• all (depends on the export.database.schema target) - full build process (default target).
6. Sample application
20
HyperJAXB - Relational persistence for JAXB
HyperJAXB distribution contains a number of test projects in the tests directory. In this section we will con-
sider one of these examples (iso19115) in detail.
• XML document (initial document) is loaded from file and unmarshalled into a JAXB object;
To support this roundtrip we use XJC and HyperJAXB to generate annotated JAXB classes. Generated sources
are then utilized to produce Hibernate mapping and database schema.
To build and run the application simply run runtest iso19115 in the tests directory. This will generate JAXB
objects out of the schema, produce Hibernate mapping, database schema and finally run the roundtrip test case.
You will not need to set up the database. Example runs against an embedded in-memory HSQL database that
does not require any setup.
The starting point is an XML schema stored in tests/iso19115/schema/schema.xsd. This schema is a very
small sample schema based on the ISO standard for geographic metadata.
21
HyperJAXB - Relational persistence for JAXB
22
HyperJAXB - Relational persistence for JAXB
23
HyperJAXB - Relational persistence for JAXB
/**
* Tests document roundtrim: unmarshall into an object, save object into the database,
* load object from the database, marshall the object.
* Resulting XML should be equivalent to the initial XML.
*/
public void testRoundtrip()
{
try
{
// Unmarshall the document
final RespParty respParty = (RespParty) unmarshaller.unmarshal(document);
// Open the session, save object into the database
final Session saveSession = sessionFactory.openSession();
// Save id for the later use
final String id = (String) saveSession.save(respParty);
saveSession.flush();
// Close the session
saveSession.close();
24
HyperJAXB - Relational persistence for JAXB
<metadata xmlns="http://www.fzi.de/dbs/tests/iso19115">
<fileIdentifier>id000001</fileIdentifier>
<language>en</language>
<hierarchyLevel>dataset</hierarchyLevel>
<hierarchyLevel>series</hierarchyLevel>
<identificationInfo>
<abstract>The abstract.</abstract>
<purpose>The purpose.</purpose>
<status>planned</status>
<geographicBox>
<extentTypeCode>true</extentTypeCode>
<westBoundLongitude>11.7254223679</westBoundLongitude>
<eastBoundLongitude>11.8123425682</eastBoundLongitude>
<southBoundLatitude>48.3282639631</southBoundLatitude>
<northBoundLatitude>48.4438272635</northBoundLatitude>
</geographicBox>
</identificationInfo>
</metadata>
When we save object structure unmarshalled from this XML file, Hibernate executes the following SQL state-
ments (identifiers are shortened for better readability):
parentid
id-0001
25
HyperJAXB - Relational persistence for JAXB
id-0001 0 dataset
id-0001 1 series
7. Acknowledgements
HyperJAXB and the sample application use or include the following products, libraries and tools:
• Hibernate;
Hibernate is a high performance object/relational persistence and query service for Java. You can find Hi-
bernate at http://www.hibernate.org
• Jakarta Commons libraries (Collections, Lang, Logging, BeanUtils, JXPath); The Jakarta Commons librar-
ies are dedicated to creating and maintaining reusable Java components. The libraries are:
• Collections - provides a suite of classes which extend the Java Collections Framework.
• BeanUtils - provides easy-to-use wrappers around the Java reflection and introspection APIs.
• JXPath - provides utilities for manipulating Java classes that conform to the JavaBeans naming conven-
tions using the XPath syntax. The library also supports maps, DOM and other object models.
Jakarta Commons can be found at http://jakarta.apache.org
26
HyperJAXB - Relational persistence for JAXB
able while SAX is better suited for bigger documents. While DOM can create XML documents SAX can
not. You can find DOM at http://www.w3.org/DOM/ and SAX at http://www.saxproject.org
• Dom4j;
Dom4j is a open source library for working with XML, XPath and XSLT for the Java platform using the
Java Collenctions Framework. It has full support for DOM, SAX and JAXP. You can find Dom4j at ht-
tp://www.dom4j.org/
• CGLib;
CGLib, the Java Code Generation Library, can be used to extend Java classes and implement Java inter-
faces at runtime. You can find CGLib at http://cglib.sourceforge.net
• HSQL database;
The Hypersonic SQL database is a Java database engine with a standard SQL and JDBC interface. Through
it's compactness, it is well suited for usage as a server in applets and applications. You can find the Hyper-
sonic SQL database at http://hsql.sourceforge.net
• JUnit;
JUnit is a regression testing framework for developers who implement unit test cases. JUnit has a console
user interface as well as a GUI. You can find JUnit at: http://www.junit.org
• XML Unit;
XML Unit extends JUnit (and NUnit) to enable unit testing of XML. It can compare a control XML docu-
ment to a test document, validate documents and compare results of XPath expressions. XML Unit can be
found at http://xmlunit.sourceforge.net/
8. Legal
This product includes software developed by the Apache Software Foundation (http://www.apache.org/).
27