• No results found

Externalizing metadata with XML files

Domain models and metadata

3.3 Domain model metadata

3.3.3 Externalizing metadata with XML files

You can replace or override every annotation in JPA with an XML descriptor element. In other words, you don’t have to use annotations if you don’t want to, or if keeping mapping metadata separate from source code is for whatever reason advantageous to your system design.

XML METADATAWITH JPA

<entity-mappings version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd"> <persistence-unit-metadata> <xml-mapping-metadata-complete/> <persistence-unit-defaults> <delimited-identifiers/> </persistence-unit-defaults> </persistence-unit-metadata>

<entity class="org.jpwh.model.simple.Item" access="FIELD">

<attributes>

<id name="id">

<generated-value strategy="AUTO"/>

</id>

<basic name="name"/> <basic name="auctionEnd">

<temporal>TIMESTAMP</temporal>

</basic>

</attributes>

</entity> </entity-mappings>

The JPA provider automatically picks up this descriptor if you place it in a META- INF/orm.xml file on the classpath of the persistence unit. If you prefer to use a differ- ent name or several files, you’ll have to change the configuration of the persistence unit in your META-INF/persistence.xml file:

<persistence-unit name="SimpleXMLCompletePU">

...

<mapping-file>simple/Mappings.xml</mapping-file>

<mapping-file>simple/Queries.xml</mapping-file>

...

</persistence-unit>

If you include the <xml-mapping-metadata-complete> element, the JPA provider ignores all annotations on your domain model classes in this persistence unit and relies only on the mappings as defined in the XML descriptor(s). You can (redundantly in this case) enable this on an entity level, with <metadata-complete="true"/>. If enabled, the JPA provider assumes that you mapped all attributes of the entity in XML and that it should ignore all annotations for this particular entity.

Listing 3.8 JPA XML descriptor containing the mapping metadata of a persistence unit

PATH: /model/src/main/resources/META-INF/persistence.xml First, global metadata Ignore all annotations and all mapping metadata in XML files.

Some default settings

Escape all SQL column, table, and other names: for example, if your SQL names are keywords (such as a “USER”).

Instead, if you don’t want to ignore but override the annotation metadata, don’t mark the XML descriptors as “complete”, and name the class and property to override:

<entity class="org.jpwh.model.simple.Item">

<attributes>

<basic name="name">

<column name="ITEM_NAME"/>

</basic>

</attributes> </entity>

Here you map the name property to the ITEM_NAME column; by default, the property would map to the NAME column. Hibernate will now ignore any existing annotations from the javax.persistence.annotation and org.hibernate.annotations pack- ages on the name property of the Item class. But Hibernate doesn’t ignore Bean Vali- dation annotations and still applies them for automatic validation and schema generation! All other annotations on the Item class are also recognized. Note that you don’t specify an access strategy in this mapping, so field access or accessor methods are used, depending on the position of the @Id annotation in Item. (We’ll get back to this detail in the next chapter.)

We won’t talk much about JPA XML descriptors in this book. The syntax of these documents is a 1:1 mirror of the JPA annotation syntax, so you shouldn’t have any problems writing them. We’ll focus on the important aspect: the mapping strategies. The syntax used to write down metadata is secondary.

Unfortunately, like many other schemas in the Java EE world, the JPA orm_2_0.xsd doesn’t allow vendor extensions. You can’t have elements and attributes from another namespace in the JPA XML mapping documents. Consequently, using vendor exten- sions and Hibernate native features requires falling back to a different XML syntax.

HIBERNATE XML MAPPINGFILES

The native Hibernate XML mapping file format was the original metadata option before JDK 5 introduced annotations. By convention, you name these files with the suf- fix .hbm.xml. The following listing shows a basic Hibernate XML mapping document.

<?xml version="1.0"?>

<hibernate-mapping

xmlns="http://www.hibernate.org/xsd/orm/hbm"

package="org.jpwh.model.simple"

default-access="field">

<class name="Item">

<id name="id">

<generator class="native"/>

</id>

<property name="name"/>

<property name="auctionEnd" type="timestamp"/>

Listing 3.9 Metadata document in Hibernate’s native XML syntax

PATH: /model/src/main/resources/simple/Native.hbm.xml Override SQL column name Declare metadata

B

Entity class mapping

</class>

<query name="findItemsHibernate">select i from Item i</query>

<database-object>

<create>create index ITEM_NAME_IDX on ITEM(NAME)</create>

<drop>drop index if exists ITEM_NAME_IDX</drop>

</database-object> </hibernate-mapping>

B

Metadata is declared ion a <hibernate-mapping> root element. Attributes such as package name and default-access apply to all mappings in this file. You may include as many entity class mappings as you like.

Note that this XML file declares a default XML namespace for all elements; this is a new option in Hibernate 5. If you have existing mapping files for Hibernate 4 or older with XML document type declarations, you can continue using them.

Although it’s possible to declare mappings for multiple classes in one mapping file by using multiple <class> elements, many older Hibernate projects are organized with one mapping file per persistent class. The convention is to give the file the same name and package as the mapped class: for example, my/model/Item.hbm.xml for the my.model.Item class.

A class mapping in a Hibernate XML document is a “complete” mapping; that is, any other mapping metadata for that class, whether in annotations or JPA XML files, will trigger a “duplicate mapping” error on startup. If you map a class in a Hibernate XML file, this declaration has to include all mapping details. You can’t override indi- vidual properties or extend an existing mapping. In addition, you have to list and map all persistent properties of an entity class in a Hibernate XML file. If you don’t map a property, Hibernate considers it transient state. Compare this with JPA mappings, where the @Entity annotation alone will make all properties of a class persistent.

Hibernate native XML files are no longer the primary choice for declaring the bulk of a project’s ORM metadata. Most engineers now prefer annotations. Native XML metadata files are mostly used to gain access to special Hibernate features that aren’t available as annotations or are easier to maintain in XML files (for example, because it’s deployment-dependent configuration metadata). You aren’t required to have any <class> elements in a Hibernate XML mapping file. Thus, all metadata in these files can be global to the persistence unit, such as externalized (even native SQL) query strings, custom type definitions, auxiliary SQL DDL for particular DBMS products, dynamic persistence context filters, and so on.

When we later discuss such advanced and native Hibernate features, we’ll show you how to declare them in Hibernate XML files. As already mentioned, your focus should be on understanding the essence of a mapping strategy, and most of our examples will use JPA and Hibernate annotations to express these strategies.

Externalized queries

Auxiliary schema DDL

The approaches we’ve described so far assume that all ORM metadata is known at development (or deployment) time. Suppose that some information isn’t known before the application starts. Can you programmatically manipulate the mapping metadata at runtime? We’ve also mentioned the JPA metadata API for access to persis- tence unit details. How does that work, and when is it useful?