3.3. Object/relational mapping metadata

ORM tools require metadata to specify the mapping between classes and tables, properties and columns, associations and foreign keys, Java types and SQL types, and so on. This information is called the object/relational mapping metadata. Metadata is data about data, and mapping metadata defines and governs the transformation between the different type systems and relationship representations in object-oriented and SQL systems.

It's your job as a developer to write and maintain this metadata. We discuss various approaches in this section, including metadata in XML files and JDK 5.0 source code annotations. Usually you decide to use one strategy in a particular project, and after reading these sections you'll have the background information to make an educated decision.

3.3.1. Metadata in XML

Any ORM solution should provide a human-readable, easily hand-editable mapping format, not just a GUI mapping tool. Currently, the most popular object/relational metadata format is XML. Mapping documents written in and with XML are lightweight, human readable, easily manipulated by version-control systems and text editors, and they can be customized at deployment time (or even at runtime, with programmatic XML generation).

But is XML-based metadata really the best approach? A certain backlash against the overuse of XML can be seen in the Java community. Every framework and application server seems to require its own XML descriptors.

In our view, there are three main reasons for this backlash:

  • Metadata-based solutions have often been used inappropriately. Metadata is not, by nature, more flexible or maintainable than plain Java code.

  • Many existing metadata formats weren't designed to be readable and easy to edit by hand. In particular, a major cause of pain is the lack of sensible defaults for attribute and element values, requiring significantly more typing than should be necessary. Even worse, some metadata schemas use only XML elements and text values, without any attributes. Another problem is schemas that are too generic, where every declaration is wrapped in a generic extension attribute of a meta element.

  • Good XML editors, especially in IDEs, aren't as common as good Java coding environments. Worst, and most easily fixable, a document type declaration (DTD) often isn't provided, preventing autocompletion and validation.

There is no getting around the need for metadata in ORM. However, Hibernate was designed with full awareness of the typical metadata problems. The XML metadata format of Hibernate is extremely readable and defines useful default values. If attribute values are missing, reflection is used on the mapped class to determine defaults. Hibernate also comes with a documented and complete DTD. Finally, IDE support for XML has improved lately, and modern IDEs provide dynamic XML validation and even an autocomplete feature.

Let's look at the way you can use XML metadata in Hibernate. You created the Category class in the previous section; now you need to map it to the CATEGORY table in the database. To do that, you write the XML mapping document in listing 3.4.

Listing 3-4. Hibernate XML mapping of the Category class
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
"-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping>
<class
name="auction.model.Category" table="CATEGORY"> <id
name="id" column="CATEGORY_ID" type="long"> <generator class="native"/> </id> <property
name="name" column="NAME" type="string"/> </class> </hibernate-mapping>

❶ The Hibernate mapping DTD should be declared in every mapping file—it's required for syntactic validation of the XML.

❷ Mappings are declared inside a <hibernate-mapping> element. You may include as many class mappings as you like, along with certain other special declarations that we'll mention later in the book.

❸ The class Category (in the auction.model package) is mapped to the CATEGORY table. Every row in this table represents one instance of type Category.

❹ We haven't discussed the concept of object identity, so you may be surprised by this mapping element. This complex topic is covered in the next chapter. To understand this mapping, it's sufficient to know that every row in the CATEGORY table has a primary key value that matches the object identity of the instance in memory. The <id> mapping element is used to define the details of object identity.

❺ The property name of type java.lang.String is mapped to a database NAME column. Note that the type declared in the mapping is a built-in Hibernate type (string), not the type of the Java property or the SQL column type. Think about this as the converter that represents a bridge between the other two type systems.

We've intentionally left the collection and association mappings out of this example. Association and especially collection mappings are more complex, so we'll return to them in the second part of the book.

Although it's possible to declare mappings for multiple classes in one mapping file by using multiple <class> elements, the recommended practice (and the practice expected by some Hibernate tools) is to use one mapping file per persistent class. The convention is to give the file the same name as the mapped class, appending a suffix (for example, Category.hbm.xml), and putting it in the same package as the Category class.

As already mentioned, XML mapping files aren't the only way to define mapping metadata in a Hibernate application. If you use JDK 5.0, your best choice is the Hibernate Annotations based on the EJB 3.0 and Java Persistence standard.

3.3.2. Annotation-based metadata

The basic idea is to put metadata next to the information it describes, instead of separating it physically into a different file. Java didn't have this functionality before JDK 5.0, so an alternative was developed. The XDoclet project introduced annotation of Java source code with meta-information, using special Javadoc tags with support for key/value pairs. Through nesting of tags, quite complex structures are supported, but only some IDEs allow customization of Javadoc templates for autocompletion and validation.

Java Specification Request (JSR) 175 introduced the annotation concept in the Java language, with type-safe and declared interfaces for the definition of annotations. Autocompletion and compile-time checking are no longer an issue. We found that annotation metadata is, compared to XDoclet, nonverbose and that it has better defaults. However, JDK 5.0 annotations are sometimes more difficult to read than XDoclet annotations, because they aren't inside regular comment blocks; you should use an IDE that supports configurable syntax highlighting of annotations. Other than that, we found no serious disadvantage in working with annotations in our daily work in the past years, and we consider annotation-metadata support to be one of the most important features of JDK 5.0.

We'll now introduce mapping annotations and use JDK 5.0. If you have to work with JDK 1.4 but like to use annotation-based metadata, consider XDoclet, which we'll show afterwards.

Defining and using annotations

Before you annotate the first persistent class, let's see how annotations are created. Naturally, you'll usually use predefined annotations. However, knowing how to extend the existing metadata format or how to write your own annotations is a useful skill. The following code example shows the definition of an Entity annotation:

package javax.persistence;

@Target(TYPE)
@Retention(RUNTIME)
public @interface Entity {
  String name() default "";
}

The first line defines the package, as always. This annotation is in the package javax.persistence, the Java Persistence API as defined by EJB 3.0. It's one of the most important annotations of the specification—you can apply it on a POJO to make it a persistent entity class. The next line is an annotation that adds meta-information to the @Entity annotation (metadata about metadata). It specifies that the @Entity annotation can only be put on type declarations; in other words, you can only mark up classes with the @Entity annotation, not fields or methods. The retention policy chosen for this annotation is RUNTIME; other options (for other use cases) include removal of the annotation metadata during compilation, or only inclusion in byte-code without possible runtime reflectivity. You want to preserve all entity meta-information even at runtime, so Hibernate can read it on startup through Java Reflection. What follows in the example is the actual declaration of the annotation, including its interface name and its attributes (just one in this case, name, with an empty string default).

Let's use this annotation to make a POJO persistent class a Java Persistence entity:

package auction.model;

import javax.persistence.*;

@Entity
@Table(name = "ITEM")
public class Item {
  ...
}

This public class, Item, has been declared as a persistent entity. All of its properties are now automatically persistent with a default strategy. Also shown is a second annotation that declares the name of the table in the database schema this persistent class is mapped to. If you omit this information, the JPA provider defaults to the unqualified class name (just as Hibernate will if you omit the table name in an XML mapping file).

All of this is type-safe, and declared annotations are read with Java Reflection when Hibernate starts up. You don't need to write any XML mapping files, Hibernate doesn't need to parse any XML, and startup is faster. Your IDE can also easily validate and highlight annotations—they are regular Java types, after all.

One of the clear benefits of annotations is their flexibility for agile development. If you refactor your code, you rename, delete, or move classes and properties all the time. Most development tools and editors can't refactor XML element and attribute values, but annotations are part of the Java language and are included in all refactoring operations.

Which annotations should you apply? You have the choice among several standardized and vendor-specific packages.

Considering standards

Annotation-based metadata has a significant impact on how you write Java applications. Other programming environments, like C# and .NET, had this kind of support for quite a while, and developers adopted the metadata attributes quickly. In the Java world, the big rollout of annotations is happening with Java EE 5.0. All specifications that are considered part of Java EE, like EJB, JMS, JMX, and even the servlet specification, will be updated and use JDK 5.0 annotations for metadata needs. For example, web services in J2EE 1.4 usually require significant metadata in XML files, so we expect to see real productivity improvements with annotations. Or, you can let the web container inject an EJB handle into your servlet, by adding an annotation on a field. Sun initiated a specification effort (JSR 250) to take care of the annotations across specifications, defining common annotations for the whole Java platform. For you, however, working on a persistence layer, the most important specification is EJB 3.0 and JPA.

Annotations from the Java Persistence package are available in javax.persistence once you have included the JPA interfaces in your classpath. You can use these annotations to declare persistent entity classes, embeddable classes (we'll discuss these in the next chapter), properties, fields, keys, and so on. The JPA specification covers the basics and most relevant advanced mappings—everything you need to write a portable application, with a pluggable, standardized persistence layer that works inside and outside of any runtime container.

What annotations and mapping features aren't specified in Java Persistence? A particular JPA engine and product may naturally offer advantages—the so-called vendor extensions.

Utilizing vendor extensions

Even if you map most of your application's model with JPA-compatible annotations from the javax.persistence package, you'll have to use vendor extensions at some point. For example, almost all performance-tuning options you'd expect to be available in high-quality persistence software, such as fetching and caching settings, are only available as Hibernate-specific annotations.

Let's see what that looks like in an example. Annotate the Item entity source code again:

package auction.model;

import javax.persistence.*;

@Entity
@Table(name = "ITEM")
@org.hibernate.annotations.BatchSize(size = 10)
@org.hibernate.annotations.DiscriminatorFormula(
  "case when ITEM_IS_SPECIAL is not null then A else B end"
)
public class Item {
  ...
}

This example contains two Hibernate annotations. The first, @BatchSize, is a fetching option that can increase performance in situations we'll examine later in this book. The second, @DiscriminatorFormula, is a Hibernate mapping annotation that is especially useful for legacy schemas when class inheritance can't be determined with simple literal values (here it maps a legacy column ITEM_IS_SPECIAL—probably some kind of flag—to a literal value). Both annotations are prefixed with the org.hibernate.annotations package name. Consider this a good practice, because you can now easily see what metadata of this entity class is from the JPA specification and which tags are vendor-specific. You can also easily search your source code for "org.hibernate.annotations" and get a complete overview of all nonstandard annotations in your application in a single search result.

If you switch your Java Persistence provider, you only have to replace the vendor-specific extensions, and you can expect a similar feature set to be available with most sophisticated solutions. Of course, we hope you'll never have to do this, and it doesn't happen often in practice—just be prepared.

Annotations on classes only cover metadata that is applicable for that particular class. However, you often need metadata at a higher level, for a whole package or even the whole application. Before we discuss these options, we'd like to introduce another mapping metadata format.

XML descriptors in JPA and EJB 3.0

The EJB 3.0 and Java Persistence standard embraces annotations aggressively. However, the expert group has been aware of the advantages of XML deployment descriptors in certain situations, especially for configuration metadata that changes with each deployment. As a consequence, every annotation in EJB 3.0 and JPA can be replaced with an XML descriptor element. In other words, you don't have to use annotations if you don't want to (although we strongly encourage you to reconsider and give annotations a try, if this is your first reaction to annotations).

Let's look at an example of a JPA XML descriptor for a particular persistence unit:

<?xml version="1.0" encoding="UTF-8"?>

<entity-mappings
  xmlns="http://java.sun.com/xml/ns/persistence/orm"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation=
  "http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
  version="1.0">

  <persistence-unit-metadata>
    <xml-mapping-metadata-complete/>
    <persistence-unit-defaults>
    <schema>MY_SCHEMA</schema>
    <catalog>MY_CATALOG</catalog>
    <cascade-persist/>
    </persistence-unit-defaults>
  </persistence-unit-metadata>

  <package>auction.model</package>

  <entity class="Item" access="PROPERTY"
    metadata-complete="true">
    <attributes>
    <id name="id">
      <generated-value strategy="AUTO"/>
    </id>
    </attributes>
  </entity>

</entity-mappings>

This XML is automatically picked up by the JPA provider if you place it in a file called orm.xml in your classpath, in the META-INF directory of the persistence unit. You can see that you only have to name an identifier property for a class; as in annotations, all other properties of the entity class are automatically considered persistent with a sensible default mapping.

You can also set default mappings for the whole persistence unit, such as the schema name and default cascading options. If you include the <xml-mapping-metadata-complete> element, the JPA provider completely ignores all annotations on your entity classes in this persistence unit and relies only on the mappings as defined in the orm.xml file. You can (redundantly in this case) enable this on an entity level, with metadata-complete="true". If enabled, the JPA provider assumes that all properties of the entity are mapped in XML, and that all annotations for this entity should be ignored.

If you don't want to ignore but instead want to override the annotation metadata, first remove the global <xml-mapping-metadata-complete> element from the orm.xml file. Also remove the metadata-complete="true" attribute from any entity mapping that should override, not replace, annotations:

<entity-mappings ...>

  <package>auction.model</package>

  <entity class="Item">
    <attributes>
    <basic name="initialPrice" optional="false">
      <column name="INIT_PRICE"/>
    </basic>
    </attributes>
  </entity>

</entity-mappings>

Here you map the initialPrice property to the INIT_PRICE column and specify it isn't nullable. Any annotation on the initialPrice property of the Item class is ignored, but all other annotations on the Item class are still applied. Also note that you didn't specify an access strategy in this mapping, so field or accessor method access is used depending on the position of the @Id annotation in Item. (We'll get back to this detail in the next chapter.)

An obvious problem with XML deployment descriptors in Java Persistence is their compatibility with native Hibernate XML mapping files. The two formats aren't compatible at all, and you should make a decision to use one or the other. The syntax of the JPA XML descriptor is much closer to the actual JPA annotations than to the native Hibernate XML mapping files.

You also need to consider vendor extensions when you make a decision for an XML metadata format. The Hibernate XML format supports all possible Hibernate mappings, so if something can't be mapped in JPA/Hibernate annotations, it can be mapped with native Hibernate XML files. The same isn't true with JPA XML descriptors—they only provide convenient externalized metadata that covers the specification. Sun does not allow vendor extensions with an additional namespace.

On the other hand, you can't override annotations with Hibernate XML mapping files; you have to define a complete entity class mapping in XML.

For these reasons, we don't show all possible mappings in all three formats; we focus on native Hibernate XML metadata and JPA/Hibernate annotations. However, you'll learn enough about the JPA XML descriptor to use it if you want to.

Consider JPA/Hibernate annotations the primary choice if you're using JDK 5.0. Fall back to native Hibernate XML mapping files if you want to externalize a particular class mapping or utilize a Hibernate extension that isn't available as an annotation. Consider JPA XML descriptors only if you aren't planning to use any vendor extension (which is, in practice, unlikely), or if you want to only override a few annotations, or if you require complete portability that even includes deployment descriptors.

But what if you're stuck with JDK 1.4 (or even 1.3) and still want to benefit from the better refactoring capabilities and reduced lines of code of inline metadata?

3.3.3. Using XDoclet

The XDoclet project has brought the notion of attribute-oriented programming to Java. XDoclet leverages the Javadoc tag format (@attribute) to specify class-, field-, or method-level metadata attributes. There is even a book about XDoclet from Manning Publications, XDoclet in Action (Walls and Richards, 2004).

XDoclet is implemented as an Ant task that generates Hibernate XML metadata (or something else, depending on the plug-in) as part of the build process. Creating the Hibernate XML mapping document with XDoclet is straightforward; instead of writing it by hand, you mark up the Java source code of your persistent class with custom Javadoc tags, as shown in listing 3.5.

Listing 3-5. Using XDoclet tags to mark up Java classes with mapping metadata
/**
 * The Category class of the CaveatEmptor auction site domain model.
 *
 * @hibernate.class
 *  table="CATEGORY"
 */
public class Category {
  ...

  /**
   * @hibernate.id
   *  generator-class="native"
   *  column="CATEGORY_ID"
   */
  public Long getId() {
    return id;
  }

  ...

  /**
   * @hibernate.property
   */
  public String getName() {
    return name;
  }

  ...
}

With the annotated class in place and an Ant task ready, you can automatically generate the same XML document shown in the previous section (listing 3.4).

The downside to XDoclet is that it requires another build step. Most large Java projects are using Ant already, so this is usually a nonissue. Arguably, XDoclet mappings are less configurable at deployment time; but there is nothing stopping you from hand-editing the generated XML before deployment, so this is probably not a significant objection. Finally, support for XDoclet tag validation may not be available in your development environment. However, the latest IDEs support at least autocompletion of tag names. We won't cover XDoclet in this book, but you can find examples on the Hibernate website.

Whether you use XML files, JDK 5.0 annotations, or XDoclet, you'll often notice that you have to duplicate metadata in several places. In other words, you need to add global information that is applicable to more than one property, more than one persistent class, or even the whole application.

3.3.4. Handling global metadata

Consider the following situation: All of your domain model persistent classes are in the same package. However, you have to specify class names fully qualified, including the package, in every XML mapping file. It would be a lot easier to declare the package name once and then use only the short persistent class name. Or, instead of enabling direct field access for every single property through the access="field" mapping attribute, you'd rather use a single switch to enable field access for all properties. Class- or package-scoped metadata would be much more convenient.

Some metadata is valid for the whole application. For example, query strings can be externalized to metadata and called by a globally unique name in the application code. Similarly, a query usually isn't related to a particular class, and sometimes not even to a particular package. Other application-scoped metadata includes user-defined mapping types (converters) and data filter (dynamic view) definitions.

Let's walk through some examples of global metadata in Hibernate XML mappings and JDK 5.0 annotations.

Global XML mapping metadata

If you check the XML mapping DTD, you'll see that the <hibernate-mapping> root element has global options that are applied to the class mapping(s) inside it—some of these options are shown in the following example:

<hibernate-mapping
  schema="AUCTION"
  default-lazy="false"
  default-access="field"
  auto-import="false">

<class ...>
  ...
</class>

</hibernate-mapping>

The schema attribute enables a database schema prefix, AUCTION, used by Hibernate for all SQL statements generated for the mapped classes. By setting default-lazy to false, you enable default outer-join fetching for some class associations, a topic we'll discuss in chapter 13, section 13.1, "Defining the global fetch plan." (This default-lazy="true" switch has an interesting side effect: It switches to Hibernate 2.x default fetching behavior—useful if you migrate to Hibernate 3.x but don't want to update all fetching settings.) With default-access, you enable direct field access by Hibernate for all persistent properties of all classes mapped in this file. Finally, the auto-import setting is turned off for all classes in this file. We'll talk about importing and naming of entities in chapter 4, section 4.3, "Class mapping options."


Tip:

Mapping files with no class declarations—Global metadata is required and present in any sophisticated application. For example, you may easily import a dozen interfaces, or externalize a hundred query strings. In large-scale applications, you often create mapping files without actual class mappings, and only imports, external queries, or global filter and type definitions. If you look at the DTD, you can see that <class> mappings are optional inside the <hibernate-mapping> root element. Split up and organize your global metadata into separate files, such as AuctionTypes.hbm.xml, AuctionQueries.hbm.xml, and so on, and load them in Hibernate's configuration just like regular mapping files. However, make sure that all custom types and filters are loaded before any other mapping metadata that applies these types and filters to class mappings.


Let's look at global metadata with JDK 5.0 annotations.

Global annotation metadata

Annotations are by nature woven into the Java source code for a particular class. Although it's possible to place global annotations in the source file of a class (at the top), we'd rather keep global metadata in a separate file. This is called package metadata, and it's enabled with a file named package-info.java in a particular package directory:

@org.hibernate.annotations.TypeDefs({
  @org.hibernate.annotations.TypeDef(
    name="monetary_amount_usd",
    typeClass = MonetaryAmountType.class,
    parameters = { @Parameter(name="convertTo", value="USD") }
  ),
  @org.hibernate.annotations.TypeDef(
    name="monetary_amount_eur",
    typeClass = MonetaryAmountType.class,
    parameters = { @Parameter(name="convertTo", value="EUR") }
  )
})


@org.hibernate.annotations.NamedQueries({
  @org.hibernate.annotations.NamedQuery(
    name = "findItemsOrderByPrice",
    query = "select i from Item i order by i.initialPrice)"
  )
})

package auction.persistence.types;

This example of a package metadata file, in the package auction.persistence.types, declares two Hibernate type converters. We'll discuss the Hibernate type system in chapter 5, section 5.2, "The Hibernate type system." You can now refer to the user-defined types in class mappings by their names. The same mechanism can be used to externalize queries and to define global identifier generators (not shown in the last example).

There is a reason the previous code example only includes annotations from the Hibernate package and no Java Persistence annotations. One of the (last-minute) changes made to the JPA specification was the removal of package visibility of JPA annotations. As a result, no Java Persistence annotations can be placed in a package-info.java file. If you need portable global Java Persistence metadata, put it in an orm.xml file.

Note that you have to name a package that contains a metadata file in your Hibernate or JPA persistence unit configuration if you aren't using automatic detection—see chapter 2, section 2.2.1, "Using Hibernate Annotations."

Global annotations (Hibernate and JPA) can also be placed in the source code of a particular class, right after the import section. The syntax for the annotations is the same as in the package-info.java file, so we won't repeat it here.

You now know how to write local and global mapping metadata. Another issue in large-scale applications is the portability of metadata.

Using placeholders

In any larger Hibernate application, you'll face the problem of native code in your mapping metadata—code that effectively binds your mapping to a particular database product. For example, SQL statements, such as in formula, constraint, or filter mappings, aren't parsed by Hibernate but are passed directly through to the database management system. The advantage is flexibility—you can call any native SQL function or keyword your database system supports. The disadvantage of putting native SQL in your mapping metadata is lost database portability, because your mappings, and hence your application, will work only for a particular DBMS (or even DBMS version).

Even simple things, such as primary key generation strategies, usually aren't portable across all database systems. In the next chapter, we discuss a special identifier generator called native, which is a built-in smart primary key generator. On Oracle, it uses a database sequence to generate primary key values for rows in a table; on IBM DB2, it uses a special identity primary key column by default. This is how you map it in XML:

<class name="Category" table="CATEGORY">

  <id name="id" column="CATEGORY_ID" type="long">
    <generator class="native"/>
  </id>

  ...
</class>

We'll discuss the details of this mapping later. The interesting part is the declaration class="native" as the identifier generator. Let's assume that the portability this generator provides isn't what you need, perhaps because you use a custom identifier generator, a class you wrote that implements the Hibernate IdentifierGenerator interface:

<id name="id" column="CATEGORY_ID" type="long">
  <generator class="auction.custom.MyOracleGenerator"/>
</id>

The XML mapping file is now bound to a particular database product, and you lose the database portability of the Hibernate application. One way to deal with this issue is to use a placeholder in your XML file that is replaced during build when the mapping files are copied to the target directory (Ant supports this). This mechanism is recommended only if you have experience with Ant or already need build-time substitution for other parts of your application.

A much more elegant variation is to use custom XML entities (not related to our application's business entities). Let's assume you need to externalize an element or attribute value in your XML files to keep it portable:

<id name="id" column="CATEGORY_ID" type="long">
  <generator class="&idgenerator;"/>
</id>

The &idgenerator; value is called an entity placeholder. You can define its value at the top of the XML file as an entity declaration, as part of the document type definition:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping SYSTEM

  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
[
<!ENTITY idgenerator  "auction.custom.MyOracleGenerator">
]>

The XML parser will now substitute the placeholder on Hibernate startup, when mapping files are read.

You can take this one step further and externalize this addition to the DTD in a separate file and include the global options in all other mapping files:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping SYSTEM
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
[
<!ENTITY % globals SYSTEM "classpath://persistence/globals.dtd">
%globals;
]>

This example shows the inclusion of an external file as part of the DTD. The syntax, as often in XML, is rather crude, but the purpose of each line should be clear. All global settings are added to the globals.dtd file in the persistence package on the classpath:

<!ENTITY idgenerator  "auction.custom.MyOracleGenerator">
<!-- Add more options if needed... -->

To switch from Oracle to a different database system, just deploy a different globals.dtd file.

Often, you need not only substitute an XML element or attribute value but also to include whole blocks of mapping metadata in all files, such as when many of your classes share some common properties, and you can't use inheritance to capture them in a single location. With XML entity replacement, you can externalize an XML snippet to a separate file and include it in other XML files.

Let's assume all the persistent classes have a dateModified property. The first step is to put this mapping in its own file, say, DateModified.hbm.xml:

<property name="dateModified"
    column="DATE_MOD"
    type="timestamp"/>

This file needs no XML header or any other tags. Now you include it in the mapping file for a persistent class:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping SYSTEM
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
[

<!ENTITY datemodified SYSTEM "classpath://model/DateModified.hbm.xml">
]>

<hibernate-mapping>

<class name="Item" table="ITEM"
  <id ...>

  &datemodified;

  ...
</class>

The content of DateModified.hbm.xml will be included and be substituted for the &datemodified; placeholder. This, of course, also works with larger XML snippets.

When Hibernate starts up and reads mapping files, XML DTDs have to be resolved by the XML parser. The built-in Hibernate entity resolver looks for the hibernate-mapping-3.0.dtd on the classpath; it should find the DTD in the hibernate3.jar file before it tries to look it up on the Internet, which happens automatically whenever an entity URL is prefixed with http://hibernate.sourceforge.net/. The Hibernate entity resolver can also detect the classpath:// prefix, and the resource is then searched for in the classpath, where you can copy it on deployment. We have to repeat this FAQ: Hibernate never looks up the DTD on the Internet if you have a correct DTD reference in your mapping and the right JAR on the classpath.

The approaches we have described so far—XML, JDK 5.0 annotations, and XDoclet attributes—assume that all mapping information is known at development (or deployment) time. Suppose, however, that some information isn't known before the application starts. Can you programmatically manipulate the mapping metadata at runtime?

3.3.5. Manipulating metadata at runtime

It's sometimes useful for an application to browse, manipulate, or build new mappings at runtime. XML APIs like DOM, dom4j, and JDOM allow direct runtime manipulation of XML documents, so you could create or manipulate an XML document at runtime, before feeding it to the Configuration object.

On the other hand, Hibernate also exposes a configuration-time metamodel that contains all the information declared in your static mapping metadata. Direct programmatic manipulation of this metamodel is sometimes useful, especially for applications that allow for extension by user-written code. A more drastic approach would be complete programmatic and dynamic definition of the mapping metadata, without any static mapping. However, this is exotic and should be reserved for a particular class of fully dynamic applications, or application building kits.

The following code adds a new property, motto, to the User class:

// Get the existing mapping for User from Configuration
PersistentClass userMapping =
  cfg.getClassMapping(User.class.getName());

// Define a new column for the USER table
Column column = new Column();
column.setName("MOTTO");
column.setNullable(false);
column.setUnique(true);
userMapping.getTable().addColumn(column);

// Wrap the column in a Value
SimpleValue value = new SimpleValue();
value.setTable( userMapping.getTable() );
value.setTypeName("string");
value.addColumn(column);

// Define a new property of the User class
Property prop = new Property();
prop.setValue(value);
prop.setName("motto");
prop.setNodeName(prop.getName());
userMapping.addProperty(prop);

// Build a new session factory, using the new mapping
SessionFactory sf = cfg.buildSessionFactory();

A PersistentClass object represents the metamodel for a single persistent class, and you retrieve it from the Configuration object. Column, SimpleValue, and Property are all classes of the Hibernate metamodel and are available in the org.hibernate.mapping package.


Tip:

Keep in mind that adding a property to an existing persistent class mapping, as shown here, is quite easy, but programmatically creating a new mapping for a previously unmapped class is more involved.


Once a SessionFactory is created, its mappings are immutable. The SessionFactory uses a different metamodel internally than the one used at configuration time. There is no way to get back to the original Configuration from the SessionFactory or Session. (Note that you can get the SessionFactory from a Session if you wish to access a global setting.) However, the application can read the SessionFactory's metamodel by calling getClassMetadata() or getCollectionMetadata(). Here's an example:

Item item = ...;
ClassMetadata meta = sessionFactory.getClassMetadata(Item.class);
String[] metaPropertyNames =
    meta.getPropertyNames();
Object[] propertyValues =
     meta.getPropertyValues(item, EntityMode.POJO);

This code snippet retrieves the names of persistent properties of the Item class and the values of those properties for a particular instance. This helps you write generic code. For example, you may use this feature to label UI components or improve log output.

Although you've seen some mapping constructs in the previous sections, we haven't introduced any more sophisticated class and property mappings so far. You should now decide which mapping metadata option you'd like to use in your project and then read more about class and property mappings in the next chapter.

Or, if you're already an experienced Hibernate user, you can read on and find out how the latest Hibernate version allows you to represent a domain model without Java classes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.148.112.211