Mapping persistent classes (Hibernate)

 

This topic presents the fundamental mapping options, explaining how classes and properties are mapped to tables and columns. We show and discuss how you can handle database identity and primary keys, and how various other metadata settings can be used to customize how Hibernate loads and stores objects. All mapping examples are done in Hibernate’s native XML format, and with JPA annotations and XML descriptors, side by side. We also look closely at the mapping of fine-grained domain models, and at how properties and embedded components are mapped.

First, though, we define the essential distinction between entities and value types, and explain how you should approach the object/relational mapping of your domain model.

Understanding entities and value types

Entities are persistent types that represent first-class business objects (the term object is used here in its natural sense). In other words, some of the classes and types you have to deal with in an application are more important, which naturally makes others less important. You probably agree that in CaveatEmptor, Item is a more important class than String. User is probably more important than Address. What makes something important? Let’s look at the issue from a different perspective.

Fine-grained domain models

A major objective of Hibernate is support for fine-grained domain models, which we isolated as the most important requirement for a rich domain model. It’s one reason why we work with POJOs. In crude terms, fine-grained means more classes than tables.

For example, a user may have both a billing address and a home address. In the database, you may have a single USERS table with the columns BILLING_STREET, BILLING_CITY, and BILLING_ZIPCODE, along with HOME_STREET, HOME_CITY, and HOME_ZIPCODE.

In the domain model, you could use the same approach, representing the two addresses as six string-valued properties of the User class. But it’s much better to model this using an Address class, where User has the billingAddress and homeAddress properties, thus using three classes for one table.

This domain model achieves improved cohesion and greater code reuse, and it’s more understandable than SQL systems with inflexible type systems. In the past, many ORM solutions didn’t provide especially good support for this kind of mapping.

Hibernate emphasizes the usefulness of fine-grained classes for implementing type safety and behavior. For example, many people model an email address as a string-valued property of User. A more sophisticated approach is to define an EmailAddress class, which adds higher-level semantics and behavior—it may provide a sendEmail() method.

This granularity problem leads us to a distinction of central importance in ORM. In Java, all classes are of equal standing—all objects have their own identity and lifecycle.

Let’s walk through an example.

Defining the concept

Two people live in the same apartment, and they both register user accounts in CaveatEmptor. Naturally, each account is represented by one instance of User, so you have two entity instances. In the CaveatEmptor model, the User class has a homeAddress association with the Address class. Do both User instances have a runtime reference to the same Address instance or does each User instance have a reference to its own Address? If Address is supposed to support shared runtime references, it’s an entity type. If not, it’s likely a value type and hence is dependent on a single reference by an owning entity instance, which also provides identity.

We advocate a design with more classes than tables: One row represents multiple instances. Because database identity is implemented by primary key value, some persistent objects won’t have their own identity. In effect, the persistence mechanism implements pass-by-value semantics for some classes! One of the objects represented in the row has its own identity, and others depend on that. In the previous example, the columns in the USERS table that contain address information are dependent on the identifier of the user, the primary key of the table. An instance of Address is dependent on an instance of User.

Hibernate makes the following essential distinction:

■ An object of entity type has its own database identity (primary key value). An object reference to an entity instance is persisted as a reference in the database (a foreign key value). An entity has its own lifecycle; it may exist independently of any other entity. Examples in CaveatEmptor are User, Item, and Category.

■ An object of value type has no database identity; it belongs to an entity instance and its persistent state is embedded in the table row of the owning entity. Value types don’t have identifiers or identifier properties. The lifespan of a value type instance is bounded by the lifespan of the owning entity instance. A value type doesn’t support shared references: If two users live in the same apartment, they each have a reference to their own homeAd-dress instance. The most obvious value types are classes like Strings and Integers, but all JDK classes are considered value types. User-defined classes can also be mapped as value types; for example, CaveatEmptor has Address and MonetaryAmount.

Identification of entities and value types in your domain model isn’t an ad hoc task but follows a certain procedure.

Identifying entities and value types

You may find it helpful to add stereotype information to your UML class diagrams so you can immediately see and distinguish entities and value types. This practice also forces you to think about this distinction for all your classes, which is a first step to an optimal mapping and well-performing persistence layer. See figure 4.1 for an example.

The Item and User classes are obvious entities. They each have their own identity, their instances have references from many other instances (shared references), and they have independent lifecycles.

Identifying the Address as a value type is also easy: A particular Address instance is referenced by only a single User instance. You know this because the association has been created as a composition, where the User instance has been made fully responsible for the lifecycle of the referenced Address instance. Therefore, Address objects can’t be referenced by anyone else and don’t need their own identity.

The Bid class is a problem. In object-oriented modeling, you express a composition (the association between Item and Bid with the diamond), and an Item manages the lifecycles of all the Bid objects to which it has a reference (it’s a collection of references). This seems reasonable, because the bids would be useless if an Item no longer existed. But at the same time, there is another association to Bid: An Item may hold a reference to its successfulBid. The successful bid must also be one of the bids referenced by the collection, but this isn’t expressed in the UML. In any case, you have to deal with possible shared references to Bid instances, so the Bid class needs to be an entity. It has a dependent lifecycle, but it must have its own identity to support shared references.

Stereotypes for entities and value types have been added to the diagram.

Figure 4.1 Stereotypes for entities and value types have been added to the diagram.

You’ll often find this kind of mixed behavior; however, your first reaction should be to make everything a value-typed class and promote it to an entity only when absolutely necessary. Try to simplify your associations: Collections, for example, sometimes add complexity without offering any advantages. Instead of mapping a persistent collection of Bid references, you can write a query to obtain all the bids for an Item.

As the next step, take your domain model diagram and implement POJOs for all entities and value types. You have to take care of three things:

■ Shared references—Write your POJO classes in a way that avoids shared references to value type instances. For example, make sure an Address object can be referenced by only one User. For example, make it immutable and enforce the relationship with the Address constructor.

■ Lifecycle dependencies—As discussed, the lifecycle of a value-type instance is bound to that of its owning entity instance. If a User object is deleted, its Address dependent object(s) have to be deleted as well. There is no notion or keyword for this in Java, but your application workflow and user interface must be designed to respect and expect lifecycle dependencies. Persistence metadata includes the cascading rules for all dependencies.

■ Identity —Entity classes need an identifier property in almost all cases. User-defined value-type classes (and JDK classes) don’t have an identifier property, because instances are identified through the owning entity.

We’ll come back to class associations and lifecycle rules when we discuss more advanced mappings later in the topic. However, object identity is a subject you have to understand at this point.


Mapping entities with identity

It’s vital to understand the difference between object identity and object equality before we discuss terms like database identity and the way Hibernate manages identity. Next, we explore how object identity and equality relate to database (primary key) identity.

Understanding Java identity and equality

Java developers understand the difference between Java object identity and equality. Object identity, ==, is a notion defined by the Java virtual machine. Two object references are identical if they point to the same memory location.

On the other hand, object equality is a notion defined by classes that implement the equals() method, sometimes also referred to as equivalence. Equivalence means that two different (nonidentical) objects have the same value. Two different instances of String are equal if they represent the same sequence of characters, even though they each have their own location in the memory space of the virtual machine. (If you’re a Java guru, we acknowledge that String is a special case. Assume we used a different class to make the same point.)

Persistence complicates this picture. With object/relational persistence, a persistent object is an in-memory representation of a particular row of a database table. Along with Java identity (memory location) and object equality, you pick up database identity (which is the location in the persistent data store). You now have three methods for identifying objects:

■ Objects are identical if they occupy the same memory location in the JVM. This can be checked by using the == operator. This concept is known as object identity.

■ Objects are equal if they have the same value, as defined by the equals(Object o) method. Classes that don’t explicitly override this method inherit the implementation defined by java.lang.Object, which compares object identity. This concept is known as equality.

■ Objects stored in a relational database are identical if they represent the same row or, equivalently, if they share the same table and primary key value. This concept is known as database identity.

We now need to look at how database identity relates to object identity in Hibernate, and how database identity is expressed in the mapping metadata.

Handling database identity

Hibernate exposes database identity to the application in two ways:

■ The value of the identifier property of a persistent instance

■ The value returned by Session.getIdentifier(Object entity)

Adding an identifier property to entities

The identifier property is special—its value is the primary key value of the database row represented by the persistent instance. We don’t usually show the identifier property in the domain model diagrams. In the examples, the identifier property is always named id. If myCategory is an instance of Category, calling myCategory.getId() returns the primary key value of the row represented by myCategory in the database.

Let’s implement an identifier property for the Category class:

tmp9A169_thumb

Should you make the accessor methods for the identifier property private scope or public? Well, database identifiers are often used by the application as a convenient handle to a particular instance, even outside the persistence layer. For example, it’s common for web applications to display the results of a search screen to the user as a list of summary information. When the user selects a particular element, the application may need to retrieve the selected object, and it’s common to use a lookup by identifier for this purpose—you’ve probably already used identifiers this way, even in applications that rely on JDBC. It’s usually appropriate to fully expose the database identity with a public identifier property accessor.

On the other hand, you usually declare the setId() method private and let Hibernate generate and set the identifier value. Or, you map it with direct field access and implement only a getter method. (The exception to this rule is classes with natural keys, where the value of the identifier is assigned by the application before the object is made persistent instead of being generated by Hibernate. Hibernate doesn’t allow you to change the identifier value of a persistent instance after it’s first assigned. A primary key value never changes—otherwise the attribute wouldn’t be a suitable primary key candidate!

The Java type of the identifier property, java.lang.Long in the previous example, depends on the primary key type of the CATEGORY table and how it’s mapped in Hibernate metadata.

Mapping the identifier property

A regular (noncomposite) identifier property is mapped in Hibernate XML files with the <id> element:

tmp9A170_thumb

The identifier property is mapped to the primary key column CATEGORY_ID of the table CATEGORY. The Hibernate type for this property is long, which maps to a BIGINT column type in most databases and which has also been chosen to match the type of the identity value produced by the native identifier generator. (We discuss identifier generation strategies in the next section.)

For a JPA entity class, you use annotations in the Java source code to map the identifier property:

tmp9A171_thumb

The @Id annotation on the getter method marks it as the identifier property, and @GeneratedValue with the GenerationType.AUTO option translates into a native identifier generation strategy, like the native option in XML Hibernate mappings. Note that if you don’t define a strategy, the default is also Generation-

Type.AUTO, so you could have omitted this attribute altogether. You also specify a database column—otherwise Hibernate would use the property name. The mapping type is implied by the Java property type, java.lang.Long.

Of course, you can also use direct field access for all properties, including the database identifier:

tmp9A172_thumb

Mapping annotations are placed on the field declaration when direct field access is enabled, as defined by the standard.

Whether field or property access is enabled for an entity depends on the position of the mandatory @Id annotation. In the preceding example, it’s present on a field, so all attributes of the class are accessed by Hibernate through fields. The example before that, annotated on the getId() method, enables access to all attributes through getter and setter methods.

Alternatively, you can use JPA XML descriptors to create your identifier mapping:

tmp9A173_thumb

In addition to operations for testing Java object identity, (a == b), and object equality, ( a.equals(b) ), you may now use a. getId (). equals ( b.getId() ) to test database identity. What do these notions have in common? In what situations do they all return true? The time when all are true is called the scope of guaranteed object identity; “Object identity and equality.”

Using database identifiers in Hibernate is easy and straightforward. Choosing a good primary key (and key-generation strategy) may be more difficult. We discuss this issue next.

Database primary keys

Hibernate needs to know your preferred strategy for generating primary keys. First, though, let’s define primary key.

Selecting a primary key

The candidate key is a column or set of columns that could be used to identify a particular row in a table. To become a primary key, a candidate key must satisfy the following properties:

■ Its value (for any column of the candidate key) is never null.

■ Each row has a unique value.

■ The value of a particular row never changes.

If a table has only one identifying attribute, it’s, by definition, the primary key. However, several columns or combinations of columns may satisfy these properties for a particular table; you choose between candidate keys to decide the best primary key for the table. Candidate keys not chosen as the primary key should be declared as unique keys in the database.

Many legacy SQL data models use natural primary keys. A natural key is a key with business meaning: an attribute or combination of attributes that is unique by virtue of its business semantics. Examples of natural keys are the U.S. Social Security Number and Australian Tax File Number. Distinguishing natural keys is simple: If a candidate key attribute has meaning outside the database context, it’s a natural key, whether or not it’s automatically generated. Think about the application users: If they refer to a key attribute when talking about and working with the application, it’s a natural key.

Experience has shown that natural keys almost always cause problems in the long run. A good primary key must be unique, constant, and required (never null or unknown). Few entity attributes satisfy these requirements, and some that do can’t be efficiently indexed by SQL databases (although this is an implementation detail and shouldn’t be the primary motivation for or against a particular key). In addition, you should make certain that a candidate key definition can never change throughout the lifetime of the database before making it a primary key. Changing the value (or even definition) of a primary key, and all foreign keys that refer to it, is a frustrating task. Furthermore, natural candidate keys can often be found only by combining several columns in a composite natural key. These composite keys, although certainly appropriate for some relations (like a link table in a many-to-many relationship), usually make maintenance, ad-hoc queries, and schema evolution much more difficult.

For these reasons, we strongly recommend that you consider synthetic identifiers, also called surrogate keys. Surrogate keys have no business meaning—they’re unique values generated by the database or application. Application users ideally don’t see or refer to these key values; they’re part of the system internals. Introducing a surrogate key column is also appropriate in a common situation: If there are no candidate keys, a table is by definition not a relation as defined by the relational model—it permits duplicate rows—and so you have to add a surrogate key column. There are a number of well-known approaches to generating surrogate key values.

Selecting a key generator

Hibernate has several built-in identifier-generation strategies. We list the most useful options in table 4.1.

Table 4.1 Hibernate’s built-in identifier-generator modules

Generator name

JPA

GenerationType

Options

Description

native

AUTO

The native identity generator picks other identity generators like identity, sequence, or hilo, depending on the capabilities of the underlying database. Use this generator to keep your mapping metadata portable to different database management systems.

identity

IDENTITY

This generator supports identity columns in DB2, MySQL, MS SQL Server, Sybase, and HypersonicSQL. The returned identifier is of type long, short, or int.

Generator name

JPA

GenerationType

Options

Description

sequence

SEQUENCE

sequence, parameters

This generator creates a sequence in DB2, PostgreSQL, Oracle, SAP DB, or Mckoi; or a generator in InterBase is used. The returned identifier is of type long, short, or int. Use the sequence option to define a catalog name for the sequence (hibernate_ sequence is the default) and parameters if you need additional settings creating a sequence to be added to the DDL.

increment

(Not available)

At Hibernate startup, this generator reads the maximum (numeric) primary key column value of the table and increments the value by one each time a new row is inserted. The generated identifier is of type long, short, or int. This generator is especially efficient if the single-server Hibernate application has exclusive access to the database but should not be used in any other scenario.

hilo

(Not available)

table, column, max_lo

A high/low algorithm is an efficient way to generate identifiers of type long, given a table and column (by default hibernate_unique_key and next, respectively) as a source of high values. The high/low algorithm generates identifiers that are unique only for a particular database. High values are retrieved from a global source and are made unique by adding a local low value. This algorithm avoids congestion when a single source for identifier values has to be accessed for many inserts. See “Data Modeling 101″ (Ambler, 2002) for more information about the high/low approach to unique identifiers. This generator needs to use a separate database connection from time to time to retrieve high values, so it isn’t supported with user-supplied database connections. In other words, don’t use it with

sessionFactory.openSession(myCo nnection). The max_lo option defines how many low values are added until a new high value is fetched. Only settings greater than 1 are sensible; the default is 32767 (Short.MAX_VALUE).

Generator name

JPA

GenerationType

Options

Description

seqhilo

(Not available)

sequence,

parameters,

max_lo

This generator works like the regular hilo generator, except it uses a named database sequence to generate high values.

(JPA only)

TABLE

table, catalog, schema, pkColumnName, valueColumnNam e,

pkColumnValue, allocationSize

Much like Hibernate’s hilo strategy, TABLE relies on a database table that holds the last-generated integer primary key value, and each generator is mapped to one row in this table. Each row has two columns: pkColumnName and valueColumnName. The pkColumnValue assigns each row to a particular generator, and the value column holds the last retrieved primary key. The persistence provider allocates up to allocationSize integers in each turn.

uuid.hex

(Not available)

separator

This generator is a 128-bit UUID (an algorithm that generates identifiers of type string, unique within a network). The IP address is used in combination with a unique timestamp. The UUID is encoded as a string of hexadecimal digits of length 32, with an optional separator string between each component of the UUID representation. Use this generator strategy only if you need globally unique identifiers, such as when you have to merge two databases regularly.

guid

(Not available)

This generator provides a database-generated globally unique identifier string on MySQL and SQL Server.

select

(Not available)

key

This generator retrieves a primary key assigned by a database trigger by selecting the row by some unique key and retrieving the primary key value. An additional unique candidate key column is required for this strategy, and the key option has to be set to the name of the unique key column.

Some of the built-in identifier generators can be configured with options. In a native Hibernate XML mapping, you define options as pairs of keys and values:

The @GenericGenerator Hibernate extension can be used to give a Hibernate identifier generator a name, in this case hibernate-uuid. This name is then referenced by the standardized generator attribute.

This declaration of a generator and its assignment by name also must be applied for sequence- or table-based identifier generation with annotations. Imagine that you want to use a customized sequence generator in all your entity classes. Because this identifier generator has to be global, it’s declared in orm.xml:

tmp9B1_thumb

This declares that a database sequence named MY_SEQUENCE with an initial value of 123 can be used as a source for database identifier generation, and that the persistence engine should obtain 20 values every time it needs identifiers. (Note, though, that Hibernate Annotations, at the time of writing, ignores the initial-Value setting.)

To apply this identifier generator for a particular entity, use its name:

tmp9B-2_thumb

You can use Hibernate identifier generators with annotations, even if no direct annotation is available:

tmp9B-3_thumb

tmp9B-4_thumb

If you declared another generator with the same name at the entity level, before the class keyword, it would override the global identifier generator. The same approach can be used to declare and apply a @TableGenerator.

You aren’t limited to the built-in strategies; you can create your own identifier generator by implementing Hibernate’s IdentifierGenerator interface. As always, it’s a good strategy to look at the Hibernate source code of the existing identifier generators for inspiration.

It’s even possible to mix identifier generators for persistent classes in a single domain model, but for nonlegacy data we recommend using the same identifier generation strategy for all entities.

For legacy data and application-assigned identifiers, the picture is more complicated. In this case, we’re often stuck with natural keys and especially composite keys. A composite key is a natural key that is composed of multiple table columns. Because composite identifiers can be a bit more difficult to work with and often only appear on legacy schemas.

We assume from now on that you’ve added identifier properties to the entity classes of your domain model, and that after you completed the basic mapping of each entity and its identifier property, you continued to map value-typed properties of the entities. However, some special options can simplify or enhance your class mappings.

Class mapping options

If you check the <hibernate-mapping> and <class> elements in the DTD (or the reference documentation), you’ll find a few options we haven’t discussed so far:

■ Dynamic generation of CRUD SQL statements

■ Entity mutability control

■ Naming of entities for querying

■ Mapping package names

■ Quoting keywords and reserved database identifiers

■ Implementing database naming conventions

Dynamic SQL generation

By default, Hibernate creates SQL statements for each persistent class on startup. These statements are simple create, read, update, and delete operations for reading a single row, deleting a row, and so on.

How can Hibernate create an UPDATE statement on startup? After all, the columns to be updated aren’t known at this time. The answer is that the generated SQL statement updates all columns, and if the value of a particular column isn’t modified, the statement sets it to its old value.

In some situations, such as a legacy table with hundreds of columns where the SQL statements will be large for even the simplest operations (say, only one column needs updating), you have to turn off this startup SQL generation and switch to dynamic statements generated at runtime. An extremely large number of entities can also impact startup time, because Hibernate has to generate all SQL statements for CRUD upfront. Memory consumption for this query statement cache will also be high if a dozen statements must be cached for thousands of entities (this isn’t an issue, usually).

Two attributes for disabling CRUD SQL generation on startup are available on the <class> mapping element:

tmp9B5_thumb

The dynamic-insert attribute tells Hibernate whether to include null property values in an SQL INSERT, and the dynamic-update attribute tells Hibernate whether to include unmodified properties in the SQL UPDATE.

If you’re using JDK 5.0 annotation mappings, you need a native Hibernate annotation to enable dynamic SQL generation:

tmp9B-6_thumb

The second @Entity annotation from the Hibernate package extends the JPA annotation with additional options, including dynamicInsert and dynamicUpdate.

Sometimes you can avoid generating any UPDATE statement, if the persistent class is mapped immutable.

Making an entity immutable

Instances of a particular class may be immutable. For example, in CaveatEmptor, a Bid made for an item is immutable. Hence, no UPDATE statement ever needs to be executed on the BID table. Hibernate can also make a few other optimizations, such as avoiding dirty checking, if you map an immutable class with the mutable attribute set to false:

tmp9B7_thumb

A POJO is immutable if no public setter methods for any properties of the class are exposed—all values are set in the constructor. Instead of private setter methods, you often prefer direct field access by Hibernate for immutable persistent classes, so you don’t have to write useless accessor methods. You can map an immutable entity using annotations:

tmp9B-8_thumb

Again, the native Hibernate @Entity annotation extends the JPA annotation with additional options. We have also shown the Hibernate extension annotation @AccessType here—this is an annotation you’ll rarely use. As explained earlier, the default access strategy for a particular entity class is implicit from the position of the mandatory @Id property. However, you can use @AccessType to force a more fine-grained strategy; it can be placed on class declarations (as in the preceding example) or even on particular fields or accessor methods.

Let’s have a quick look at another issue, the naming of entities for queries.

Naming entities for querying

By default, all class names are automatically “imported” into the namespace of the Hibernate query language, HQL. In other words, you can use the short class names without a package prefix in HQL, which is convenient. However, this auto-import can be turned off if two classes with the same name exist for a given Ses-sionFactory, maybe in different packages of the domain model.

If such a conflict exists, and you don’t change the default settings, Hibernate won’t know which class you’re referring to in HQL. You can turn off auto-import of names into the HQL namespace for particular mapping files with the auto-import=”false” setting on the <hibernate-mapping> root element.

Entity names can also be imported explicitly into the HQL namespace. You can even import classes and interfaces that aren’t explicitly mapped, so a short name can be used in polymorphic HQL queries:

tmp9B9_thumb

You can now use an HQL query such as from IAuditable to retrieve all persistent instances of classes that implement the auction.model.Auditable interface. (Don’t worry if you don’t know whether this feature is relevant to you at this point; we’ll get back to queries later in the topic.) Note that the <import> element, like all other immediate child elements of <hibernate-mapping>, is an application-wide declaration, so you don’t have to (and can’t) duplicate this in other mapping files.

With annotations, you can give an entity an explicit name, if the short name would result in a collision in the JPA QL or HQL namespace:

tmp9B10_thumb

Now let’s consider another aspect of naming: the declaration of packages.

Declaring a package name

All the persistent classes of the CaveatEmptor application are declared in the Java package auction.model. However, you don’t want to repeat the full package name whenever this or any other class is named in an association, subclass, or component mapping. Instead, specify a package attribute:

tmp9B11_thumb

Now all unqualified class names that appear in this mapping document will be prefixed with the declared package name. We assume this setting in all mapping examples in this topic and use unqualified names for CaveatEmptor model classes.

Names of classes and tables must be selected carefully. However, a name you’ve chosen may be reserved by the SQL database system, so the name has to be quoted.

Quoting SQL identifiers

By default, Hibernate doesn’t quote table and column names in the generated SQL. This makes the SQL slightly more readable, and it also allows you to take advantage of the fact that most SQL databases are case insensitive when comparing unquoted identifiers. From time to time, especially in legacy databases, you encounter identifiers with strange characters or whitespace, or you wish to force case sensitivity. Or, if you rely on Hibernate’s defaults, a class or property name in Java may be automatically translated to a table or column name that isn’t allowed in your database management system. For example, the User class is mapped to a USER table, which is usually a reserved keyword in SQL databases. Hibernate doesn’t know the SQL keywords of any DBMS product, so the database system throws an exception at startup or runtime.

If you quote a table or column name with backticks in the mapping document, Hibernate always quotes this identifier in the generated SQL. The following property declaration forces Hibernate to generate SQL with the quoted column name “DESCRIPTION”. Hibernate also knows that Microsoft SQL Server needs the variation [DESCRIPTION] and that MySQL requires “DESCRIPTION”.

tmp9B12_thumb

There is no way, apart from quoting all table and column names in backticks, to force Hibernate to use quoted identifiers everywhere. You should consider renaming tables or columns with reserved keyword names whenever possible. Quoting with backticks works with annotation mappings, but it’s an implementation detail of Hibernate and not part of the JPA specification.

Implementing naming conventions

We often encounter organizations with strict conventions for database table and column names. Hibernate provides a feature that allows you to enforce naming standards automatically.

Suppose that all table names in CaveatEmptor should follow the pattern CE_<table name>. One solution is to manually specify a table attribute on all <class> and collection elements in the mapping files. However, this approach is time-consuming and easily forgotten. Instead, you can implement Hibernate’s NamingStrategy interface, as in listing 4.1.

Listing 4.1 NamingStrategy implementation

NamingStrategy implementation

You extend the ImprovedNamingStrategy, which provides default implementations for all methods of NamingStrategy you don’t want to implement from scratch (look at the API documentation and source). The classToTableName() method is called only if a <class> mapping doesn’t specify an explicit table name. The propertyToColumnName() method is called if a property has no explicit column name. The tableName() and columnName() methods are called when an explicit name is declared.

If you enable this CENamingStrategy, the class mapping declaration

tmp9B14_thumb

results in CE_BANKACCOUNT as the name of the table. However, if a table name is specified, like this,

tmp9B15_thumb

then CE_BANK_ACCOUNT is the name of the table. In this case, BANK_ACCOUNT is passed to the tableName() method.

This allows you to have multiple SessionFactory instances based on the same mapping documents, each using a different NamingStrategy. This is extremely useful in a multiclient installation, where unique table names (but the same data model) are required for each client. However, a better way to handle this kind of requirement is to use an SQL schema (a kind of namespace).

You can set a naming strategy implementation in Java Persistence in your per-sistence.xml file with the hibernate.ejb.naming_strategy option.

Now that we have covered the concepts and most important mappings for entities, let’s map value types.

Fine-grained models and mappings

After spending the first half of this topic almost exclusively on entities and the respective basic persistent class-mapping options, we’ll now focus on value types in their various forms. Two different kinds come to mind immediately: value-typed classes that came with the JDK, such as String or primitives, and value-typed classes defined by the application developer, such as Address and MonetaryAmount.

First, you map persistent class properties that use JDK types and learn the basic mapping elements and attributes. Then you attack custom value-typed classes and map them as embeddable components.

Mapping basic properties

If you map a persistent class, no matter whether it’s an entity or a value type, all persistent properties have to be mapped explicitly in the XML mapping file. On the other hand, if a class is mapped with annotations, all of its properties are considered persistent by default. You can mark properties with the @javax.persis-tence.Transient annotation to exclude them, or use the transient Java keyword (which usually only excludes fields for Java serialization).

In a JPA XML descriptor, you can exclude a particular field or property:

The best feature of the NamingStrategy interface is the potential for dynamic behavior. To activate a specific naming strategy, you can pass an instance to the Hibernate Configuration at startup:

tmp9B-16_thumb

tmp9B-17_thumb

A typical Hibernate property mapping defines a POJO’s property name, a database column name, and the name of a Hibernate type, and it’s often possible to omit the type. So, if description is a property of (Java) type java.lang.String, Hibernate uses the Hibernate type string by default (we come back to the Hibernate type system in the next topic).

Hibernate uses reflection to determine the Java type of the property. Thus, the following mappings are equivalent:

tmp9B-18_thumb

It’s even possible to omit the column name if it’s the same as the property name, ignoring case. (This is one of the sensible defaults we mentioned earlier.)

For some more unusual cases, which you’ll see more about later, you may need to use a <column> element instead of the column attribute in your XML mapping. The <column> element provides more flexibility: It has more optional attributes and may appear more than once. (A single property can map to more than one column, a technique we discuss in the next topic.) The following two property mappings are equivalent:

tmp9B-19_thumb

The <property> element (and especially the <column> element) also defines certain attributes that apply mainly to automatic database schema generation.  However, it’s preferable to include at least the not-null attribute, because Hibernate can then report illegal null property values without going to the database:

tmp9B-20_thumb

JPA is based on a configuration by exception model, so you could rely on defaults. If a property of a persistent class isn’t annotated, the following rules apply:

■ If the property is of a JDK type, it’s automatically persistent. In other words, it’s handled like <property name= “propertyName”/> in a Hibernate XML mapping file.

■ Otherwise, if the class of the property is annotated as @Embeddable, it’s mapped as a component of the owning class. We’ll discuss embedding of components later in this topic.

■ Otherwise, if the type of the property is Serializable, its value is stored in its serialized form. This usually isn’t what you want, and you should always map Java classes instead of storing a heap of bytes in the database. Imagine maintaining a database with this binary information when the application is gone in a few years.

If you don’t want to rely on these defaults, apply the @Basic annotation on a particular property. The @Column annotation is the equivalent of the XML <column> element. Here is an example of how you declare a property’s value as required:

tmp9B-21_thumb

The @Basic annotation marks the property as not optional on the Java object level. The second setting, nullable = false on the column mapping, is only responsible for the generation of a NOT NULL database constraint. The Hibernate JPA implementation treats both options the same way in any case, so you may as well use only one of the annotations for this purpose.

In a JPA XML descriptor, this mapping looks the same:

tmp9B-22_thumb

Quite a few options in Hibernate metadata are available to declare schema constraints, such as NOT NULL on a column. Except for simple nullability, however, they’re only used to produce DDL when Hibernate exports a database schema from mapping metadata. We’ll discuss customization of SQL, including DDL,  the Hibernate Annotations package includes a more advanced and sophisticated data validation framework, which you can use not only to define database schema constraints in DDL, but also for data validation at runtime.

Are annotations for properties always on the accessor methods? Customizing property access

Properties of a class are accessed by the persistence engine either directly (through fields) or indirectly (through getter and setter property accessor methods). In XML mapping files, you control the default access strategy for a class with the default-access=”field|property|noop|custom.Class” attribute of the hibernate-mapping root element. An annotated entity inherits the default from the position of the mandatory @Id annotation. For example, if @Id has been declared on a field, not a getter method, all other property mapping annotations, like the name of the column for the item’s description property, are also declared on fields:

tmp9B23_thumb

This is the default behavior as defined by the JPA specification. However, Hibernate allows flexible customization of the access strategy with the @org.hiber-nate.annotations.AccessType(<strategy>) annotation:

■ If AccessType is set on the class/entity level, all attributes of the class are accessed according to the selected strategy. Attribute-level annotations are expected on either fields or getter methods, depending on the strategy. This setting overrides any defaults from the position of the standard @Id annotations.

■ If an entity defaults or is explicitly set for field access, the Access-Type(“property”) annotation on a field switches this particular attribute to runtime access through property getter/setter methods. The position of the AccessType annotation is still the field.

■ If an entity defaults or is explicitly set for property access, the AccessType(“field”) annotation on a getter method switches this particular attribute to runtime access through a field of the same name. The position of the AccessType annotation is still the getter method.

■ Any @Embedded class inherits the default or explicitly declared access strategy of the owning root entity class.

■ Any @MappedSuperclass properties are accessed with the default or explicitly declared access strategy of the mapped entity class.

Or, you can set the access strategy for all class mappings inside a root <hibernate-mapping> element with the default-access attribute.

Another strategy besides field and property access that can be useful is noop. It maps a property that doesn’t exist in the Java persistent class. This sounds strange, but it lets you refer to this “virtual” property in HQL queries (in other words, to use the database column in HQL queries only).

If none of the built-in access strategies are appropriate, you can define your own customized property-access strategy by implementing the interface org.hibernate.property.PropertyAccessor. Set the (fully qualified) class name on the access mapping attribute or @AccessType annotation. Have a look at the Hibernate source code for inspiration; it’s a straightforward exercise.

Some properties don’t map to a column at all. In particular, a derived property takes its value from an SQL expression.

Using derived properties

The value of a derived property is calculated at runtime by evaluating an expression that you define using the formula attribute. For example, you may map a totalIncludingTax property to an SQL expression:

tmp9B24_thumb

The given SQL formula is evaluated every time the entity is retrieved from the database (and not at any other time, so the result may be outdated if other properties are modified). The property doesn’t have a column attribute (or sub-element) and never appears in an SQL INSERT or UPDATE, only in SELECTs. Formulas may refer to columns of the database table, they can call SQL functions, and they may even include SQL subselects. The SQL expression is passed to the underlying database as is; this is a good chance to bind your mapping file to a particular database product, if you aren’t careful and rely on vendor-specific operators or keywords.

Formulas are also available with a Hibernate annotation:

You can also control access strategies on the property level in Hibernate XML mappings with the access attribute:

tmp9B25_thumb

tmp9B-26_thumb

Notice that unqualified column names refer to columns of the table of the class to which the derived property belongs.

Another special kind of property relies on database-generated values.

Generated and default property values

Imagine a particular property of a class has its value generated by the database, usually when the entity row is inserted for the first time. Typical database-generated values are timestamp of creation, a default price for an item, and a trigger that runs for every modification.

Typically, Hibernate applications need to refresh objects that contain any properties for which the database generates values. Marking properties as generated, however, lets the application delegate this responsibility to Hibernate. Essentially, whenever Hibernate issues an SQL INSERT or UPDATE for an entity that has defined generated properties, it immediately does a SELECT afterwards to retrieve the generated values. Use the generated switch on a property mapping to enable this automatic refresh:

tmp9B27_thumb

Properties marked as database-generated must additionally be noninsertable and nonupdateable, which you control with the insert and update attributes. If both are set to false, the property’s columns never appear in the INSERT or UPDATE statements—the property value is read-only. Also, you usually don’t add a public setter method in your class for an immutable property (and switch to field access).

With annotations, declare immutability (and automatic refresh) with the @Generated Hibernate annotation:

tmp9B28_thumb

The following example uses a correlated subselect to calculate the average amount of all bids for an item:

tmp9B29_thumb

tmp9B-30_thumb

The settings available are GenerationTime.ALWAYS and GenerationTime.INSERT, and the equivalent options in XML mappings are generated=”always” and gen-erated=”insert”.

A special case of database-generated property values are default values. For example, you may want to implement a rule that every auction item costs at least $1. First, you’d add this to your database catalog as the default value for the

INITIAL_PRICE column:

tmp9B31_thumb

If you use Hibernate’s schema export tool, hbm2ddl, you can enable this output by adding a default attribute to the property mapping:

tmp9B32_thumb

Note that you also have to enable dynamic insertion and update statement generation, so that the column with the default value isn’t included in every statement if its value is null (otherwise a NULL would be inserted instead of the default value). Furthermore, an instance of Item that has been made persistent but not yet flushed to the database and not refreshed again won’t have the default value set on the object property. In other words, you need to execute an explicit flush:

tmp9B33_thumb

Because you set generated=”insert”, Hibernate knows that an immediate additional SELECT is required to read the database-generated property value.

You can map default column values with annotations as part of the DDL definition for a column:

tmp9B34_thumb

The columnDefinition attribute includes the complete properties for the column DDL, with datatype and all constraints. Keep in mind that an actual nonportable SQL datatype may bind your annotation mapping to a particular database management system.

Next, you’ll map user-defined value-typed classes. You can easily spot them in your UML class diagrams if you search for a composition relationship between two classes. One of them is a dependent class, a component.

Mapping components

So far, the classes of the object model have all been entity classes, each with its own lifecycle and identity. The User class, however, has a special kind of association with the Address class, as shown in figure 4.2.

In object-modeling terms, this association is a kind of aggregation—a part-of relationship. Aggregation is a strong form of association; it has some additional semantics with regard to the lifecycle of objects. In this case, you have an even stronger form, composition, where the lifecycle of the part is fully dependent upon the lifecycle of the whole.

Object modeling experts and UML designers claim that there is no difference between this composition and other weaker styles of association when it comes to the actual Java implementation. But in the context of ORM, there is a big difference: A composed class is often a candidate value type.

Relationships between User and Address using composition

Figure 4.2

Relationships between User and Address using composition

You map Address as a value type and User as an entity. Does this affect the implementation of the POJO classes?

Java has no concept of composition—a class or attribute can’t be marked as a component or composition. The only difference is the object identifier: A component has no individual identity, hence the persistent component class requires no identifier property or identifier mapping. It’s a simple POJO:

tmp9B-36_thumb

The composition between User and Address is a metadata-level notion; you only have to tell Hibernate that the Address is a value type in the mapping document or with annotations.

Component mapping in XML

Hibernate uses the term component for a user-defined class that is persisted to the same table as the owning entity, an example of which is shown in listing 4.2. (The use of the word component here has nothing to do with the architecture-level concept, as in software component.)

Listing 4.2 Mapping of the User class with a component Address

Listing 4.2 Mapping of the User class with a component Address tmp9B-38_thumb

O You declare the persistent attributes of Address inside the <component> element. The property of the User class is named homeAddress.

© You reuse the same component class to map another property of this type to the same table.

Figure 4.3 shows how the attributes of the Address class are persisted to the same table as the User entity.

Notice that, in this example, you model the composition association as unidirectional. You can’t navigate from Address to User. Hibernate supports both unidirectional and bidirectional compositions, but unidirectional composition is far more common. An example of a bidirectional mapping is shown in listing 4.3.

Table attributes of User with Address component

Figure 4.3 Table attributes of User with Address component

Listing 4.3 Adding a back-pointer to a composition

Listing 4.3 Adding a back-pointer to a composition

In listing 4.3, the <parent> element maps a property of type User to the owning entity, which in this example is the property named user. You can then call Address.getUser() to navigate in the other direction. This is really a simple back-pointer.

A Hibernate component can own other components and even associations to other entities. This flexibility is the foundation of Hibernate’s support for finegrained object models. For example, you can create a Location class with detailed information about the home address of an Address owner:

tmp9B-41_thumb

The design of the Location class is equivalent to the Address class. You now have three classes, one entity, and two value types, all mapped to the same table. Now let’s map components with JPA annotations.

Annotating embedded classes

The Java Persistence specification calls components embedded classes. To map an embedded class with annotations, you can declare a particular property in the owning entity class as @Embedded, in this case the homeAddress of User:

tmp9B42_thumb

If you don’t declare a property as @Embedded, and it isn’t of a JDK type, Hibernate looks into the associated class for the @Embeddable annotation. If it’s present, the property is automatically mapped as a dependent component.

This is what the embeddable class looks like:

tmp9B-43_thumb

You can further customize the individual property mappings in the embeddable class, such as with the @Column annotation. The USERS table now contains, among others, the columns ADDRESS_STREET, ADDRESS_ZIPCODE, and ADDRESS_CITY. Any other entity table that contains component fields (say, an Order class that also has an Address) uses the same column options. You can also add a back-pointer property to the Address embeddable class and map it with @org.hibernate.annota-tions.Parent.

Sometimes you’ll want to override the settings you made inside the embeddable class from outside for a particular entity. For example, here is how you can rename the columns:

tmp9B44_thumb

The new @Column declarations in the User class override the settings of the embeddable class. Note that all attributes on the embedded @Column annotation are replaced, so they’re no longer nullable = false.

In a JPA XML descriptor, a mapping of an embeddable class and a composition looks like the following:

tmp9B-45_thumb

There are two important limitations to classes mapped as components. First, shared references, as for all value types, aren’t possible. The component homeAd-dress doesn’t have its own database identity (primary key) and so can’t be referred to by any object other than the containing instance of User.

Second, there is no elegant way to represent a null reference to an Address. In lieu of any elegant approach, Hibernate represents a null component as null values in all mapped columns of the component. This means that if you store a component object with all null property values, Hibernate returns a null component when the owning entity object is retrieved from the database.

You’ll find many more component mappings (even collections of them) throughout the topic.

Summary

In this topic, you learned the essential distinction between entities and value types and how these concepts influence the implementation of your domain model as persistent Java classes.

Entities are the coarser-grained classes of your system. Their instances have an independent lifecycle and their own identity, and they can be referenced by many other instances. Value types, on the other hand, are dependent on a particular entity class. An instance of a value type has a lifecycle bound by its owning entity instance, and it can be referenced by only one entity—it has no individual identity.

We looked at Java identity, object equality, and database identity, and at what makes good primary keys. You learned which generators for primary key values are built into Hibernate, and how you can use and extend this identifier system.

You also learned various (mostly optional) class mapping options and, finally, how basic properties and value-type components are mapped in XML mappings and annotations.

For convenience, table 4.2 summarizes the differences between Hibernate and Java Persistence related to concepts discussed in this topic.

Table 4.2 Hibernate and JPA comparison chart

Hibernate Core

Java Persistence and EJB 3.0

Entity- and value-typed classes are the essential concepts for the support of rich and fine-grained domain models.

The JPA specification makes the same distinction, but calls value types “embeddable classes.” However, nested embeddable classes are considered a nonportable feature.

Hibernate supports 10 identifier generation strategies out-of-the-box.

JPA standardizes a subset of 4 identifier generators, but allows vendor extension.

Hibernate can access properties through fields, accessor methods, or with any custom PropertyAccessor implementation. Strategies can be mixed for a particular class.

JPA standardizes property access through fields or access methods, and strategies can’t be mixed for a particular class without Hibernate extension annotations.

Hibernate supports formula properties and database-generated values.

JPA doesn’t include these features, a Hibernate extension is needed.

In the next topic, we’ll attack inheritance and how hierarchies of entity classes can be mapped with various strategies. We’ll also talk about the Hibernate mapping type system, the converters for value types we’ve shown in a few examples

Next post:

Previous post: