-
Notifications
You must be signed in to change notification settings - Fork 32
Purchase_Order_Tutorial
- Introduction
- Starting a project with Hyperjaxb3
- What was generated?
- Working with JAXB and JPA
- Generated database
- Customizing the generated mappings
- Conclusion
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
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Purchase order schema for Example.com.
Copyright 2000 Example.com. All rights reserved.
</xsd:documentation>
</xsd:annotation>
<xsd:element name="purchaseOrder" type="PurchaseOrderType"/>
<xsd:element name="comment" type="xsd:string"/>
<xsd:complexType name="PurchaseOrderType">
<xsd:sequence>
<xsd:element name="shipTo" type="USAddress"/>
<xsd:element name="billTo" type="USAddress"/>
<xsd:element ref="comment" minOccurs="0"/>
<xsd:element name="items" type="Items"/>
</xsd:sequence>
<xsd:attribute name="orderDate" type="xsd:date"/>
</xsd:complexType>
<xsd:complexType name="USAddress">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="street" type="xsd:string"/>
<xsd:element name="city" type="xsd:string"/>
<xsd:element name="state" type="xsd:string"/>
<xsd:element name="zip" type="xsd:decimal"/>
</xsd:sequence>
<xsd:attribute name="country" type="xsd:NMTOKEN" fixed="US"/>
</xsd:complexType>
<xsd:complexType name="Items">
<xsd:sequence>
<xsd:element name="item" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="productName" type="xsd:string"/>
<xsd:element name="quantity">
<xsd:simpleType>
<xsd:restriction base="xsd:positiveInteger">
<xsd:maxExclusive value="100"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="USPrice" type="xsd:decimal"/>
<xsd:element ref="comment" minOccurs="0"/>
<xsd:element name="shipDate" type="xsd:date" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="partNum" type="SKU" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<!-- Stock Keeping Unit, a code for identifying products -->
<xsd:simpleType name="SKU">
<xsd:restriction base="xsd:string">
<xsd:pattern value="\d{3}-[A-Z]{2}"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
XML Schema Primer also provides a sample XML file:
po.xml
<purchaseOrder orderDate="1999-10-20">
<shipTo country="US">
<name>Alice Smith</name>
<street>123 Maple Street</street>
<city>Mill Valley</city>
<state>CA</state>
<zip>90952</zip>
</shipTo>
<billTo country="US">
<name>Robert Smith</name>
<street>8 Oak Avenue</street>
<city>Old Town</city>
<state>PA</state>
<zip>95819</zip>
</billTo>
<comment>Hurry, my lawn is going wild!</comment>
<items>
<item partNum="872-AA">
<productName>Lawnmower</productName>
<quantity>1</quantity>
<USPrice>148.95</USPrice>
<comment>Confirm this is electric</comment>
</item>
<item partNum="926-AA">
<productName>Baby Monitor</productName>
<quantity>1</quantity>
<USPrice>39.98</USPrice>
<shipDate>1999-05-21</shipDate>
</item>
</items>
</purchaseOrder>
We will use Hibernate as JPA provider and HSQLDB as database
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
orbuild.xml
Maven users will recognize the usual Maven project structure:
-
src/main/java
andsrc/main/resources
- main java code and resources. -
src/test/java
andsrc/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:
ant clean install
Or:
mvn clean install
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.
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
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "PurchaseOrderType", propOrder = {
"shipTo",
"billTo",
"comment",
"items"
})
@Entity(name = "generated.PurchaseOrderType")
@Table(name = "PURCHASEORDERTYPE")
@Inheritance(strategy = InheritanceType.JOINED)
public class PurchaseOrderType implements Equals, HashCode
{
@XmlElement(required = true)
protected generated.USAddress shipTo;
@XmlElement(required = true)
protected generated.USAddress billTo;
protected String comment;
@XmlElement(required = true)
protected generated.Items items;
@XmlAttribute
@XmlSchemaType(name = "date")
protected XMLGregorianCalendar orderDate;
@XmlAttribute(name = "Hjid")
protected Long hjid;
@ManyToOne(targetEntity = generated.USAddress.class, cascade = {
CascadeType.ALL
})
@JoinColumn(name = "SHIPTO_PURCHASEORDERTYPE_ID")
public generated.USAddress getShipTo() {
return shipTo;
}
public void setShipTo(generated.USAddress value) {
this.shipTo = value;
}
@ManyToOne(targetEntity = generated.USAddress.class, cascade = {
CascadeType.ALL
})
@JoinColumn(name = "BILLTO_PURCHASEORDERTYPE_ID")
public generated.USAddress getBillTo() {
return billTo;
}
public void setBillTo(generated.USAddress value) {
this.billTo = value;
}
@Basic
@Column(name = "COMMENT_")
public String getComment() {
return comment;
}
public void setComment(String value) {
this.comment = value;
}
@ManyToOne(targetEntity = generated.Items.class, cascade = {
CascadeType.ALL
})
@JoinColumn(name = "ITEMS_PURCHASEORDERTYPE_ID")
public generated.Items getItems() {
return items;
}
public void setItems(generated.Items value) {
this.items = value;
}
@Transient
public XMLGregorianCalendar getOrderDate() {
return orderDate;
}
public void setOrderDate(XMLGregorianCalendar value) {
this.orderDate = value;
}
@Id
@Column(name = "HJID")
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getHjid() {
return hjid;
}
public void setHjid(Long value) {
this.hjid = value;
}
@Basic
@Column(name = "ORDERDATEITEM")
@Temporal(TemporalType.DATE)
public Date getOrderDateItem() {
return XmlAdapterUtils.unmarshall(XMLGregorianCalendarAsDate.class, this.getOrderDate());
}
public void setOrderDateItem(Date target) {
setOrderDate(XmlAdapterUtils.marshall(XMLGregorianCalendarAsDate.class, target));
}
public void equals(Object object, EqualsBuilder equalsBuilder) {
// ...
}
public boolean equals(Object object) {
// ...
}
public void hashCode(HashCodeBuilder hashCodeBuilder) {
// ...
}
public int hashCode() {
// ...
}
}
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
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence version="1.0" ... >
<persistence-unit name="generated">
<class>generated.Items</class>
<class>generated.Items$Item</class>
<class>generated.PurchaseOrderType</class>
<class>generated.USAddress</class>
</persistence-unit>
</persistence>
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.
This section illustrates basic JAXB operations: unmarshalling, marshalling and validating. Source code for the unit tests can be found here.
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):
private JAXBContext context;
private ObjectFactory objectFactory;
protected void setUp() throws Exception {
context = JAXBContext.newInstance("generated");
objectFactory = new ObjectFactory();
}
Let's start with unmarshalling. To unmarshall, we'll need an unmarshaller
first:
final Unmarshaller unmarshaller = context.createUnmarshaller();
Unmarshalling itself is just one line of code:
final Object object = unmarshaller.unmarshal(new File("src/test/samples/po.xml"));
Now just cast an check that unmarshalling really worked:
final PurchaseOrderType purchaseOrder = ((JAXBElement<PurchaseOrderType>) object).getValue();
assertEquals("Wrong city", "Mill Valley", purchaseOrder .getShipTo().getCity());
Marshalling is not more complex. First of all all, let's create a structure we want to marshal:
final PurchaseOrderType purchaseOrder = objectFactory.createPurchaseOrderType();
purchaseOrder.setShipTo(objectFactory.createUSAddress());
purchaseOrder.getShipTo().setCity("New Orleans");
final JAXBElement<PurchaseOrderType> purchaseOrderElement = objectFactory
.createPurchaseOrder(purchaseOrder);
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:
final Marshaller marshaller = context.createMarshaller();
final DOMResult result = new DOMResult();
marshaller.marshal(purchaseOrderElement, result);
Finally, let's check we've got what we wanted:
final XPathFactory xPathFactory = XPathFactory.newInstance();
assertEquals("Wrong city", "New Orleans",
xPathFactory.newXPath().evaluate("/purchaseOrder/shipTo/city", result.getNode()));
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:
final PurchaseOrderType purchaseOrder = objectFactory.createPurchaseOrderType();
purchaseOrder.setShipTo(objectFactory.createUSAddress());
purchaseOrder.setBillTo(objectFactory.createUSAddress());
final JAXBElement<PurchaseOrderType> purchaseOrderElement =
objectFactory.createPurchaseOrder(purchaseOrder);
Next step is creating an instance of schema:
final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
final Schema schema = schemaFactory.newSchema(new StreamSource(
getClass().getClassLoader().getResourceAsStream("po.xsd")));
Now all you need to do to validate on marshal is to provide the created schema to the marshaller:
final Marshaller marshaller = context.createMarshaller();
marshaller.setSchema(schema);
To receive notifications of validation events, register an appropriate event handler:
final List<ValidationEvent> events = new LinkedList<ValidationEvent>();
marshaller.setEventHandler(new ValidationEventHandler() {
public boolean handleEvent(ValidationEvent event) {
events.add(event);
return true;
}
});
Since out structure is invalid, we may check that the the list of validation events is not empty:
marshaller.marshal(purchaseOrderElement, new DOMResult());
assertFalse("List of validation events must not be empty.", events.isEmpty());
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.
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.
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(...)
:
private EntityManagerFactory entityManagerFactory;
public void setUp() throws Exception {
entityManagerFactory = Persistence.createEntityManagerFactory("generated");
}
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:
final Properties persistenceProperties = new Properties();
InputStream is = null;
try {
is = getClass().getClassLoader().getResourceAsStream(
"persistence.properties");
persistenceProperties.load(is);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ignored) {
}
}
}
entityManagerFactory = Persistence.createEntityManagerFactory(
"generated", persistenceProperties);
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
hibernate.dialect=org.hibernate.dialect.HSQLDialect
hibernate.connection.driver_class=org.hsqldb.jdbcDriver
hibernate.connection.username=sa
hibernate.connection.password=
hibernate.connection.url=jdbc:hsqldb:target/test-database/database
hibernate.hbm2ddl.auto=create-drop
hibernate.cache.provider_class=org.hibernate.cache.HashtableCacheProvider
hibernate.jdbc.batch_size=0
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.
First let's create an object structure to test with:
final PurchaseOrderType alpha = objectFactory.createPurchaseOrderType();
alpha.setShipTo(objectFactory.createUSAddress());
alpha.getShipTo().setCity("Sacramento");
To save out object into the database we'll need o get an entity manager from the factory:
final EntityManager saveManager = entityManagerFactory
.createEntityManager();
saveManager.getTransaction().begin();
saveManager.persist(alpha);
saveManager.getTransaction().commit();
saveManager.close();
After the object is saved, we can get the generated id:
final Long id = alpha.getHjid();
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:
final EntityManager loadManager = entityManagerFactory
.createEntityManager();
final PurchaseOrderType beta = loadManager.find(
PurchaseOrderType.class, id);
loadManager.close();
// Check that we're still shipping to Sacramento
assertEquals("Sacramento", beta.getShipTo().getCity());
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:
final Unmarshaller unmarshaller = context.createUnmarshaller();
final Object object = unmarshaller.unmarshal(new File(
"src/test/samples/po.xml"));
final PurchaseOrderType alpha =
((JAXBElement<PurchaseOrderType>) object).getValue();
Persist:
final EntityManager saveManager = entityManagerFactory
.createEntityManager();
saveManager.getTransaction().begin();
saveManager.persist(alpha);
saveManager.getTransaction().commit();
saveManager.close();
final Long id = alpha.getHjid();
Load:
final EntityManager loadManager = entityManagerFactory
.createEntityManager();
final PurchaseOrderType beta = loadManager.find(
PurchaseOrderType.class, id);
assertEquals("Objects are not equal.", alpha, beta);
Marshal:
final Marshaller marshaller = context.createMarshaller();
marshaller.marshal(objectFactory.createPurchaseOrder(beta), System.out);
loadManager.close();
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.
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
create table ITEM(
HJID bigint generated by default as identity (start with 1),
USPRICE numeric,
COMMENT_ varchar(255),
PARTNUM varchar(255),
PRODUCTNAME varchar(255),
QUANTITY integer,
SHIPDATEITEM date,
ITEM_ITEMS_ID bigint,
primary key (HJID))
create table ITEMS (
HJID bigint generated by default as identity (start with 1),
primary key (HJID))
create table PURCHASEORDERTYPE (
HJID bigint generated by default as identity (start with 1),
COMMENT_ varchar(255),
ORDERDATEITEM date,
BILLTO_PURCHASEORDERTYPE_ID bigint,
ITEMS_PURCHASEORDERTYPE_ID bigint,
SHIPTO_PURCHASEORDERTYPE_ID bigint,
primary key (HJID))
create table USADDRESS (
HJID bigint generated by default as identity (start with 1),
CITY varchar(255),
COUNTRY varchar(255),
NAME_ varchar(255),
STATE_ varchar(255),
STREET varchar(255),
ZIP numeric,
primary key (HJID))
alter table ITEM
add constraint FK2273132BB7C427
foreign key (ITEM_ITEMS_ID) references ITEMS
alter table PURCHASEORDERTYPE
add constraint FKB58DEB8716608DE7
foreign key (BILLTO_PURCHASEORDERTYPE_ID) references USADDRESS
alter table PURCHASEORDERTYPE
add constraint FKB58DEB87465EAC12
foreign key (SHIPTO_PURCHASEORDERTYPE_ID) references USADDRESS
alter table PURCHASEORDERTYPE
add constraint FKB58DEB8783F81253
foreign key (ITEMS_PURCHASEORDERTYPE_ID) references ITEMS
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 |
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.
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
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:hj="http://hyperjaxb3.jvnet.org/ejb/schemas/customizations"
xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
jaxb:extensionBindingPrefixes="hj orm">
<!-- ... -->
</xsd:schema>
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:
<xsd:complexType name="Items">
<!-- ... -->
<xsd:element name="productName" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<hj:basic>
<orm:column length="1024"/>
</hj:basic>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<!-- ... -->
</xsd:complexType>
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
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jaxb:bindings
version="2.1"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:hj="http://hyperjaxb3.jvnet.org/ejb/schemas/customizations"
xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
jaxb:extensionBindingPrefixes="hj orm">
<jaxb:bindings schemaLocation="po.xsd" node="/xs:schema">
<jaxb:schemaBindings>
<jaxb:package name="org.jvnet.hyperjaxb3.ejb.tutorials.po.steptwo"/>
</jaxb:schemaBindings>
<jaxb:bindings node="xs:complexType[@name='PurchaseOrderType']">
<hj:entity>
<orm:table name="po"/>
</hj:entity>
</jaxb:bindings>
<jaxb:bindings node="xs:complexType[@name='USAddress']">
<hj:entity>
<orm:table name="address"/>
</hj:entity>
</jaxb:bindings>
<jaxb:bindings node="xs:complexType[@name='Items']//xs:complexType">
<hj:entity>
<orm:table name="item"/>
</hj:entity>
</jaxb:bindings>
<jaxb:bindings node="xs:complexType[@name='Items']//xs:attribute[@name='partNum']">
<hj:id merge="false"/>
</jaxb:bindings>
<jaxb:bindings node="xs:complexType[@name='Items']//xs:element[@name='USPrice']">
<hj:basic>
<orm:column name="Price"/>
</hj:basic>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
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:
<jaxb:bindings schemaLocation="po.xsd" node="/xs:schema">
<!-- ... -->
<jaxb:bindings node="xs:complexType[@name='USAddress']">
<hj:entity>
<orm:table name="address"/>
</hj:entity>
</jaxb:bindings>
<!-- ... -->
</jaxb:bindings>
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:
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:hj="http://hyperjaxb3.jvnet.org/ejb/schemas/customizations"
xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
jaxb:extensionBindingPrefixes="hj orm">
<!-- ... -->
<xsd:complexType name="USAddress">
<xsd:annotation>
<xsd:appinfo>
<hj:entity>
<orm:table name="address"/>
</hj:entity>
</xsd:appinfo>
</xsd:annotation>
<!-- ... -->
</xsd:complexType>
<!-- ... -->
</xsd:schema>
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
create table ITEMS (
HJID bigint generated by default as identity (start with 1),
primary key (HJID))
create table address (
HJID bigint generated by default as identity (start with 1),
CITY varchar(255),
COUNTRY varchar(255),
NAME_ varchar(255),
STATE_ varchar(255),
STREET varchar(255),
ZIP numeric, primary key (HJID))
create table item (
PARTNUM varchar(255) not null,
Price numeric,
COMMENT_ varchar(255),
PRODUCTNAME varchar(1024),
QUANTITY integer,
SHIPDATEITEM date,
ITEM_ITEMS_ID bigint,
primary key (PARTNUM))
create table po (
HJID bigint generated by default as identity (start with 1),
COMMENT_ varchar(255),
ORDERDATEITEM date,
BILLTO_PURCHASEORDERTYPE_ID bigint,
ITEMS_PURCHASEORDERTYPE_ID bigint,
SHIPTO_PURCHASEORDERTYPE_ID bigint,
primary key (HJID))
...
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:
@OneToMany(targetEntity = Items.Item.class, cascade = {
CascadeType.ALL
})
@JoinColumn(name = "ITEM_ITEMS_HJID")
public List<Items.Item> getItem() { ... }
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
:
<jaxb:bindings schemaLocation="po.xsd" node="/xs:schema">
<!-- ... -->
<hj:persistence>
<hj:default-one-to-many>
<orm:join-table/>
</hj:default-one-to-many>
</hj:persistence>
<!-- ... -->
</jaxb:bindings>
Check the customization guide and the reference for further information on customizations.
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 [email protected].
You can find the source code for this tutorial in SVN:
- Step one - initial schema.
- Step two - customized schema.
- Step three - TopLink customizations.
{width="8" height="8"} po.xsd (application/octet-stream) {width="8" height="8"} po.xml (text/xml)