Documentation | Reference | Configuration Hyperjaxb3 Reference Documentation Overview 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 trip. Mission statement The goal of Hyperjaxb3 is to provide schema-derived classes with JPA persistence. Therefore 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 JAXB plugin which augments or corrects the generated code so that the produced schema-derived classes become valid persistent entity classes. Technically, Hyperjaxb3 tasks are:
- Correct, adapt or wrap types and properties not supported by JPA.
- Add the required properties (ex. identifier or version) and methods (collection setters, equals, hashCode) where necessary.
- Generate the required ORM metadata - JPA annotations or XML mapping resources.
- Allow to customize the generated ORM metadata.
JAXB versus JPA 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 Commons Basic to generate these methods. These plugins generate Commons Lang-style equals and hashCode methods. With Hyperjaxb3 you can configure, which equals and hash code builders will be used TODO (org.jvnet.jaxb2_commons.lang.builder.JAXBEqualsBuilder and org.jvnet.jaxb2_commons.lang.builder.JAXBHashCodeBuilder) or turn off equals and hashCode generation off TODO. Generating persistence.xml According to the entity packaging guidelines of the JPA specification, Hyperjaxb3 generates the persistence.xml 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:
<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, the first one in alphabetic order will be picked up*TODO*. You may also specify the name of the persistence unit expliciltly in configuration. In future versions of Hyperjaxb3 you'll be able to customize this file to include own definitions TODO. You may also turn off the generation of persistence.xml TODO. In order to turn schema-derived classes into JPA-compliant entities, Hyperjaxb3 annotates them with an @Entity annotation. Entity name is not specified and is implicitly defaulted to the fully qualified name of the derived class. Schema type:
<xsd:complexType name="USAddress">
<xsd:sequence>
<!-- ... -->
</xsd:sequence>
</xsd:complexType>
Derived class:
@Entity
@Table(name = "USAddress")
@Inheritance(strategy = InheritanceType.JOINED)
public class USAddress implements Equals, HashCode
{ ... }
You have probably mentioned two further annotations - @Table and @Inheritance. Target database table (@Table) The @Table annotation provides parameters of the target database table. By default, it only defines the table name which is generated automatically. Table name is typically picked after the class name (corrected, if it's too long or matches a reserved name etc.). Table names and further parameters may be customized using the hj:table customization element:
<xsd:complexType name="USAddress">
<xsd:annotation>
<xsd:appinfo>
<hj:table
name="address"
catalog="..."
schema="..."/>
</xsd:appinfo>
</xsd:annotation>
<xsd:sequence>
<!-- ... -->
</xsd:sequence>
</xsd:complexType>
In future version you'll be able to declare unique constraints in hj:table customizations as well TODO. Inheritance strategy (@Inheritance) JPA is capable of handling hierarchical class structures (inheritance or polyformic mapping). Default inheritance strategy is the single table per class hierarchy. From our experience, schema-derived class hierarchies are better mapped using the joined strategy: A strategy in which fields that are specific to a subclass are mapped to a separate table than the fields that are common to the parent class, and a join is performed to instantiate the subclass. Therefore Hyperjaxb3 per default declares the @Inheritance(strategy = InheritanceType.JOINED) annotation. In future releases you'll be able to customize the default inheritance strategy globally for all TODO or locally for some chosen classes TODO. Marking transient classes By default, Hyperjaxb3 makes all of your classes pesistenent. In some cases you may want to exclude some classes to avoid them becoming persistence. For this purpose, attach the hj:ignored customization to this class:
<xs:complexType name="ignoredType">
<xs:annotation>
<xs:appinfo>
<hj:ignored/>
</xs:appinfo>
</xs:annotation>
<!-- ... -->
</xs:complexType>
Note that if you mark a type as ignored, all sub-types (those derived by extension or restriction) will be ignored as well. Moreover, all fields of this type (and subtypes) will be made transient. In future versions it will be possible to annotate ignored classes with the @MappedSuperclass annotation in order to allow subclasses for being persistent TODO. Another nice-to-have feature is ignoring whole packages TODO and making only certain classes persistent TODO (opposite to making all the classes persistent and ignoring only certain classes). Annotations for properties The JPA specification does not require persistent properties to be annotated - on the contrary, it's transient properties that must be annotated with the @Transient annotation. Nevertheless, Hyperjaxb does annotate all the persistent properties - first, to make it clear what's persistent and, second, avoid ambiguity with implicit annotations. Before we go on discussing property annotations generated by Hyperjaxb3 I first need to describe the taxonomy of property types and cardinalities Hyperjaxb3 needs to handle. This may look a bit complex, but it's essential in order to understand why certain annotation is generated or why Hyperjaxb3 complains it can't handle the situation (and what you can do about this). Cardinality Properties may be single or collections. Single properties contain at most one item and are typically represented in the XML Schema as attributes or elements with maxOrccurs="1". Collection properties may contain several items and are typically represented in the XML Schema as elements with maxOccurs="n" where n>1 or maxOccurs="unbounded". Typically but not always, there are exceptions. On the Java side, in schema-derived classes collection properties have a type of List<T>. One JAXB peculiarity is that it generates no setters for collection properties (Hyperjaxb3 has to add setters for these properties). Homogeneous and heterogeneous properties Properties usually contain items of a single type only. These properties are called homogeneous since they contain homogeneous content. However, in some cases JAXB generates heterogeneous properties which contain items of different types (for instance, strings and other elements for the mixed content properties). JPA as well as most modern ORM can't handle this case - therefore Hyperjaxb3 needs to "homogenize" heterogeneous properties. Property types For the usual case of a homogeneous properties, there are several type categories to consider:
- Basic properties. Numbers, strings, dates and so on. Most XML Schema's simple types (with the exception of enumerated types.
- Enumerated properties having Java 5 enums as content. Usually generated from simple types with enumerations.
- DOM properties - with DOM elements as content. Result from xsd:any with skip processing or xjc:dom customization.
- Any-type properties - with unmarshalled objects as content. Result from xsd:any with strict processing or xsd:anyType type.
- Complex properties - contain instances of schema-derived classes.
- Heterogeneous properties - contain several types of content.
Reference properties There is a further peculiarity of JAXB to be taken into an account. In some cases JAXB wraps the content in a JAXBElement so you'll get JAXBElement<T>-typed property instead of T-typed property:
<xsd:element
name="stringNillable"
type="xsd:string"
minOccurs="0"
nillable="true"/>
public JAXBElement<String> getStringNillable() {
return stringNillable;
}
This happens not only with nillable elements but also in a number of further scenarios. JPA can't handle JAXBElement so Hyperjaxb3 has to adapt/wrap it as well. Transient property By default, Hyperjaxb3 will make the property transient (annotate it with @Transient) in two cases: if it decides it can't handle this property or if you explicitly customize it to be transient. The first case occurs if Hyperjaxb3 finds out that it can't generate an appropriate mapping or adapting code for the given property. You'll also see a warning and (usually) a reference to the corresponding issue. Theoretically all the JAXB properties are ORM-mappable, so it's just a matter of time for all cases to be implemented. You may also explicitly customize a property to be transient. To do this, simply use the <hj:ignored/> customization (just like with classes):
<xs:complexType name="ignoredFieldsType">
<xs:sequence>
<xs:element name="a" type="xs:string">
<xs:annotation>
<xs:appinfo>
<hj:ignored/>
</xs:appinfo>
</xs:annotation>
</xs:element>
<!-- ... -->
</xs:sequence>
</xs:complexType>
Note that this will make property transient in the given schema-derived class as well as in all its subclasses. Identifier property According to the EJB3 spec, every entity must have a primary key. The primary key must be defined on the entity that is the root of the entity hierarchy or on a mapped superclass of the entity hierarchy. The primary key must be defined exactly once in an entity hierarchy. This basically means that root schema-derived classes (those having no schema-derived superclass) must declare and identifier property. There is two ways to do this: either let Hyperjaxb3 generate a default identifier property for you or mark an existing property as identifier using a customization. Generating default identifier If you do not customize some property to be an identifier, Hyperjaxb3 will generate a default identifier property for you:
@XmlAttribute
protected Long hjid;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "Hjid")
public Long getHjid() {
return hjid;
}
public void setHjid(Long value) {
this.hjid = value;
}
Default identifier property is called hjid and has the java.lang.Long type. It gets a generated value produced by the automatic strategy. Hyperjaxb3 also makes this property an attribute of the schema-derived class (so you'll see hjid="1234" when you marshal your object. In future releases you'll be able to customize the name, type and further attributes of the default identifier property TODO. Using an existing property as identifier Alternatively, you may mark an existing property as identifier. Simply mark it with the <hj:id/> customization - and Hyperjaxb3 will use it as an identifier instead of generating a new property.
<xs:complexType name="customizedIdType">
<xs:sequence>
<xs:element name="id" type="xs:long">
<xs:annotation>
<xs:appinfo>
<hj:id/>
</xs:appinfo>
</xs:annotation>
</xs:element>
<!-- ... -->
</xs:sequence>
</xs:complexType>
protected long id;
@Id
@Column(name = "Id")
public long getId() {
return id;
}
public void setId(long value) {
this.id = value;
}
Please note that your identifier property must have an appropriate type (spec allows any Java primitive type, any primitive wrapper type, java.lang.String, java.util.Date, java.sql.Date however floating-point types are not recommended). Customizing the identifier property The hj:id customization element may contain the hj:column (issue 134), hj:generated-value, hj:temporal (issue 135), hj:table-generator and hj:sequence-generator customizations. Below is a typical usage example:
<xsd:attribute name="id" use="optional" type="xsd:int">
<xsd:annotation>
<xsd:appinfo>
<hj:id>
<hj:generated-value strategy="SEQUENCE" generator="mySequenceGenerator"/>
<hj:sequence-generator
name="mySequenceGenerator"
sequence-name="mySequence"
initial-value="1"
allocation-size="100"/>
</hj:id>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
Version property In case you want to allow for performing optimistic locking with you schema-derived classes, you'll need to have a version property in your entities. Hyperjaxb3 supports this by allowing you to mark an existing property as version using the <hj:version/> customization:
<xs:complexType name="customizedVersionType">
<xs:sequence>
<!-- ... -->
<xs:element name="version" type="xs:long">
<xs:annotation>
<xs:appinfo>
<hj:version/>
</xs:appinfo>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
protected long version;
@Version
@Column(name = "Version")
public long getVersion() {
return version;
}
public void setVersion(long value) {
this.version = value;
}
Note that version properties have also type constraints (int, java.lang.Integer, short, java.lang.Short, long, java.lang.Long, java.sql.Timestamp). Single basic property The simplest case of a persistent property is a single-valued property containing a simple value. This type of properties is annotated with the @Basic annotation:
<xs:element name="string" type="xs:string" minOccurs="0"/>
protected String string;
@Basic
@Column(name = "String")
public String getString() {
return string;
}
public void setString(String value) {
this.string = value;
}
Considering facets Since 0.3. In case your simple type of your property contains constraining faces like minLength, maxLength, length, totalDigits, fractionDigits, Hyperjaxb3 will consider them when generating the @Column annotation. Hyperjaxb3 considers not only the type itself, but its parent types as well. If length or maxLength are defined, they are used as the length attribute of @Column (length has priority over maxLength). If minLength is defined and it's greater that 127 the minLength * 2 is used as length of the @Column. If totalDigits facet is defined, its value is used as the precision attribute of the @Column. If fractionDigits facet is found, its value is used as the scale attribute of the @Column. Check the following example:
<xsd:simpleType name="largeMinLength">
<xsd:restriction base="xsd:string">
<xsd:minLength value="200"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="minLength">
<xsd:restriction base="xsd:string">
<xsd:minLength value="5"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="maxLength">
<xsd:restriction base="test:minLength">
<xsd:maxLength value="10"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="length">
<xsd:restriction base="test:maxLength">
<xsd:length value="8"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="digits">
<xsd:restriction base="xsd:decimal">
<xsd:totalDigits value="5"/>
<xsd:fractionDigits value="2"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="totalDigits">
<xsd:restriction base="xsd:decimal">
<xsd:totalDigits value="3"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="fractionDigits">
<xsd:restriction base="xsd:decimal">
<xsd:fractionDigits value="2"/>
</xsd:restriction>
</xsd:simpleType>
Produces the following:
@Basic
@Column(name = "LARGEMINLENGTH", length = 400)
public String getLargeMinLength() { ... }
@Basic
@Column(name = "MINLENGTH")
public String getMinLength() { ... }
@Basic
@Column(name = "MAXLENGTH", length = 10)
public String getMaxLength() { ... }
@Basic
@Column(name = "LENGTH_", length = 8)
public String getLength() { ... }
@Basic
@Column(name = "DIGITS", precision = 5, scale = 2)
public BigDecimal getDigits() { ... }
@Basic
@Column(name = "TOTALDIGITS", precision = 3)
public BigDecimal getTotalDigits() { ... }
@Basic
@Column(name = "FRACTIONDIGITS", scale = 2)
public BigDecimal getFractionDigits() { ... }
Adapted property Due to JPA limitations, in some cases even common simple types are cannot be mapped. There's also no mechanism to define an implicit conversion. Hyperjaxb3 handles this type of properties by adapting them with an explicit conversion. Consider the following example of a xs:QName-typed element:
<xs:element name="QName" type="xs:QName" minOccurs="0"/>
JAXB generates a javax.xml.namespace.QName-typed property which is not supported by JPA:
@XmlElement(name = "QName")
protected QName qName;
public QName getQName() {
return qName;
}
public void setQName(QName value) {
this.qName = value;
}
To handle this case, JAXB makes the QName property itself transient and generates an additional QNameItem property which performs an on-the-fly conversion of QName:
@XmlElement(name = "QName")
protected QName qName;
@Transient
public QName getQName() {
return qName;
}
public void setQName(QName value) {
this.qName = value;
}
@Basic
@Column(name = "QNameItem")
public String getQNameItem() {
return XmlAdapterUtils.unmarshall(QNameAsString.class, this.qName);
}
public void setQNameItem(String target) {
this.qName = XmlAdapterUtils.marshall(QNameAsString.class, target);
}
The QNameItem property added by Hyperjaxb3 uses the QNameAsString built-in adapter which converts QName to string and back. Thus the QName will be persisted in the database as string. The conversion is done implicitly in the additional QNameItem property, all the other APIs are left intact. In future releases you'll be able to customize the name of the adapting property as well as the used adapter class TODO. Temporal properties JPA requires temporal properties like date, time and so on to be annotated with the @Temporal annotations. Hyperjaxb3 detects temporal properties and chooses the appropriate temporal type TemporalType.DATE, TemporalType.TIME or TemporalType.TIMESTAMP. Temporal type is chosen based on the Java type of the property and its type in the XML Schema:
- java.sql.Date maps to TemporalType.DATE.
- java.sql.Time maps to TemporalType.TIME.
- java.sql.Timestamp maps to TemporalType.TIMESTAMP.
- java.util.Calendar maps to TemporalType.TIMESTAMP.
- xsd:date, xsd:gYearMonth, xsd:gYear, xsd:gMonth, xsd:gMonthDay, xsd:gDay maps to TemporalType.DATE.
- xsd:time maps to TemporalType.TIME.
- xsd:dateTime maps to TemporalType.TIMESTAMP.
- All other cases map to TemporalType.TIMESTAMP (fallback behaviour).
In future releases you'll be able to customize the generated @Temporal annotation issue 136. Single enumerated property In case you have an enumerated simple type in your schema, JAXB can produce a Java 5 enum for this type. COnsider the following enumerated simple type:
<xsd:simpleType name="SexType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Male"/>
<xsd:enumeration value="Female"/>
</xsd:restriction>
</xsd:simpleType>
JAXB produces a corresponding SexType enum with MALE and FEMALE items (having literal values of "Male" and "Female" correspondingly.
@XmlType(name = "SexType")
@XmlEnum
public enum SexType {
@XmlEnumValue("Male")
MALE("Male"),
@XmlEnumValue("Female")
FEMALE("Female");
private final String value;
SexType(String v) { value = v; }
public String value() { return value; }
public static SexType fromValue(String v) {
for (SexType c: SexType.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
}
Hyperjaxb3 can detect enum properties produced by JAXB and annotate these properties with @Enumerated annotation.
<xsd:element name="sex" type="test:SexType"/>
@XmlElement(required = true)
protected SexType sex;
@Enumerated(EnumType.STRING)
public SexType getSex() {
return sex;
}
public void setSex(SexType value) {
this.sex = value;
}
By default, Hyperjaxb3 uses string representation of the enum, EnumType.STRING. Another option would be EnumType.ORDINAL. In future releases you could configure the default value TODO or customize specific properties TODO. Single DOM property In certain scenarios you may want to load parts of XML into you object structure without unmarshalling. This may be some semi-structured markup which does not fit object structures well - or simply arbitrary xsd:any content. To resolve this, JAXB allows unmarshalling parts of XML as DOM element. There are two main methods to get DOM elements into you objects:
- using the xjc:dom customization;
- using xsd:any with processContents="skip".
In both cases you'll get an DOM element-typed property (org.w3c.dom.Element as type by default). Element customized with xjc:dom:
<xsd:element name="content" minOccurs="0">
<xsd:annotation>
<xsd:appinfo>
<xjc:dom/>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
Turns into:
@XmlAnyElement
protected Element content;
public Element getContent() {
return content;
}
public void setContent(Element value) {
this.content = value;
}
xsd:any with processContents="skip":
<xsd:any namespace="##other" processContents="skip" minOccurs="0" axOccurs="1"/>
turns into:
@XmlAnyElement
protected Element any;
public Element getAny() {
return any;
}
public void setAny(Element value) {
this.any = value;
}
Note that there are some JAXB limitation on DOM contents. As you see, Java representation is in both cases identical. The problem between JAXB and JPA is that JPA can't persist DOM elements. Therefor Hyperjaxb3 needs to generate a wrapping property which serializes DOM into string in getter and parses the string back to DOM in setter:
@XmlAnyElement
protected Element any;
@Transient
public Element getAny() {
return any;
}
public void setAny(Element value) {
this.any = value;
}
@Basic
@Column(name = "AnyItem")
public String getAnyItem() {
return XmlAdapterUtils.unmarshall(ElementAsString.class, this.any);
}
public void setAnyItem(String target) {
this.any = XmlAdapterUtils.marshall(ElementAsString.class, target);
}
The original property is made transient, while the wrapping property (which is string-typed) is annotated as @Basic. In future releases you'll be able to customize the name of the wrapping property TODO and override the default ElementAsString adapter TODO. Single 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). In future releases you'll be able to customize this TODO. You'll be able to customize the name of the adapting property as well.
@XmlAnyElement(lax = true)
protected Object any;
@Transient
public Object getAny() {
return any;
}
public void setAny(Object value) {
this.any = value;
}
@Basic
@Column(name = "AnyItem")
public String getAnyItem() {
return JAXBContextUtils.unmarshall("org.jvnet.hyperjaxb3.ejb.tests.any", this.any);
}
public void setAnyItem(String target) {
this.any = JAXBContextUtils.marshall("org.jvnet.hyperjaxb3.ejb.tests.any", target);
}
The approach presented above is quite similar to 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. Single complex property If you have an complex-type element, this will be probably represented on the Java side as complex property - a property which has a schema-derived class as its type:
<xs:complexType name="complexTypesType">
<xs:sequence>
<xs:element name="single" type="complexType" minOccurs="0"/>
<!-- ... -->
</xs:sequence>
</xs:complexType>
<xs:complexType name="complexType">
<xs:sequence>
<xs:element name="a" type="xs:string" minOccurs="0"/>
<xs:element name="b" type="xs:long" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
Turns into:
protected ComplexType single;
public ComplexType getSingle() {
return single;
}
public void setSingle(ComplexType value) {
this.single = value;
}
Hyperjaxb3 annotates complex single properties with @ManyToOne annotation:
@ManyToOne(cascade = {
CascadeType.ALL
})
public ComplexType getSingle() {
return single;
}
Since the type of the property is known from the return type of the method, the targetEntity attribute of the annotation is not necessary. By default, Hyperjaxb3 adds {{cascade=
Unknown macro: {CascadeType.ALL}
}} meaning that all the persistent actions (persist, merge, remove, refresh) will be cascaded. In future releases you'll be able to customize the cascade TODO as well as fetch TODO and optional TODO attributes. Two further persistence options for complex single properties are @OneToOne TODO and @Embedded/@Embeddable TODO. They will be implemented in future versions. Single heterogeneous property In some cases your single-valued property may be multi-typed. For instance, if you have xsd:any content with processContents="lax", this results a property which would contain either JAXBElement representing unmarshalled content or org.w3c.dom.Element holding a DOM representation of XML if unmarshalling did not succeeded:
<xs:any namespace="##other" processContents="lax"
minOccurs="0" maxOccurs="1"/>
@XmlAnyElement(lax = true)
protected Object any;
/**
* Gets the value of the any property.
*
* @return
* possible object is
* {@link Object }
* {@link Element }
*
*/
public Object getAny() {
return any;
}
/**
* Sets the value of the any property.
*
* @param value
* allowed object is
* {@link Object }
* {@link Element }
*
*/
public void setAny(Object value) {
this.any = value;
}
This case is currently not fully implemented (see issue 44). Currently Hyperjaxb3 only supports the particular case shown above TBD. Element-referencing properties Under some circumstances you'll be getting instead of getting a normally typed property you'll be getting JAXBElement<T>-typed property. A typical case are elements marked with nillable="true". A small note on minOccurs="0" and nillable="true". They mean two different things. For a truly optional element, one that is allowed to be absent altogether, minOccurs="0" is what you want. nillable="true" means that the element can be present without its normal content, provided that xsi:nil="true" is one of its attributes. Consider the following example:
<xs:element name="longNillable" type="xs:long"
minOccurs="0" nillable="true"/>
@XmlElementRef(name = "longNillable", type = JAXBElement.class)
protected JAXBElement<Long> longNillable;
public JAXBElement<Long> getLongNillable() {
return longNillable;
}
public void setLongNillable(JAXBElement<Long> value) {
this.longNillable = ((JAXBElement<Long> ) value);
}
In this case JAXB uses the JAXBElement to realize the "nillability" semantics for single properties. There are further cases where you get JAXBElement<T> instead of simply T as type. And, as already mentioned before, JPA can't handle this type of properties. Hyperjaxb3 tries to handle this situation by adding an adapting property:
@XmlElementRef(name = "longNillable", type = JAXBElement.class)
protected JAXBElement<Long> longNillable;
@Transient
public JAXBElement<Long> getLongNillable() {
return longNillable;
}
public void setLongNillable(JAXBElement<Long> value) {
this.longNillable = ((JAXBElement<Long>) value);
}
// ...
@Basic
@Column(name = "LongNillableItem")
public Long getLongNillableItem() {
return XmlAdapterUtils.unmarshallJAXBElement(this.longNillable);
}
public void setLongNillableItem(Long target) {
this.longNillable = XmlAdapterUtils.marshallJAXBElement(Long.class, new QName("", "longNillable"), SimpleTypesType.class, target);
}
In the example above, Hyperjaxb3 has added the longNillableItem property (property name will be customizable in future TODO). Since there is only one possible element for this property, it can be embedded into marshalling/unmarshalling. Unfortunatelly, nil semantics are lost in this case. Moreover, this approach does not work if a property may reference several elements (like with substitution groups). I'll try to find a better solution in future versions TODO. Probably an @Embeddable item. Complex collection property Repeatable complex type elements are turned into complex collections:
<xs:complexType name="complexTypesType">
<xs:sequence>
<xs:element name="collection" type="complexType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
protected List<ComplexType> collection;
@OneToMany(cascade = {
CascadeType.ALL
})
@JoinTable(name = "COMPLEXTYPESTYPE_COLLECTION")
public List<ComplexType> getCollection() {
if (collection == null) {
collection = new ArrayList<ComplexType>();
}
return this.collection;
}
public void setCollection(List<ComplexType> collection) {
this.collection = collection;
}
Hyperjaxb3 uses @OneToMany to annotate this type of properties. By default, all actions are cascaded (TODO allow for customization). The joining table name is generated automatically (TODO allow for customization). Future versions will provide extensive customization possibilities (like using @ManyToMany, customizing annotation parameters and so on) TODO. Collection basic, enumerated, DOM or any-type property Previous section discussed single-valued properties which typically result from attributes and elements with maxOccurs="1" (or with maxOccurs omitted, which is the same). If you have a greater maxOccurs specified, your property will be probably a collection.
<xs:element name="int" type="xs:int"
minOccurs="0" maxOccurs="unbounded"/>
Turns into:
@XmlElement(name = "int", type = Integer.class)
protected List<Integer> _int;
public List<Integer> getInt() {
if (_int == null) {
_int = new ArrayList<Integer>();
}
return this._int;
}
As I mentioned before, the first problem between JPA and JAXB that concerns collections is that JAXB does not generate setters for collection properties. JAXB assumes you can do getMyCollectionProperty().add(myNewValue). However, JPA requires setters for all the properties. Therefore Hyperjaxb3 adds the missing setters:
public void setInt(List<Integer> _int) {
this._int = _int;
}
The second (and much more severe) problem is JPA limitation that it can't handle collections of simple types. Only collections of entities are allowed. I've heard that this may be changed in the future version of JPA spec, but right now Hyperjaxb3 need to create an artificial entity class in order to make simple collections persistable. Here's how it works. Hyperjaxb3 creates a new MyPropertyItem entity class. This class just holds an id and a single item field corresponding to the item of the single collection:
@XmlAccessorType(XmlAccessType.FIELD)
@Entity(name = "org.jvnet.hyperjaxb3.ejb.tests.one.SimpleCollectionTypesType$IntItem")
@Table(name = "SCTTINTITEM")
@Inheritance(strategy = InheritanceType.JOINED)
public static class IntItem
implements Item<Integer>
{
@XmlElement(name = "int")
protected Integer item;
@XmlAttribute
protected Long hjid;
@Basic
@Column(name = "ITEM")
public Integer getItem() {
return item;
}
public void setItem(Integer value) {
this.item = value;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "HJID")
public Long getHjid() {
return hjid;
}
public void setHjid(Long value) {
this.hjid = value;
}
}
This entity class is intended to wrap item of the collection. Next step is to adapt our simple-typed collection so that it becomes a collection of wrapping entities:
@OneToMany(cascade = {
CascadeType.ALL
})
@JoinTable(name = "SCTT_INTITEM")
public List<SimpleCollectionTypesType.IntItem> getIntItems() {
if (this.intItems == null) {
this.intItems = new ArrayList<SimpleCollectionTypesType.IntItem>();
}
if (ItemUtils.shouldBeWrapped(this._int)) {
this._int = ItemUtils.wrap(this._int, this.intItems, SimpleCollectionTypesType.IntItem.class);
}
return this.intItems;
}
public void setIntItems(List<SimpleCollectionTypesType.IntItem> value) {
this._int = null;
this.intItems = null;
this.intItems = value;
if (this.intItems == null) {
this.intItems = new ArrayList<SimpleCollectionTypesType.IntItem>();
}
if (ItemUtils.shouldBeWrapped(this._int)) {
this._int = ItemUtils.wrap(this._int, this.intItems, SimpleCollectionTypesType.IntItem.class);
}
}
@Transient
public List<Integer> getInt() {
if (_int == null) {
_int = new ArrayList<Integer>();
}
return this._int;
}
public void setInt(List<Integer> _int) {
this._int = _int;
}
This approach is applied to all the collection properties with the exception of complex and heterogeneous collections. I personally don't think that this solution is that perfect - but it works and currently I don't have other options. Heterogeneous collection property In some cases JAXB generates multi-typed or heterogeneous properties. Here's an example:
<xs:complexType name="heteroSequenceType">
<xs:sequence>
<!-- ... -->
<xs:sequence maxOccurs="unbounded">
<xs:element name="k" type="xs:string" nillable="true"/>
<xs:element name="l" type="complexType" nillable="true"/>
</xs:sequence>
</xs:sequence>
</xs:complexType>
Turns into:
@XmlElements({
@XmlElement(name = "l", required = true, type = ComplexType.class, nillable = true),
@XmlElement(name = "k", required = true, type = String.class, nillable = true)
})
protected List<Object> kAndL;
/**
* ...
* Objects of the following type(s) are allowed in the list
* {@link ComplexType }
* {@link String }
*/
@Transient
public List<Object> getKAndL() {
if (kAndL == null) {
kAndL = new ArrayList<Object>();
}
return this.kAndL;
}
public void setKAndL(List<Object> kAndL) {
this.kAndL = kAndL;
}
These types require homogenization. This type of properties are currently not supported (see issue 53). Adding custom annotations to generated bindings Details TBD. For now see example in ejb/tests/annox
|