Skip to content

HyperJAXB3 Reference Single Page

Laurent Schoelens edited this page May 17, 2024 · 6 revisions

WARNING

This is a dropin from old and offline highsource's confluence page that may need some adaptation with latest work and may contain links to web.archive.org

Hyperjaxb3 reference is work in progress.

Introduction

If you already use JAXB, you know how much easier does this great tool make the XML processing in Java. Few lines of code and your XML is turned into an object stucture which is much suitable for Java code than, for instance, DOM or SAX. And you can always turn your object structure back to XML with almoust zero effort.

Generally speaking, there are two principal alternatives for using JAXB in your applications:

  • You may start with existing classes and annotate them JAXB annotations.
  • You may also start with an XML schema and use the schema compiler (XJC) to compile it into a set of schema-derived classes.

Although the first method is very useful if you want to XMLize existing class hierarchies, the real power of JAXB lies in the second, schema-driven approach. In this case you do not write the code manually - you just provide the XML schema and let JAXB generate the code for you. Schema compiler will analyze the schema and generate the sources of Java classes that represent the XML content.

These classes are called schema-derived classes. They are just normal POJOs (well, almost) and therefore quite easy to handle in Java code. At the same time schema-derived classes contain all the required JAXB annotations (generated by the schema compiler).

Hyperjaxb3 takes you one step further. It makes schema-derived classes persistable with JPA (Java Persistence API), allowing for storing and querying your JAXB objects (instances of schema-derived classes) in a relational database.

Now, if consider JAXB implementing the XML <> objects connection and Hyperjaxb3 providing relational persistence (objects <> RDB), you get the full XML <> objects <> RDB roundtrip.

Mission statement

The goal of Hyperjaxb3 is to provide relational persistence for schema-derived classes. The principal task of Hyperjaxb3 is to turn JAXB schema-derived classes into JPA entity classes so that JPA layer could persist them in a relational database.

In order to achieve this goal, Hyperjaxb3 implements a plugin for JAXB schema compiler which augments modifies or customizes the generated code so that the produced schema-derived classes become valid persistent entity classes.

Tasks solved by Hyperjaxb3

In order to make schema-derived classes persistable with JPA, Hyperjaxb3 must ensure that they fulfill requirements on the entity class as defined in the JPA specificaltion (see section 2.1). Moreover, to expose generated entities as a persistence unit, Hyperjaxb3 must generate additional files needed for entity packaging (namely the META-INF/persistence.xml file) as described in the Chapter 6 of the JPA specification.

More specifically, to turn schema-derived classes into JPA entities, Hyperjaxb3 solves the following tasks:

The following sections of this reference describe how Hyperjaxb3 solves each of these tasks in particular.

Features

Compatibility

JDK compatibility

JAXB compatibility

JPA compatibility

Hyperjaxb3 supports standard JPA. Both JPA 1 and JPA 2 (since 0.5.6) are supported.

Database compatilibility

Build system compatibility

Customizations

Hyperjaxb3 is a highly customizable tool. Almost everything Hyperjaxb3 generates can be customized using special customization elements - either directly in schema or in external binding files (*.xjb).

Customizations are purely optional. You can use them if you want to get certain specific result, but you don't have to if you don't need it. In this case Hyperjaxb3 will apply the default generation strategies.

Please refer to the customization guide for documentation on customizations.

Extensions

Hyperjaxb3 customizations are very powerful, but still they can not cover all the potential custom cases. However, you can customize Hyperjaxb3 code generation on a deeper level - by writing your own extensions.

Hyperjaxb3 makes heavy use of the strategy pattern. In fact, Hyperjaxb3 is composed from a number of small strategies, each solving its own task. These strategies are configured in a Spring application context. If you want to extend Hyperjaxb3, you can write your own strategy implementations and configure them in the application context (possibly overriding default strategies).

Please refer to the extension guide for more information.

Making schema-dervied classes ready for JPA

Making schema-derived classes serializable

According to the Java Persistence API specification, section 2.1, entities must implement the serializable interface:

If an entity instance is to be passed by value as a detached object (e.g., through a remote interface), the entity class must implement the Serializable interface.

Hyperjaxb2 itself does not enforce serializable classes generation, you should use the jaxb:serializable global bindings customization element to make your model serializable:

bindings.xjb

<jaxb:bindings version="1.0"
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     
    <jaxb:bindings schemaLocation="schema.xsd" node="/xsd:schema">
        <jaxb:globalBindings localScoping="toplevel">
            <jaxb:serializable/>
        </jaxb:globalBindings>
    </jaxb:bindings>
</jaxb:bindings>

Enforcing top-level classes

According to the Java Persistence API specification, section 2.1, entities must be top-level classes:

> The entity class must be a top-level class.

Hyperjaxb3 on its own does not (and technically cannot) enforce top-level class generation. You will need to use a JAXB customization to allow this:

schema.xsd

<?xml version="1.0"?>
<xs:schema
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb">
 
    <xs:annotation>
        <xs:appinfo>
            <jaxb:globalBindings generateIsSetMethod="true" localScoping="toplevel">
                <jaxb:serializable/>
            </jaxb:globalBindings>
        </xs:appinfo>
    </xs:annotation>
 
    <!-- ... -->
</xs:schema>

Generating equals(...) and hashCode() methods

Generating equals(...) and hashCode() methods

Although this is not directly required by the JPA specification, it is still recommended to have correct implementations of equals(...) and hashCode() methods in entity classes. JAXB uses the equals and hashCode plugins from JAXB2 Basics Plugins plugin package to generate these methods.

These plugins generate equals(...) and hashCode() methods. With Hyperjaxb3 you can configure, which equals and hash code strategies will be used TODO (default implementations are org.jvnet.jaxb2_commons.lang.JAXBEqualsStrategy and org.jvnet.jaxb2_commons.lang.JAXBHashCodeStrategy).

Alternatively you can turn equals(...) and hashCode() generation off TODO.

Applying workaround for JAXB vs. JPA conflicts

As you might have read in the JAXB vs. JPA document, there is a number of incompatibilities between JAXB and JPA 1. This section explains how Hyperjaxb3 overcomes these incompatibilities.

Simple types not supported by JPA

Collections of simple types

Heterogeneous collections

JAXBElement<...>-typed properties

DOM properties

Generic (any-type) properties

JPA 2 support

Hyperjaxb3 supports JPA 2 since version 0.5.6.

Generating JPA 2 metadata

Use the jpa2 variant to generate JPA 2 annotations:

Maven:

<plugin>
    <groupId>org.jvnet.hyperjaxb3</groupId>
    <artifactId>maven-hyperjaxb3-plugin</artifactId>
    <configuration>
        <variant>jpa2</variant>
    </configuration>
</plugin>

Ant:

<xjc destdir="${basedir}/target/generated-sources/xjc" extension="true">
    <arg line="
        -Xhyperjaxb3-jpa2
        -Xhyperjaxb3-jpa2-roundtripTestClassName=RoundtripTest
        -Xequals
        -Xinheritance
        -XhashCode
        -XtoString"/>
    <binding dir="${basedir}/src/main/resources">
        <include name="**/*.xjb"/>
    </binding>
    <schema dir="${basedir}/src/main/resources">
        <include name="**/*.xsd"/>
    </schema>
    <classpath>
        <fileset dir="${basedir}/lib">
            <include name="*.jar"/>
        </fileset>
    </classpath>
</xjc>

Hyperjaxb3 can generate JPA 2 annotations as well as XML mapping files according to the JPA 2 according to the JPA 2 orm_2_0.xsd XML schema. Use mappingFiles or -Xhyperjaxb3-jpa2-result=mappingFiles configuration items accordingly.

Supported JPA 2 features

Collections of simple types

JPA 2 supports collections of simple types using the @ElementCollection annotation.
Hyperjaxb3 uses this feature to improve mapping of the repeatable simple type elements.

Example:

<xsd:complexType name="HJIII-63-Alpha">
    <xsd:sequence>
        <xsd:element name="stringElements" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
        <xsd:element name="dateElements" type="xsd:date" minOccurs="0" maxOccurs="unbounded"/>
    </xsd:sequence>
</xsd:complexType>

Generates:

@ElementCollection
@OrderColumn(name = "HJINDEX")
@Column(name = "HJVALUE", length = 255)
@CollectionTable(name = "HJIII63ALPHA_STRINGELEMENTS", joinColumns = {
    @JoinColumn(name = "HJID")
})
public List<String> getStringElements() { ... }
 
 
@ElementCollection
@OrderColumn(name = "HJINDEX")
@Column(name = "HJVALUE", precision = 20, scale = 10)
@CollectionTable(name = "HJIII63ALPHA_DECIMALELEMENTS", joinColumns = {
    @JoinColumn(name = "HJID")
})
public List<BigDecimal> getDecimalElements() { ... }

Improved embedding

In JPA 2, @Embeddable mechanism is much more powerful when compared to to JPA 1. Embeddable classes can now have complex properties, associations, collections - and even nested embeddables.
Hyperjaxb3 now supportes @Embeddable features.

Example:

<xs:element name="unit" type="UnitType"/>
 
<xs:complexType name="UnitType">
    <xs:sequence>
        <xs:element name="head" type="PersonType"/>
        <xs:element name="address" type="AddressType"/>
    </xs:sequence>
</xs:complexType>  
 
<xs:complexType name="PersonType">
    <xs:annotation>
        <xs:appinfo>
            <hj:embeddable/>
        </xs:appinfo>
    </xs:annotation>
    <xs:sequence>
        <xs:element name="name" type="xs:string"/>
        <xs:element name="address" type="AddressType"/>
    </xs:sequence>
</xs:complexType>  
 
<xs:complexType name="AddressType">
    <xs:annotation>
        <xs:appinfo>
            <hj:embeddable/>
        </xs:appinfo>
    </xs:annotation>
    <xs:sequence>
        <xs:element name="street" type="xs:string"/>
        <xs:element name="city" type="xs:string"/>
        <xs:element name="state" type="xs:string" minOccurs="0"/>
        <xs:element name="zip" type="xs:string"/>
        <xs:element name="country" type="xs:string"/>
    </xs:sequence>
</xs:complexType> 

Note that both PersonType as well as AddressType are embeddable. Moreover, PersonType has a nested embeddable (address).

Generates:

@Embedded
@AttributeOverrides({
    @AttributeOverride(name = "name", column = @Column(name = "HEAD_NAME", length = 255)),
    @AttributeOverride(name = "address.street", column = @Column(name = "HEAD_ADDRESS_STREET", length = 255)),
    @AttributeOverride(name = "address.city", column = @Column(name = "HEAD_ADDRESS_CITY", length = 255)),
    @AttributeOverride(name = "address.state", column = @Column(name = "HEAD_ADDRESS_STATE", length = 255)),
    @AttributeOverride(name = "address.zip", column = @Column(name = "HEAD_ADDRESS_ZIP", length = 255)),
    @AttributeOverride(name = "address.country", column = @Column(name = "HEAD_ADDRESS_COUNTRY", length = 255))
})
public PersonType getHead() { ... }
 
@Embedded
@AttributeOverrides({
    @AttributeOverride(name = "street", column = @Column(name = "ADDRESS_STREET", length = 255)),
    @AttributeOverride(name = "city", column = @Column(name = "ADDRESS_CITY", length = 255)),
    @AttributeOverride(name = "state", column = @Column(name = "ADDRESS_STATE", length = 255)),
    @AttributeOverride(name = "zip", column = @Column(name = "ADDRESS_ZIP", length = 255)),
    @AttributeOverride(name = "country", column = @Column(name = "ADDRESS_COUNTRY", length = 255))
})
public AddressType getAddress() { ... }

Here's how it looks like in the database:

create table UNITTYPE (
    HJID bigint generated by default as identity (start with 1),
    ADDRESS_CITY varchar(255),
    ADDRESS_COUNTRY varchar(255),
    ADDRESS_STATE varchar(255),
    ADDRESS_STREET varchar(255),
    ADDRESS_ZIP varchar(255),
    HEAD_ADDRESS_CITY varchar(255),
    HEAD_ADDRESS_COUNTRY varchar(255),
    HEAD_ADDRESS_STATE varchar(255),
    HEAD_ADDRESS_STREET varchar(255),
    HEAD_ADDRESS_ZIP varchar(255),
    HEAD_NAME varchar(255), primary key (HJID))

Note that column names for both embeddable AddressType fields do not collide.

Adding required properties

Adding identifier property

Support for composite primary keys

You can mark several properties of your class as identifier properties. In this case these properties will build up a composite primary key. Hyperjaxb3 detects declarations of composite primary keys and automatically generates the required identifier class.

Consider the following schema snippet:

<xsd:complexType name="Parent">
    <xsd:sequence>
        <xsd:element name="children" type="Child" minOccurs="0" maxOccurs="unbounded" />
    </xsd:sequence>
    <xsd:attribute name="pk1" type="xsd:string">
        <xsd:annotation><xsd:appinfo><hj:id /></xsd:appinfo></xsd:annotation>
    </xsd:attribute>
    <xsd:attribute name="pk2" type="xsd:string">
        <xsd:annotation><xsd:appinfo><hj:id /></xsd:appinfo</xsd:annotation>
    </xsd:attribute>
    <xsd:attribute name="parentData" type="xsd:string"/>
</xsd:complexType>

This type declares two id properties (attributes pk1 and pk2). To map this composite primary key, HJ3 will generate a ParentId class:

parent.java

...
@Entity(name = "org.jvnet.hyperjaxb3.ejb.tests.ids.tests.Parent")
@Table(name = "PARENT")
@IdClass(Parent.ParentId.class)
@Inheritance(strategy = InheritanceType.JOINED)
public class Parent implements Equals, HashCode
{
    ...
 
    @Id
    @Column(name = "PK1")
    public String getPk1() { ... }
 
    ...
 
    @Id
    @Column(name = "PK2")
    public String getPk2() { ... }
 
    ...
 
    public static class ParentId implements Serializable, Equals, HashCode
    {
 
        protected String pk1;
 
        protected String pk2;
 
        public String getPk1() { ... }
 
        public void setPk1(String value) { ... }
 
        public String getPk2() { ... }
 
        public void setPk2(String value) { ... }
 
        public boolean equals(Object object) { ... }
 
        public int hashCode() { ... }
 
    }
}

Many thanks to Constantine Kulak who helped with this feature.

Support for cascading ids

Yet to be developed.

Generating ORM metadata

Generating annotations or mapping files

Please note that Hyperjaxb3 normalizes and collapses whitespace when generating string fields of annotations. Please see this issue for more information.

Mapping schema-derived classes

Mapping schema-derived classes as entities

By default, Hyperjaxb3 will map schema-derived classes as entities.

Schema fragment:

<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>

Produces:

@Entity(name = "PurchaseOrderType")
@Table(name = "PURCHASEORDERTYPE")
@Inheritance(strategy = InheritanceType.JOINED)
public class PurchaseOrderType
    implements Equals, HashCode
{ ... }

Entity name is typically the local name of the class and table name is derived from the class name. You can customize both (and much more) using the customization element hj:entity:

<jaxb:bindings node="xs:complexType[@name='PurchaseOrderType']">
    <hj:entity name="PO">
        <orm:table name="PO"/>
    </hj:entity>
</jaxb:bindings>

Produces:

@Entity(name = "PO")
@Table(name = "PO")
@Inheritance(strategy = InheritanceType.JOINED)
public class PurchaseOrderType
    implements Equals, HashCode
{ ... }

For better compatibility, entity names should not contain dots. Prior to version 0.5.5 HJ3 used fully qualified class names as entity names. It worked fine with Hibernate but caused problems in other persistence providers.

Mapping schema-derived classes as mapped superclasses

TODO

Mapping schema-derived classes as embeddable

JPA provides support for embeddable classes which represent entity state but do not have persistent identity of their own. You may instruct Hyperjaxb3 to map your schema-derived class as embeddable class by using the hj:embeddable customization element:

<xs:complexType name="PrimaryPhoneType">
    <xs:annotation>
        <xs:appinfo>
            <hj:embeddable/>
        </xs:appinfo>
    </xs:annotation>
    <xs:sequence>
        <xs:element name="phoneNumber" type="xs:string" nillable="false"/>
        <xs:element name="phoneType" type="xs:string" nillable="false"/>
    </xs:sequence>
</xs:complexType>

JPA poses strict limitations on embeddable classes. In particular, only simple properties are allowed in embeddable classes. If your embeddable class has properties which are not supported in JPA, they will be made transient automatically.

In order to avoid name collisions, Hyperjaxb3 by default generates the attribute-override definitions and provides syntetic column names:

@Embedded
@AttributeOverrides({
    @AttributeOverride(name = "phoneNumber", column = @Column(name = "PRIMARYPHONEITEM_PHONENUMBER")),
    @AttributeOverride(name = "phoneType", column = @Column(name = "PRIMARYPHONEITEM_PHONETYPE"))
})
public PrimaryPhoneType getPrimaryPhoneItem() { ... }

Single property which references an embeddable class will can be mapped as embedded (default) or embedded-id.

Ignoring schema-derived classes

If you don't want to map a certain class, you can annotate it with <hj:ignored/>:

<xs:complexType name="MyType">
    <xs:annotation>
        <xs:appinfo>
            <hj:ignored/>
        </xs:appinfo>
    </xs:annotation>
    <!-- ... -->
</xs:complexType>

Ignoring packages

Since 0.5.5.

In certain cases you may want to exclude whole packages from mapping. Here's how to do it:

schema-ignored.xsd

<xsd:schema ...
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:hj="http://hyperjaxb3.jvnet.org/ejb/schemas/customizations"
 
    jaxb:extensionBindingPrefixes="hj">
 
    <xsd:annotation>
        <xsd:appinfo>
            <jaxb:schemaBindings>
                <jaxb:package name="org.jvnet.hyperjaxb3.ejb.tests.issuesignored"/>
            </jaxb:schemaBindings>
            <hj:ignored-package name="org.jvnet.hyperjaxb3.ejb.tests.issuesignored"/>
        </xsd:appinfo>
    </xsd:annotation>
 
    <!-- ... -->
 
</xsd:schema>

You can do the same in bindings file:

<!-- ... -->
<jaxb:bindings schemaLocation="schema-ignored.xsd" node="/xsd:schema">
    <jaxb:schemaBindings>
        <jaxb:package name="org.jvnet.hyperjaxb3.ejb.tests.issuesignored"/>
    </jaxb:schemaBindings>
    <hj:ignored-package name="org.jvnet.hyperjaxb3.ejb.tests.issuesignored"/>
</jaxb:bindings>
<!-- ... -->

Due to certain techical limitations you need to specify the package name in hj:ignored-package/@name.

Mapping properties

Taxonomy of properties

Cardinality
Type
Homogenity, heterogenity
Element reference properties

Ignoring properties

Identifier property

JPA requires entities to have primary keys. To satisfy this requirement, you can select an identifier property using a customization or let Hyperjaxb3 generate an identifier property for you. Below is the identifier property which will be generated by default:

@XmlAttribute(name = "Hjid")
protected Long hjid;
 
@Id
@Column(name = "HJID")
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getHjid() {
    return hjid;
}
 
public void setHjid(Long value) {
    this.hjid = value;
}

You still can customize the identifier property generated for a certain entity or even by default for all entities.

Version property

Version properties are optional in JPA, but they are very useful for handling optimistic locking. You can select one of your properties as the version property using the customization. If you don't have a suitable property, you may also customize Hyperjaxb3 to generate a version property for you:

<xsd:complexType name="MyType">
    <xsd:annotation>
        <xsd:appinfo>
            <hj:generated-version/>
        </xsd:appinfo>
    </xsd:annotation>
    <xsd:sequence>
        <xsd:element name="value" type="xsd:string" minOccurs="0"/>
    </xsd:sequence>
</xsd:complexType> 

Here's what will be generated:

@Version
@Column(name = "HJVERSION")
public Long getHjversion() {
    return hjversion;
}
 
public void setHjversion(Long value) {
    this.hjversion = value;
}

You can customize the version property generated for a certain entity or by default for all entities.

Single property

Basic single property

Simple-typed single properties are mapped as @Basic properties:

<xs:complexType name="simpleTypesType">
    <xs:sequence>
        <xs:element name="string" type="xs:string" minOccurs="0"/>
    </xs:sequence>
</xs:complexType>
protected String string;
 
@Basic
@Column(name = "STRING", length = 255)
public String getString() {
    return string;
}
public void setString(String value) {
    this.string = value;
}

If Java type of the simple property is not supported by JPA, such properties will be wrapped:

<xs:element name="duration" type="xs:duration" minOccurs="0"/>
protected Duration duration;
@Transient
public Duration getDuration() {
    return duration;
}
 
public void setDuration(Duration value) {
    this.duration = value;
}
 
@Basic
@Column(name = "DURATIONITEM", length = 127)
public String getDurationItem() {
    return XmlAdapterUtils.unmarshall(DurationAsString.class, this.getDuration());
}
 
public void setDurationItem(String target) {
    setDuration(XmlAdapterUtils.marshall(DurationAsString.class, target));
}
Considering XML Schema facets

Hyperjaxb3 considers XML Schema facets when generating length, scale and precision:

<xsd:simpleType name="digits">
    <xsd:restriction base="xsd:decimal">
        <xsd:totalDigits value="5"/>
        <xsd:fractionDigits value="2"/>
    </xsd:restriction>
</xsd:simpleType>
 
<xsd:complexType ...>
    <xsd:sequence>
        <xsd:element name="digits" minOccurs="0" type="test:digits"/>
    </xsd:sequence>
</xsd:complexType>
protected BigDecimal digits;
@Basic
@Column(name = "DIGITS", precision = 5, scale = 2)
public BigDecimal getDigits() {
    return digits;
}
 
public void setDigits(BigDecimal value) {
    this.digits = value;
}

Hyperjaxb3 considers the following facets:

  • xsd:minLength
  • xsd:maxLength
  • xsd:length
  • xsd:totalDigits
  • xsd:fractionDigits
Temporal property

Temporal properties (typed xsd:dateTime, xsd:date, xsd:time and so on) will be mapped as temporal JPA properties. Hyperjaxb3 will choose temporal type as TIMESTAMP, DATE or TIME depending on the XML Schema type of the temporal property.

Temporal properties are typically mapped onto XMLGregorianCalendar which is not supported by JPA - and therefor must be wrapped:

<xs:element name="dateTime" type="xs:dateTime" minOccurs="0"/>
@XmlSchemaType(name = "dateTime")
protected XMLGregorianCalendar dateTime;
@Transient
public XMLGregorianCalendar getDateTime() {
    return dateTime;
}
public void setDateTime(XMLGregorianCalendar value) {
    this.dateTime = value;
}
@Basic
@Column(name = "DATETIMEITEM")
@Temporal(TemporalType.TIMESTAMP)
public Date getDateTimeItem() {
    return XmlAdapterUtils.unmarshall(XMLGregorianCalendarAsDateTime.class, this.getDateTime());
}
 
public void setDateTimeItem(Date target) {
    setDateTime(XmlAdapterUtils.marshall(XMLGregorianCalendarAsDateTime.class, target));
}
Enumerated property

Enumerated properties are typically mapped as enumerated JPA properties:

    <xsd:simpleType name="SexType">
        <xsd:restriction base="xsd:string">
            <xsd:enumeration value="Male"/>
            <xsd:enumeration value="Female"/>
        </xsd:restriction>
    </xsd:simpleType>
 
...
    <xsd:element name="sex" type="test:SexType"/>
...
@XmlElement
protected SexType sex;
 
@Basic
@Column(name = "SEX")
@Enumerated(EnumType.STRING)
public SexType getSex() {
    return sex;
}
 
public void setSex(SexType value) {
    this.sex = value;
}

You can also map the enumerated property as value (instead of enumeration):

<xsd:element name="sex" type="test:SexType">
    <xsd:annotation>
        <xsd:appinfo>
            <hj:basic>
                <hj:enumerated-value/>
            </hj:basic>
        </xsd:appinfo>
    </xsd:annotation>
</xsd:element>
@XmlElement(required = true)
protected SexType sex;
 
@Transient
public SexType getSex() {
    return sex;
}
 
public void setSex(SexType value) {
    this.sex = value;
}
 
@Basic
@Column(name = "SEXITEM")
public String getSexItem() {
    return ((this.getSex() == null)?null:this.getSex().value());
}
 
public void setSexItem(String target) {
    setSex(((target == null)?null:SexType.fromValue(target)));
}
LOB property

Binary properties (xsd:hexBinary, xsd:base64Binary) will be mapped as LOB properties:

<xs:element name="base64Binary" type="xs:base64Binary" minOccurs="0"/>
protected byte[] base64Binary;
 
@Basic
@Column(name = "BASE64BINARY")
@Lob
public byte[] getBase64Binary() {
    return base64Binary;
}
public void setBase64Binary(byte[] value) {
    this.base64Binary = ((byte[]) value);
}
DOM property

Generic properties represented in the Java model by DOM elements will be wrapped and mapped as String (XML representation of the DOM element):

<xsd:complexType ...>
    <xsd:sequence>
        <xsd:any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="1"/>
    </xsd:sequence>
</xsd:complexType>
@XmlAnyElement
protected Element any;
 
@Transient
public Element getAny() {
    return any;
}
 
public void setAny(Element value) {
    this.any = value;
}
 
@Basic
@Column(name = "ANYITEM")
@Lob
public String getAnyItem() {
    return XmlAdapterUtils.unmarshall(ElementAsString.class, this.getAny());
}
 
public void setAnyItem(String target) {
    setAny(XmlAdapterUtils.marshall(ElementAsString.class, target));
}
Generic ("any"-type) property

A further JAXB feature is generic or any-type properties like those generated from xsd:anyType-typed elements or xsd:any with strict processing:

<xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="1"/>

Turns into:

@XmlAnyElement(lax = true)
protected Object any;
 
public Object getAny() {
    return any;
}
 
public void setAny(Object value) {
    this.any = value;
}

JPA can't handle these generic properties. Hyperjaxb3's approach to handle this scenario is to adapt the property by marshalling its contents into string in getter and unmarshalling the string in setter. By default, Hyperjaxb3 uses the context path of the currently processed schema (or schemas).

public final static String AnyObjectContextPath = "org.jvnet.hyperjaxb3.ejb.tests.any";
 
@Basic
@Column(name = "AnyObject")
public String getAnyObject() {
    if (JAXBContextUtils.isMarshallable(AnyObjectContextPath , this.getAny())) {
        return JAXBContextUtils.marshal(AnyObjectContextPath , this.getAny());
    } else {
        return null;
    }
}
 
public void setAnyObject(String target) {
    if (target!= null) {
        setAny(JAXBContextUtils.unmarshal(AnyObjectContextPath , target));
    }
}

The approach presented above is quite similar to the way how the DOM elements are processed. The only difference is that instead of using XML parsing and serialization, contents of the property are marshalled/unmarshalled with JAXB context.

You can use the hj:generated-property customization element to customize the generated property or hj:jaxb-context to customize the path of the JAXB context used for marshalling/unmarshalling.

Complex single property
Mapping as many-to-one

By default complex single properties are mapped as @ManyToOne:

<xs:element name="single" type="complexType" minOccurs="0"/>
protected ComplexType single;
 
@ManyToOne(targetEntity = ComplexType.class, cascade = {
    CascadeType.ALL
})
@JoinColumn(name = "SINGLE_COMPLEXTYPESTYPE_HJID")
public ComplexType getSingle() {
    return single;
}
 
public void setSingle(ComplexType value) {
    this.single = value;
}
Mapping as one-to-one

You can use the hj:one-to-one customization element to map your complex single property as @OneToOne instead of @ManyToOne:

<xs:element name="one-to-one-join-column" type="test:three" minOccurs="0">
    <xs:annotation>
        <xs:appinfo>
            <hj:one-to-one>
                <orm:join-column name="O2OJC_THREE_ID"/>
            </hj:one-to-one>
        </xs:appinfo>
    </xs:annotation>
</xs:element>
@XmlElement(name = "one-to-one-join-column")
protected Three oneToOneJoinColumn;
 
@OneToOne(targetEntity = Three.class, cascade = {
    CascadeType.ALL
})
@JoinColumn(name = "O2OJC_THREE_ID")
public Three getOneToOneJoinColumn() {
    return oneToOneJoinColumn;
}
 
public void setOneToOneJoinColumn(Three value) {
    this.oneToOneJoinColumn = value;
}
Mapping as embedded

If the target class is made embeddable, the complex single property can be mapped either as embedded or as embedded-id property.

Consider the following example:

<xs:complexType name="PrimaryPhoneType">
    <xs:annotation>
        <xs:appinfo>
            <hj:embeddable/>
        </xs:appinfo>
    </xs:annotation>
    <xs:sequence>
        <xs:element name="phoneNumber" type="xs:string" nillable="false"/>
        <xs:element name="phoneType" type="xs:string" nillable="false"/>
    </xs:sequence>
</xs:complexType>

By default, complex single properties referencing the PrimaryPhoneType will be automatically turned mapped as embedded. You can further customize this mapping using hj:embedded customization element. For instance, you can specify orm::attribute-override elements to override column mappings:

<xs:complexType name="EmployeeType">
    <xs:sequence>
        <!-- ... -->
        <xs:element name="primaryPhone" nillable="true" minOccurs="0" type="PrimaryPhoneType">
            <xs:annotation>
                <xs:appinfo>
                    <hj:embedded>
                        <orm:attribute-override name="phoneNumber">
                            <orm:column name="phone_no"/>
                        </orm:attribute-override>
                        <orm:attribute-override name="phoneType">
                            <orm:column name="phone_type_id"/>
                        </orm:attribute-override>
                    </hj:embedded>
                </xs:appinfo>
            </xs:annotation>
        </xs:element>
    </xs:sequence>
</xs:complexType>
Mapping as embedded-id

Alternatively, you can map a complex single property which references an embeddable class as embedded-id. This is not a default mapping, you have to use the hj:embedded-id customization element to enable this:

<xs:complexType name="embeddableIdType">
    <xs:annotation>
        <xs:appinfo>
            <hj:embeddable/>
        </xs:appinfo>
    </xs:annotation>
    <xs:sequence>
        <xs:element name="id1" type="xs:long" minOccurs="0"/>
        <xs:element name="id2" type="xs:long" minOccurs="0"/>
    </xs:sequence>
</xs:complexType>
<xs:complexType name="entityWithEmbeddedIdType">
    <xs:sequence>
        <xs:element name="id" type="embeddableIdType">
            <xs:annotation>
                <xs:appinfo>
                    <hj:embedded-id/>
                </xs:appinfo>
            </xs:annotation>
        </xs:element>
    </xs:sequence>
</xs:complexType>

This will instruct HJ3 to generate an embedded-id property:

@EmbeddedId
@AttributeOverrides({
    @AttributeOverride(name = "id1", column = @Column(name = "ID_ID1")),
    @AttributeOverride(name = "id2", column = @Column(name = "ID_ID2"))
})
public EmbeddableIdType getId() { ... }

Accordingly, no further identifier properties will be generated in this case.

Many thanks to Joachim Björklund for implementing the embedded-id support.

Heterogeneous single property

TODO

Element reference property

TODO

Default mappings for single properties
Default mappings for simple single properties

Default mappings for simple properties are defined on the per-type basis in default customizations. These mappings are developed based on the XML Schema type hierarchy :

image

Below is the table describing default mappings for XML Schema build-in types:

#

Type

Mapping

3.2.1

xsd:string

<hj:basic><orm:column length="255"/></hj:basic>

3.2.3

xsd:decimal

<hj:basic><orm:column scale="10" precision="20"/></hj:basic>

3.2.4

xsd:float

<hj:basic><orm:column scale="10" precision="20"/></hj:basic>

3.2.5

xsd:double

<hj:basic><orm:column scale="10" precision="20"/></hj:basic>

3.2.6

xsd:duration

<hj:basic><orm:column length="127"/></hj:basic>

3.2.7

xsd:dateTime

<hj:basic><orm:temporal>TIMESTAMP</orm:temporal></hj:basic>

3.2.8

xsd:time

<hj:basic><orm:temporal>TIME</orm:temporal></hj:basic>

3.2.9

xsd:date

<hj:basic><orm:temporal>DATE</orm:temporal></hj:basic>

3.2.10

xsd:gYearMonth

<hj:basic><orm:temporal>DATE</orm:temporal></hj:basic>

3.2.11

xsd:gYear

<hj:basic><orm:temporal>DATE</orm:temporal></hj:basic>

3.2.12

xsd:gMonthDay

<hj:basic><orm:temporal>DATE</orm:temporal></hj:basic>

3.2.13

xsd:gDay

<hj:basic><orm:temporal>DATE</orm:temporal></hj:basic>

3.2.14

xsd:gMonth

<hj:basic><orm:temporal>DATE</orm:temporal></hj:basic>

3.2.15

xsd:hexBinary

<hj:basic><<orm:lob/></hj:basic>

3.2.16

xsd:base64Binary

<hj:basic><orm:lob/></hj:basic>

3.3.3

xsd:language

<hj:basic><orm:column length="17"/></hj:basic>

3.3.13

xsd:integer

<hj:basic><orm:column scale="0" precision="20"/></hj:basic>

3.3.16

xsd:long

<hj:basic><orm:column scale="0" precision="20"/></hj:basic>

3.3.17

xsd:int

<hj:basic><orm:column scale="0" precision="10"/></hj:basic>

3.3.18

xsd:short

<hj:basic><orm:column scale="0" precision="5"/></hj:basic>

3.3.19

xsd:byte

<hj:basic><orm:column scale="0" precision="3"/></hj:basic>

3.3.21

xsd:unsignedLong

<hj:basic><orm:column scale="0" precision="20"/></hj:basic>

3.3.22

xsd:unsignedInt

<hj:basic><orm:column scale="0" precision="10"/></hj:basic>

3.3.23

xsd:unsignedShort

<hj:basic><orm:column scale="0" precision="5"/></hj:basic>

3.3.24

xsd:unsignedByte

<hj:basic><orm:column scale="0" precision="3"/></hj:basic>

Collection property

Complex collection property
Complex basic property
Heterogeneous collection property

Generating the persistence unit descriptor

According to the entity packaging guidelines of the JPA specification, Hyperjaxb3 generates the META-INF/persistence.xml file which describes generated classes in a persistence unit. This allows easier usage of mapped classes - entity manager will pick up the classes automatically.

Hyperjaxb3 generates the persistence unit in the META-INF/persistence.xml file in the target directory. You may configure an alternative location TODO if you wish.

Persistence unit generated by Hyperjaxb3 simply lists classes which were made entities:

META-INF/persistence.xml

<persistence
    version="1.0"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/persistence">
    <persistence-unit name="org.jvnet.hyperjaxb3.ejb.tests.po">
        <class>org.jvnet.hyperjaxb3.ejb.tests.po.Items</class>
        <class>org.jvnet.hyperjaxb3.ejb.tests.po.Items$Item</class>
        <class>org.jvnet.hyperjaxb3.ejb.tests.po.USAddress</class>
        <class>org.jvnet.hyperjaxb3.ejb.tests.po.PurchaseOrderType</class>
    </persistence-unit>
</persistence>

Persistence unit in named after the package of the schema-derived classes. If there are several packages, they'll be colon-separated (just lice JAXB context path). You may also specify the name of the persistence unit expliciltly in configuration.

You may also turn off the generation of persistence.xml TODO.

Creating a custom persistence unit descriptor

Augmenting a provided persistence.xml

One of the ways to create a custom persistence unit descriptor is letting HJ3 inject its definitions into an existing persistence.xml template. To do this, you have to set the persistenceXml configuration element in Ant or Maven. Below is an example for Maven:

<plugin>
    <groupId>org.jvnet.hyperjaxb3</groupId>
    <artifactId>maven-hyperjaxb3-plugin</artifactId>
    <configuration>
        <persistenceXml>src/main/etc/persistence.xml</persistenceXml>
    </configuration>
</plugin>

The persistenceXml configuration element should point to the persistence.xml template:

src/main/etc/persistence.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence version="1.0"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/persistence">
    <persistence-unit name="##generated" transaction-type="RESOURCE_LOCAL">
    <mapping-file>org/jvnet/hyperjaxb3/ejb/tests/po/orm.xml</mapping-file>
    </persistence-unit>
</persistence>

In this case HJ3 will not create the persistence.xml file from scratch, instead it will parse the provided template and inject its definitions (class or mapping-file elements) into the right places:

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

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence version="1.0"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd
 http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
  xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://java.sun.com/xml/ns/persistence">
    <persistence-unit name="org.jvnet.hyperjaxb3.ejb.tests.po" transaction-type="RESOURCE_LOCAL">
        <mapping-file>org/jvnet/hyperjaxb3/ejb/tests/po/orm.xml</mapping-file>
        <class>org.jvnet.hyperjaxb3.ejb.tests.po.Items</class>
        <class>org.jvnet.hyperjaxb3.ejb.tests.po.Items$Item</class>
        <class>org.jvnet.hyperjaxb3.ejb.tests.po.PurchaseOrderType</class>
        <class>org.jvnet.hyperjaxb3.ejb.tests.po.USAddress</class>
    </persistence-unit>
</persistence>

Integrating Hyperjaxb3 in builds

Overview

Using Hyperjaxb3 plugin for XJC

Using Hyperjaxb3 in Ant builds

Using Hyperjaxb3 plugin in Maven builds

Using Hyperjaxb3 with maven-jaxb2-plugin

Using maven-hyperjaxb3-plugin

Generating the database schema

When building the project, you may also want to generate a database schema (DDL) for the persistence unit which was created by the Hyperjaxb3. This is done differently depending in your build tool and the persistence provider you use. The following sections illustrate some of the setups.

Using hbm2ddl from Hibernate Tools

Hibernate includes the Hibernate Tools sub-project which provides the hbm2ddl database schema exporter.

Maven usage

The easiest way of integrating hbm2ddl in your Maven builds is the Hibernate3 Maven Plugin. Here's a sample configuration:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>hibernate3-maven-plugin</artifactId>
    <executions>
        <execution>
            <phase>process-classes</phase>
            <goals>
                <goal>hbm2ddl</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <components>
            <component>
                <name>hbm2ddl</name>
                <implementation>jpaconfiguration</implementation>
                <outputDirectory>target/generated-resources/hbm2ddl</outputDirectory>
            </component>
        </components>
        <componentProperties>
            <!-- Name of the persistence unit -->
            <persistenceunit>com.acme.foo</persistenceunit>
            <!-- Hibernate properties -->
            <propertyfile>src/test/resources/persistence.properties</propertyfile>
            <outputfilename>schema.ddl</outputfilename>
            <drop>false</drop>
            <create>true</create>
            <export>false</export>
            <format>true</format>
        </componentProperties>
    </configuration>
</plugin>

It is important that this plugin is invoked, after the compile phase (for instance, in the process-classes phase) since it needs the compiled classes for schema generation.

Hibernate properties file must at least provide the dialect:

persistence.properties

hibernate.dialect=org.hibernate.dialect.HSQLDialect

Using Hyperjaxb3 with Apache CXF

Apache CXF is an open source services framework. CXF helps you build and develop services using frontend programming APIs, like JAX-WS and JAX-RS. These services can speak a variety of protocols such as SOAP, XML/HTTP, RESTful HTTP, or CORBA and work over a variety of transports such as HTTP, JMS or JBI.

One of the features of Apache CXF is WSDL-to-Java code generation with the cxf-codegen-plugin for Maven. You can invoke Hyperjaxb3 from this plugin to turn some of the WSDL- or schema-derived classes JPA-conform.

Invoking Hyperjaxb3 from cxf-codegen-plugin

Since 0.5.5.

The cxf-codegen-plugin uses JAXB schema compiler (XJC) internally. This gives the possibility to invoke Hyperjaxb3 as a normal XJC plugin. To achieve this, you need to do the following:

  • Add JAXB2 Basics and Hyperjaxb3 EJB Plugin to the dependencies of cxf-codegen-plugin.
  • Activate equals, hashCode and hyperjaxb3-ejb plugins using the extraargs/extraarg configuration elements.

Here's an example of the cxf-codegeb-plugin configuration:

A fragment of pom.xml

<plugin>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-codegen-plugin</artifactId>
    <executions>
        <execution>
            <id>generate-sources</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>wsdl2java</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <wsdlOptions>
            <wsdlOption>
                <wsdl>${basedir}/src/main/wsdl/myService.wsdl</wsdl>
                <bindingFiles>
                    <!-- JAX-WS bindings -->
                    <bindingFile>${basedir}/src/main/wsdl/binding.xml</bindingFile>
                    <!-- XJC bindings -->
                    <bindingFile>${basedir}/src/main/wsdl/binding.xjb</bindingFile>
                </bindingFiles>
                <extraargs>
                    <!-- Turns on the hashCode plugin -->
                    <extraarg>-xjc-XhashCode</extraarg>
                    <!-- Turns on the equals plugin -->
                    <extraarg>-xjc-Xequals</extraarg>
                    <!-- Turns on the Hyperjaxb3 EJB plugin -->
                    <extraarg>-xjc-Xhyperjaxb3-ejb</extraarg>
                </extraargs>
            </wsdlOption>
        </wsdlOptions>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>org.jvnet.jaxb2_commons</groupId>
            <artifactId>jaxb2-basics</artifactId>
            <version><!-- version --></version>
        </dependency>
        <dependency>
            <groupId>org.jvnet.hyperjaxb3</groupId>
            <artifactId>hyperjaxb3-ejb-plugin</artifactId>
            <version><!-- version --></version>
        </dependency>
    </dependencies>
</plugin>

See this tutorial for an example.

Adding vendor-specific annotations

Hyperjaxb3 does not generate any vendor-specific annotations or mappings, only standard JPA. The main reason for this is that vendor-specific support is extremely effort-consuming, I can't handle such efforts in the frame of a non-commercial product.

If you have a high-profile project which need vendor-specific extensions and ready to invest in development, you may get in contact with me.

Having stated that, there is still a solution which allows you adding vendor-specific annotations to the generated classes. This solution is based on the Annotate Plugin which I've developed in the frame of the JAXB2 Basics project.

Here's a short overview of this solution:

  • Include the annotation library into your classpath.
  • Enable the Annotate plugin in your build.
  • Add annox:annotate customization elements.

Basically, you let Hyperjaxb3 generate standard JPA annotations and add non-standard (verndor-specific) things via customizations.

Hibernate Annotations example

I'll demonstrate it by the example of Hibernate Annotations.

Assume we want to add a DELETE_ORPHAN cascade to the one-to-many property:

@OneToMany(targetEntity = Foo.class, cascade = {      
    javax.persistence.CascadeType.ALL                 
})                                                    
@org.hibernate.annotations.Cascade({                                            
    org.hibernate.annotations.CascadeType.DELETE_ORPHAN
})
public List<Foo> getFoos() {...}

The @OneToMany is a standard JPA annotation which is generated by Hyperjaxb3. However @org.hibernate.annotations.Cascade is Hibernate-specific annotation which is not handled by Hyperjaxb3. We'll need the Annotate Plugin for this.

Including the annotation library to the classpath

First of all you must make the annotation library (Hibernate Annotations in this case) available by adding it to the dependencies of the maven-hyperjaxb3-plugin:

<plugin>
    <groupId>org.jvnet.hyperjaxb3</groupId>
    <artifactId>maven-hyperjaxb3-plugin</artifactId>
    <configuration>
        ...
    </configuration>
    <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-annotations</artifactId>
            <version>3.4.0.GA</version>
        </dependency>
    </dependencies>
</plugin>

Turning on the Annotate plugin

Next we'll need to turn on the Annotate plugin:

<plugin>
    <groupId>org.jvnet.hyperjaxb3</groupId>
    <artifactId>maven-hyperjaxb3-plugin</artifactId>
    <configuration>
        ...
        <args>
            <arg>-Xannotate</arg>
        </args>
    </configuration>
    <dependencies>...</dependencies>
</plugin>

Adding customization elements

Now we're ready to customize:

<xs:schema
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
 
    xmlns:hj="http://hyperjaxb3.jvnet.org/ejb/schemas/customizations"
    xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
 
    xmlns:annox="http://annox.dev.java.net"
    xmlns:ha="http://annox.dev.java.net/org.hibernate.annotations"
 
    jaxb:extensionBindingPrefixes="hj orm xjc annox"
    jaxb:version="2.1" ... >
 
    <!-- ... -->
 
    <xs:complexType name="...">
        <xs:sequence>
            <xs:element name="foo" type="test:foo" minOccurs="0" maxOccurs="unbounded">
                <xs:annotation>
                    <xs:appinfo>
                        <annox:annotate>
                            <ha:Cascade value="DELETE_ORPHAN"/>
                        </annox:annotate>
                    </xs:appinfo>
                </xs:annotation>
            </xs:element>
            <!-- ... -->
        </xs:sequence>
    </xs:complexType>
 
    <!-- ... -->
 
</xs:schema>

Please see the Annotate Plugin documentation and Annox User Guide for more information on formulation your Java annotations in XML.

See the cu-one test project for the working example.

Testing generated mappings

Overview

Generating roundtrip test case

Configuring JPA provider for tests

Providing sample XML files for tests

Running roundtrip tests

Roundtripping caveats

Clone this wiki locally