Skip to content

HyperJAXB3 JAXB vs. JPA

Laurent Schoelens edited this page May 17, 2024 · 1 revision

Both JAXB and JPA work with Java objects. JAXB provides objects with XML binding, JPA - with relational persistence. Unfortunatelly, combining these two technologies is complicated by the fact that there is a number of severe incompatibilities between them. These incompatibilities are mainly due to the limitations of JPA specifications. However, there are also few JAXB-specific issues.

In this section I'll explain major JAXB/JPA incompatibilities and sketch out solutions implemented in Hyperjaxb3.

JAXB/JPA conflicts

Limited support for property types

JPA only supports the following property types:

  • primitive types, wrappers of the primitive types;
  • java.lang.String;
  • java.math.BigInteger, java.math.BigDecimal;
  • java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Time, java.sql.Timestamp
  • serializable types;
  • byte[], Byte[], char[], Character[];
  • enums;
  • entity types;
  • collections of entity types;
  • embeddable classes.

As you see, this set is quite limited. Only a very small number of simple types is supported. Take, for instance a good old java.net.URL - it will be mapped as a serializiable type (so say goodbye to the nice string representation of the URL). JPA also does not provide any way to define own custom types (like "user-defined types" in Hibernate).

JAXB in its turn does use some simple types which are not supported by JPA, for instance:

  • javax.xml.namespace.QName (xsd:QName),
  • javax.xml.datatype.Duration (xsd:duration),
  • javax.xml.datatype.XMLGregorianCalendar (xsd:date, xsd:time, xsd:dateTime, xsd:gYearMonth, xsd:gYear, xsd:gMonthDay, xsd:gDay, xsd:gMonth).

Sometimes it is also desired to map a list of strings as a comma-separated strings, not as a collection

In order to allow these "custom" simple types to be mapped by JPA, Hyperjaxb3 adds another getter/setter pair which adapt the problematic property and perform conversion when accessed.

Collections of simple types

As the JPA spec excerpt above says, JPA supports collections of entity types only. So if you have a list of number or a set of strings, you can't map it with JPA. Not too POJO-friendly, I would say.

At the same time, JAXB generates collections of simple types quite naturally. For instance the following element:

<xs:element name="myLongCollection" type="xs:long" minOccurs="0" maxOccurs="unbounded"/>

This will quite naturally produce the following property:

public List<Long> getLong() {
    if (_long == null) {
        _long = new ArrayList<Long>();
    }
    return this._long;
}

This property is not mappable with pure JPA. To overcome this, Hyperjaxb3 creates a new artificial entity and wrapes the simple collection as a collection of entities.

Heterogeneous collections

Depending on typing, collections may be homogeneous (all elements have the same common base type) or heterogeneous (elements have different types). Heterogeneous collections are not quite common in the POJO world, depelopers usually prefer homogeneous collections.

There are certain cases, when JAXB generates heterogeneous collection properties. For instance, if you have a repetable sequence in your schema, it's likely that you'll get a heterogeneous collection. Consider the following example:

<xs:complexType name="heteroSequenceType">
    <xs:sequence>
        <!-- ... -->
        <xs:sequence maxOccurs="unbounded">
            <xs:element name="e" type="xs:string"/>
            <xs:element name="f" type="complexType"/>
        </xs:sequence>
        <!-- ... -->
    </xs:sequence>
</xs:complexType>
/**
 * Gets the value of the eAndF property.
 * ...
 * Objects of the following type(s) are allowed in the list
 * {@link String }
 * {@link ComplexType }
 */
@Transient
public List<Object> getEAndF() {
    if (eAndF == null) {
        eAndF = new ArrayList<Object>();
    }
    return this.eAndF;
}

The EAndF property is a heterogeneous list - it can contain strings as well as instances of ComplexType as well.

JPA can't map this type of collections. Therefore Hyperjaxb3 needs to "homogenize" the collection by introducing a new artificial entity capable of holding all the possible collection types - and adapting the heterogeneous collection property with a new homogenized collection.

This is probably the most complicated hack carried out by Hyperjaxb3.

JAXBElement properties

In certain cases JAXB generates properties where the real value is wrapped into javax.xml.bind.JAXBElement. For instance, if you declare a nillable element, JAXB will wrap the actual value:

<xs:element name="dateNillable" type="xs:date" minOccurs="0" nillable="true"/>
public JAXBElement<XMLGregorianCalendar> getDateNillable() {
        return dateNillable;
}

Needless to say, this type of properties can't be mapped by JPA. They have to be wrapped into collections of simplifies types.

DOM properties

A very useful extension of JAXB is xjc:dom which instructs JAXB to load a part of the XML document into an object structure as a DOM element - without any unmarshalling:

<xs:element name="single" minOccurs="0">
    <xs:annotation>
        <xs:appinfo>
            <xjc:dom/>
        </xs:appinfo>
    </xs:annotation>
</xs:element>
@XmlAnyElement
protected Element single;
 
public Element getSingle() {
    return single;
}

DOM elements can't be persisted by JPA. Hyperjaxb3 solves this problem by converting the DOM into XML string in the wrapping property.

Generic (any type) properties

JAXB is capable of handling XML Schema's xsd:any constructs. Depending on the processing type (strict, lax, skip) you may get DOM properties (skip procesing), generic properties (strict) or even mixed properties. Generic properties may contain any object type available in current JAXB context. This contradicts JPA where you have to know the type of the associated entity.

To handle this case, Hyperjaxb3 marshalls the generic content and saves it as string.

Clone this wiki locally