-
Notifications
You must be signed in to change notification settings - Fork 99
HyperJAXB3 Generating ORM metadata
Please note that Hyperjaxb3 normalizes and collapses whitespace when generating string fields of annotations. Please see this issue for more information.
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.
TODO
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
.
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>
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
.
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 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.
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));
}
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 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 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)));
}
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);
}
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));
}
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.
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;
}
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;
}
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>
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.
TODO
TODO
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 :
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> |
- Home
- Migration guide
-
JAXB Maven Plugin
- Quick Start
-
User Guide
- Basic Usage
- Specifying What To Compile
- Referencing Resources in Maven Artifacts
- Using Catalogs
- Using Episodes
- Modular Schema Compilation
- Controlling the Output
- Using JAXB Plugins
- Using a Specific JAXB Version
- Configuring Extension, Validation and XML Security
- IDE Integration
- Miscellaneous
- Configuring Proxies
- Maven Documentation
- Configuration Cheat Sheet
- Common Pitfalls and Problems
-
JAXB2 Basics Plugins
- Using JAXB2 Basics Plugins
- JSR-305 Support
-
JAXB2 Basics Plugins List
- SimpleEquals Plugin
- SimpleHashCode Plugin
- Equals Plugin
- HashCode Plugin
- ToString Plugin
- Copyable Plugin
- Mergeable Plugin
- Inheritance Plugin
- AutoInheritance Plugin
- Wildcard Plugin
- Setters Plugin
- Simplify Plugin
- EnumValue Plugin
- JAXBIndex Plugin
- FixJAXB1058 Plugin
- Commons Lang Plugin
- Default Value Plugin
- Fluent API Plugin
- Namespace Prefix Plugin
- Value Constructor Plugin
- Boolean Getter Plugin
- CamelCase Plugin
- XML ElementWrapper Plugin
- Parent Pointer Plugin
- Property Listener Injector Plugin
- Annox
- JAXB Annotate Plugin
-
HyperJAXB3
- Build System Support
- Customization Guide
- Databases
- Development guide
- Extension guide
- FAQ
- IDE Support
- Java Persistence
- JAXB
- JDK Support
- Project Templates
-
Reference
- Adding vendor-specific annotations
- Features
- Integrating Hyperjaxb3 in builds
- Introduction
- Making schema-derived classes ready for JPA
- Adding required properties
- Applying workarounds for JAXB vs. JPA conflicts
- Enforcing top-level classes
- Generating equals and hashCode methods
- Generating ORM metadata
- Generating persistence unit descriptor
- JPA 2 Support
- Making classes serializable
- Testing generated mappings
- Reference - single page
- Related Projects
- Sample projects
- Solutions
- Target Scenarios
- Test Projects
- Tutorials
- Best Practices
- FAQ
- Sample Projects
- Support
- License
- Distribution