Skip to end of metadata
Go to start of metadata

Introduction

This is a simple tutorial which is intended to help you get started with Hyperjaxb3. The tutorial illustrates how to carry out the following basic tasks:

  • Start a Hyperjaxb3 project.
  • Compile the XML schema and use Hyperjaxb3 to annotate the generated classes.
  • Parse an XML sample and save the parsed object the relational database.
  • Load object from the database and marshall it back to XML.
  • Apply basic customizations - customize column names.

As an XML Schema for this tutorial we will use the well-known purchase order schema example from the XML Schema Primer:

po.xsd

XML Schema Primer also provides a sample XML file:

po.xml

We will use Hibernate as JPA provider and HSQLDB as database

Starting a project with Hyperjaxb3

The easiest way to start a project with Hyperjaxb3 is to take one of the project templates. For this tutorial, we will take the basic project template.

Hyperjaxb3 provides templates for both Ant and Maven, please download the appropriate distribution here and unzip it to the target directory.

After unzipping you will get the following directory structure:

  • src
    • main
      • java
      • resources
    • test
      • java
      • resources
      • samples
  • pom.xml or build.xml

Maven users will recognize the usual Maven project structure:

  • src/main/java and src/main/resources - main java code and resources.
  • src/test/java and src/test/resources - java code and resources used for testing.
  • There is also an additional src/test/samples directory which will contain XML samples we will use for testing.

What you have to do next is to put po.xsd to src/main/resources and po.xml to src/test/samples. If you wish, you may also modify the project name in build.xml or pom.xml, but this is purely optional.

After this step you have a functional project. To build it just run:

Or:

You'll see a few log statements displayed by the build system and at the end there'll be a JAR generated in target directory. Behind the curtains, the build system did a complex job of cleaning, generating and compiling the code, running the tests (roundtrip test in this case) and packaging the compile code, but what you finally get is a neat JAR artifact and a message that everything worked.

What was generated?

If you browse the target/generated-sources/xjc directory, you'll find few generated java files, for instance PurchaseOrderType.java. Let's take a closer look into this file:

generated/PurchaseOrderType.java
Icon

Note that Hyperjaxb3 wrapped the orderDate property into orderDateItem property, presenting javax.xml.datatype.XMLGregorianCalendar-typed property (which is not supported by JPA)) as simple java.util.Date. JAXB is not 100% compatible with JPA, so Hyperjaxb3 often need to apply such workarounds to make things work.

As you see, apart from the JAXB @Xml... annotations, this class also contains @Entity, @Table, @ManyToOne and few other annotations generated by the Hyperjaxb3 plugin. These annotations turn this class into an entity which can be persisten with JPA. This entity is a part of the persistence unit defined by the META-INF/persistence.xml descriptor in target/generated-sources/xjc:

target/generated-sources/xjc/META-INF/persistence.xml

As you may guess, this file was also generated by Hyperjaxb3.

To sum it up, what we got produced from the XJC schema compiler and Hyperjaxb3 plugin is:

  • persistence unit description in META-INF/persistence.xml;
  • a set of Java files containing both JAXB and JPA annotations.

This is may be not too much, but this is enough to:

  • unmarshall objects from XML and marshall them back with JAXB;
  • or to persist objects into the database and to load them back with JPA.

Next sections of this tutorial demonstrates these operations.

Working with JAXB and JPA

Unmarshalling, marshalling and validating with JAXB

This section illustrates basic JAXB operations: unmarshalling, marshalling and validating. Source code for the unit tests can be found here.

Set-up

First of all, what we need to work with JAXB is an instance of JAXB context. This can be obtained by the JAXBContext.newInstance(...) call with the context path. Context path is typically the package name of the generated classes (our schema-derived classes were generated into the generated package hence generated context path). We will also need an instance of the generated ObjectFactory to help us creating objects (this surely optional):

Unmarshalling

Let's start with unmarshalling. To unmarshall, we'll need an unmarshaller first:

Unmarshalling itself is just one line of code:

Now just cast an check that unmarshalling really worked:

Marshalling

Marshalling is not more complex. First of all all, let's create a structure we want to marshal:

Icon

Strictly speaking the structure we created is not valid according to the schema, but this is not relevant for out test.

Then create a marshaller and use it to marshall the object into the desired target, for instance into a DOM result:

Finally, let's check we've got what we wanted:

Validating

Since our po.xsd schema is place in src/main/resources, it will be available as a resource in the runtime. For instance, we could use it to validate data during marshalling or unmarshalling. This section illustrates validation on marshalling.

Let's start by composing an invalid structure:

Next step is creating an instance of schema:

Now all you need to do to validate on marshal is to provide the created schema to the marshaller:

To receive notifications of validation events, register an appropriate event handler:

Since out structure is invalid, we may check that the the list of validation events is not empty:

Icon

Note that no exception will be thrown in this case. This is due to the return true; we have in ValidationEventHandler.handleEvent(...). Returning true means "go on with current operation, this error is not severe.

Persisting objects with JPA

This section considers basic JPA operations: persisting an object into a relational database and retrieving it back. Source code for the unit tests can be found here.

Set-up

In order to work with JPA we first need to create an entity manager factory. The easiest way to do this is to simply pass the name of persistence unit (see META-INF/persistence.xml above) to the Persistence.createEntityManagerFactory(...):

However, this method assumes that your database connection is configured in the persistence unit itself. I usually do not recommend specifying any database-specific properties in persistence.xml. ORM mappings should be generic, database properties are case-specific and therefore must be kept separately.

For this reason I usually define such specific properties in the resource persistence.properties. This can be placed in src/main/resources or in src/test/resources depending on wether you use it to define your main database connection - or just something for testing. We're testing so our persistence.properties will be placed in src/test/resources.

Reading the additional persistence.properties make the initialization of the entity manager factory slightly more complicated:

Properties contained in the persistence.properties are database and JPA provider specific. For instance, basic project template is preconfigured for Hibernate and HSQLDB:

src/test/resources/persistence.properties
Icon

Note the hibernate.hbm2ddl.auto=create-drop property in the list above. This property instructs Hibernate to initalize the database. This feature is extremely useful for testing - you don't have to take care of the database schema, JPA provider will do it for you. But don't forget to remove this setting from productional configurations - you may loose or corrupt your data if you forget it.

This configuration uses HSQLDB in standalone (in-process) mode. The database will be automatically created in the target/test-database folder, there is no set-up database necessary.

Saving the object into the database

First let's create an object structure to test with:

To save out object into the database we'll need o get an entity manager from the factory:

After the object is saved, we can get the generated id:

Loading the object from the database

On the last step we've got the generated id of the object we saved. We'll need this id to retrieve out object back:

Combining JAXB and JPA

Now it comes to the core point of Hyperjaxb. Since our schema-derived classes are both JAXB-enabled (thanks to the JAXB schema compiler) and JPA-enabled (thanks to the Hyperjaxb3 plugin), there is not problem to go XML-objects-database or the other way round. Let's demonstrate it.

Unmarshall:

Persist:

Load:

Marshal:

The complete unit test can be found here.

Icon

If you want to check wether generated mappings work, you don't need to write such unit tests yourself. You may simply instruct Hyperjaxb3 to generate a roundtrip test for you.

Generated database

At this point you might be curious what does the generated database look like. Below is the DDL for the HSQL database schema:

Database schema

As you can recognize, this database schema reflects our XML Schema precisely, including types and associations. For our po.xml XML sample these table will contain the following data:

PURCHASEORDERTYPE

HJID

COMMENT_

ORDERDATEITEM

BILLTO_..._ID

ITEMS_..._ID

SHIPTO_..._ID

1

Hurry, my lawn is going wild!

1999-10-20

1

1

2

USADDRESS

HJID

CITY

COUNTRY

NAME_

STATE_

STREET

ZIP

1

Old Town

US

Robert Smith

PA

8 Oak Avenue

95819

2

Mill Valley

US

Alice Smith

CA

123 Maple Street

90952

ITEM

HJID

USPRICE

COMMENT_

PARTNUM

PRODUCTNAME

QUANTITY

SHIPDATEITEM

ITEM_ITEMS_ID

1

148.95

Confirm this is electric

872-AA

Lawnmower

1

NULL

1

2

39.98

NULL

926-AA

Baby Monitor

1

1999-05-21

1

ITEMS

HJID

1

Customizing the generated mappings

In the previous sections you have seen how easy it is to implement an XML-persistence solution with Hyperjaxb3. It is indeed a very powerful tool, but now the question is if this power can be controlled. That is, if generated mappings can be customized.

Hyperjaxb3 is built with focus on customizations. When generating mappings, Hyperjaxb3 assumes certain reasonable default generation strategy so that mappings will work even if you don't customize a single thing. But if you do need to change the generated expression of a certain field, you usually can.

This section demonstrates few very simple customizations that will be applied to the purchase order schema: we will customize table and column names, length of one of the columns. We'll also mark one of the partNum attribute of the Items complex type as primary identifier.

Icon

You can customize a lot more than that, please check the reference and customization guide for more information.

There are two possibilities to customize your schema: place your customization elements directly in the schema in xs:annotation/xs:appinfo elements or place them externally in binding files. We'll consider both possibilities.

Customizing in schema

In order to use Hyperjaxb3 customization elements in the schema you first need to declare customziation namespaces and to list the corresponding prefixes in the jaxb:extensionBindingPrefixes attribute. You typically need xmlns:hj="http://hyperjaxb3.jvnet.org/ejb/schemas/customizations" and xmlns:orm="http://java.sun.com/xml/ns/persistence/orm" namespaces, here's how it looks in the schema:

po.xsd

Now we're ready to customize.

When customizing elements, attributes or types directly in schema all you have to do is to add the customiziation elements in xsd:annotation/xsd:appinfo. For instance, imagine we need long (up to 1024 characters) product names:

Customizing in external binding files

Another possibility is to define your customizations in external binding files. This is very handy if you can't modify the schema. To do this, create a bindings.xjb file in the schema directory src/main/resources:

src/main/resources/bindings.xjb
Icon

Basic project template is pre-configured to pick up all the *.xjb files from the schema directory as binding files.

Icon

Note the xmlns:hj and xmlns:orm namespace declarations and jaxb:extensionBindingPrefixes="hj orm" attribute. These definitions enable Hyperjaxb3 customization elements - just like with in-schema customizations.

Binding elements in the example above associate customization elements with XML Schema constructs using XPath expression. Consider, for example the following definition:

The inner binding associates the hj:entity customization element with the USAddress complex element of the po.xsd schema (via the XPath /xs:schema/xs:complexType[@name='USAddress']). This definition is equivalent to the following in-schema version:

The effect of customization

If you take a look at the database schema generated for this customized version of po example, you'll see that few tables and columns are changed:

Customized database schema

For instance, note that USADDRESS was renamed to address or PRODUCTNAME has now a length of 1024.

Customizations shown here are really primitive, but Hyperjaxb3 is not limited to that. There is a whole lot more you can do with customizations. For example, you can swith between @OneToMany or @ManyToMany modes for associations, map your classes as @Embeddable instead of @Entity, choose inheritance strategies and so on.

There are also "global" customizations which can influence the default behavior of Hyperjaxb3. For instance, Hyperjaxb3 generates join-column mappings for one-to-many associations per default:

This causes a conflict with TopLink. In order to overcome this problem you can change the default one-to-many join mapping strategy to join-table:

Check the customization guide and the reference for further information on customizations.

Conclusion

This tutorial gave the primary introduction for Hyperjaxb3. You have seen how to start a Hyperjaxb3 project, how to use JAXB and JPA APIs together and how to customize the generated mappings.

I hope I could inspire you with these example. You can find further information on Hyperjaxb3 in our documentation section. If you need further support, please write to our users@hyperjaxb.java.net.

You can find the source code for this tutorial in SVN: