Inheritance and custom types (Hibernate)

 

We deliberately didn’t talk much about inheritance mapping so far. Mapping a hierarchy of classes to tables can be a complex issue, and we’ll present various strategies in this topic. You’ll learn which strategy to choose in a particular scenario.

The Hibernate type system, with all its built-in converters and transformers for Java value-typed properties to SQL datatypes, is the second big topic we discuss in this topic.

Let’s start with the mapping of entity inheritance.

Mapping class inheritance

A simple strategy for mapping classes to database tables might be “one table for every entity persistent class.” This approach sounds simple enough and, indeed, works well until we encounter inheritance.

Inheritance is such a visible structural mismatch between the object-oriented and relational worlds because object-oriented systems model both is a and has a relationships. SQL-based models provide only has a relationships between entities; SQL database management systems don’t support type inheritance—and even when it’s available, it’s usually proprietary or incomplete.

There are four different approaches to representing an inheritance hierarchy:

■ Table per concrete class with implicit polymorphism—Use no explicit inheritance mapping, and default runtime polymorphic behavior.

■ Table per concrete class—Discard polymorphism and inheritance relationships completely from the SQL schema.

■ Table per class hierarchy—Enable polymorphism by denormalizing the SQL schema, and utilize a type discriminator column that holds type information.

■ Table per subclass—Represent is a (inheritance) relationships as has a (foreign key) relationships.

This section takes a top-down approach; it assumes that you’re starting with a domain model and trying to derive a new SQL schema. However, the mapping strategies described are just as relevant if you’re working bottom up, starting with existing database tables. We’ll show some tricks along the way that help you dealing with nonperfect table layouts.

Table per concrete class with implicit polymorphism

Suppose we stick with the simplest approach suggested. You can use exactly one table for each (nonabstract) class. All properties of a class, including inherited properties, can be mapped to columns of this table, as shown in figure 5.1.

Mapping all concrete classes to an independent table

Figure 5.1 Mapping all concrete classes to an independent table

You don’t have to do anything special in Hibernate to enable polymorphic behavior. The mapping for CreditCard and BankAccount is straightforward, each in its own entity <class> element, as we have done already for classes without a superclass (or persistent interfaces). Hibernate still knows about the superclass (or any interface) because it scans the persistent classes on startup.

The main problem with this approach is that it doesn’t support polymorphic associations very well. In the database, associations are usually represented as foreign key relationships. In figure 5.1, if the subclasses are all mapped to different tables, a polymorphic association to their superclass (abstract BillingDe-tails in this example) can’t be represented as a simple foreign key relationship. This would be problematic in our domain model, because Bill-ingDetails is associated with User; both subclass tables would need a foreign key reference to the USERS table. Or, if User had a many-to-one relationship with BillingDetails, the USERS table would need a single foreign key column, which would have to refer both concrete subclass tables. This isn’t possible with regular foreign key constraints.

Polymorphic queries (queries that return objects of all classes that match the interface of the queried class) are also problematic. A query against the superclass must be executed as several SQL SELECTs, one for each concrete subclass. For a query against the BillingDetails class Hibernate uses the following SQL:

tmp9B-47_thumb

Notice that a separate query is needed for each concrete subclass. On the other hand, queries against the concrete classes are trivial and perform well—only one of the statements is needed.

(Also note that here, and in other places in this topic, we show SQL that is conceptually identical to the SQL executed by Hibernate. The actual SQL may look superficially different.)

A further conceptual problem with this mapping strategy is that several different columns, of different tables, share exactly the same semantics. This makes schema evolution more complex. For example, a change to a superclass property results in changes to multiple columns. It also makes it much more difficult to implement database integrity constraints that apply to all subclasses.

We recommend this approach (only) for the top level of your class hierarchy, where polymorphism isn’t usually required, and when modification of the superclass in the future is unlikely.

Also, the Java Persistence interfaces don’t support full polymorphic queries; only mapped entities (@Entity) can be officially part of a Java Persistence query (note that the Hibernate query interfaces are polymorphic, even if you map with annotations).

If you’re relying on this implicit polymorphism, you map concrete classes with @Entity, as usual. However, you also have to duplicate the properties of the superclass to map them to all concrete class tables. By default, properties of the superclass are ignored and not persistent! You need to annotate the superclass to enable embedding of its properties in the concrete subclass tables:

tmp9B48_thumb

Now map the concrete subclasses:

tmp9B49_thumb

You can override column mappings from the superclass in a subclass with the @AttributeOverride annotation. You rename the OWNER column to CC_OWNER in the CREDIT_CARD table. The database identifier can also be declared in the superclass, with a shared column name and generator strategy for all subclasses. Let’s repeat the same mapping in a JPA XML descriptor:

tmp9B-50_thumb

NOTE A component is a value type; hence, the normal entity inheritance rules presented in this topic don’t apply. However, you can map a subclass as a component by including all the properties of the superclass (or interface) in your component mapping. With annotations, you use the @MappedSuperclass annotation on the superclass of the embeddable component you’re mapping just like you would for an entity. Note that this feature is available only in Hibernate Annotations and isn’t standardized or portable.

With the help of the SQL UNION operation, you can eliminate most of the issues with polymorphic queries and associations, which are present with this mapping strategy.

Table per concrete class with unions

First, let’s consider a union subclass mapping with BillingDetails as an abstract class (or interface), as in the previous section. In this situation, we again have two tables and duplicate superclass columns in both: CREDIT_CARD and BANK_ACCOUNT. What’s new is a special Hibernate mapping that includes the superclass, as you can see in listing 5.1.

Listing 5.1 Using the <union-subclass> inheritance strategy

Listing 5.1 Using the <union-subclass> inheritance strategy

O An abstract superclass or an interface has to be declared as abstract=”true”; otherwise a separate table for instances of the superclass is needed.

© The database identifier mapping is shared for all concrete classes in the hierarchy. The CREDIT_CARD and the BANK_ACCOUNT tables both have a BILLING_DETAILS_ID primary key column. The database identifier property now has to be shared for all subclasses; hence you have to move it into BillingDetails and remove it from CreditCard and BankAccount.

© Properties of the superclass (or interface) are declared here and inherited by all concrete class mappings. This avoids duplication of the same mapping.

© A concrete subclass is mapped to a table; the table inherits the superclass (or interface) identifier and other property mappings.

The first advantage you may notice with this strategy is the shared declaration of superclass (or interface) properties. No longer do you have to duplicate these mappings for all concrete classes—Hibernate takes care of this. Keep in mind that the SQL schema still isn’t aware of the inheritance; effectively, we’ve mapped two unrelated tables to a more expressive class structure. Except for the different primary key column name, the tables look exactly alike, as shown in figure 5.1.

In JPA annotations, this strategy is known as TABLE_PER_CLASS:

tmp9B-52_thumb[1]

The database identifier and its mapping have to be present in the superclass, to be shared across all subclasses and their tables. An @Entity annotation on each subclass is all that is required:

tmp9B-53_thumb

Note that TABLE_PER_CLASS is specified in the JPA standard as optional, so not all JPA implementations may support it. The actual implementation is also vendor dependent—in Hibernate, it’s equivalent to a <union-subclass> mapping in XML files.

The same mapping looks like this in a JPA XML descriptor:

tmp9B-54_thumbtmp9B55_thumb

If your superclass is concrete, then an additional table is needed to hold instances of that class. We have to emphasize again that there is still no relationship between the database tables, except for the fact that they share some similar columns. The advantages of this mapping strategy are clearer if we examine polymorphic queries. For example, a query for BillingDetails executes the following SQL statement:

tmp9B-56_thumb

This SELECT uses a FROM-clause subquery to retrieve all instances of BillingDe-tails from all concrete class tables. The tables are combined with a UNION operator, and a literal (in this case, 1 and 2) is inserted into the intermediate result; Hibernate reads this to instantiate the correct class given the data from a particular row. A union requires that the queries that are combined project over the same columns; hence, we have to pad and fill up nonexistent columns with NULL. You may ask whether this query will really perform better than two separate statements. Here we can let the database optimizer find the best execution plan to combine rows from several tables, instead of merging two result sets in memory as Hibernate’s polymorphic loader engine would do.

Another much more important advantage is the ability to handle polymorphic associations; for example, an association mapping from User to BillingDetails would now be possible. Hibernate can use a UNION query to simulate a single table as the target of the association mapping.

So far, the inheritance mapping strategies we’ve discussed don’t require extra consideration with regard to the SQL schema. No foreign keys are needed, and relations are properly normalized. This situation changes with the next strategy.

Table per class hierarchy

An entire class hierarchy can be mapped to a single table. This table includes columns for all properties of all classes in the hierarchy. The concrete subclass represented by a particular row is identified by the value of a type discriminator column. This approach is shown in figure 5.2.

This mapping strategy is a winner in terms of both performance and simplicity. It’s the best-performing way to represent polymorphism—both polymorphic and nonpolymorphic queries perform well—and it’s even easy to implement by hand. Ad-hoc reporting is possible without complex joins or unions. Schema evolution is straightforward.

Mapping a whole class hierarchy to a single table

Figure 5.2

Mapping a whole class hierarchy to a single table

There is one major problem: Columns for properties declared by subclasst must be declared to be nullable. If your subclasses each define several nonnu lable properties, the loss of NOT NULL constraints may be a serious problem fro the point of view of data integrity. Another important issue is normalizatio We’ve created functional dependencies between nonkey columns, violating th third normal form. As always, denormalization for performance can be misleai ing, because it sacrifices long-term stability, maintainability, and the integrity o data for immediate gains that may be also achieved by proper optimization of th SQL execution plans (in other words, ask your DBA).

In Hibernate, you use the <subclass> element to create a table per class hie archy mapping, as in listing 5.2.

Listing 5.2 Hibernate <subclass> mapping

Hibernate <subclass> mapping tmp9B59_thumb

O The root class BillingDetails of the inheritance hierarchy is mapped to the table BILLING_DETAILS.

© You have to add a special column to distinguish between persistent classes: the discriminator. This isn’t a property of the persistent class; it’s used internally by Hibernate. The column name is BILLING_DETAILS_TYPE, and the values are strings—in this case, “CC” or “BA”. Hibernate automatically sets and retrieves the discriminator values.

© Properties of the superclass are mapped as always, with a simple <property> element.

© Every subclass has its own <subclass> element. Properties of a subclass are mapped to columns in the BILLING_DETAILS table. Remember that NOT NULL constraints aren’t allowed, because a BankAccount instance won’t have an expMonth property, and the CC_EXP_MONTH field must be NULL for that row.

The <subclass> element can in turn contain other nested <subclass> elements, until the whole hierarchy is mapped to the table.

Hibernate generates the following SQL when querying the BillingDetails class:

tmp9B60_thumb

To query the CreditCard subclass, Hibernate adds a restriction on the discrimina tor column:

tmp9B61_thumb

This mapping strategy is also available in JPA, as SINGLE_TABLE:

tmp9B62_thumb

tmp9B63_thumb

If you don’t specify a discriminator column in the superclass, its name defaults to DTYPE and its type to string. All concrete classes in the inheritance hierarchy can have a discriminator value; in this case, BillingDetails is abstract, and Credit-Card is a concrete class:

tmp9B64_thumb

Without an explicit discriminator value, Hibernate defaults to the fully qualified class name if you use Hibernate XML files and the entity name if you use annotations or JPA XML files. Note that no default is specified in Java Persistence for non-string discriminator types; each persistence provider can have different defaults.

This is the equivalent mapping in JPA XML descriptors:

tmp9B-65_thumb

Sometimes, especially in legacy schemas, you don’t have the freedom to include an extra discriminator column in your entity tables. In this case, you can apply a formula to calculate a discriminator value for each row:

tmp9B-66_thumb

This mapping relies on an SQL CASE/WHEN expression to determine whether a particular row represents a credit card or a bank account (many developers never used this kind of SQL expression; check the ANSI standard if you aren’t familiar with it). The result of the expression is a literal, CC or BA, which in turn is declared on the <subclass> mappings. Formulas for discrimination aren’t part of the JPA specification. However, you can apply a Hibernate annotation:

tmp9B-67_thumb

The disadvantages of the table per class hierarchy strategy may be too serious for your design—after all, denormalized schemas can become a major burden in the long run. Your DBA may not like it at all. The next inheritance mapping strategy doesn’t expose you to this problem.

Table per subclass

The fourth option is to represent inheritance relationships as relational foreign key associations. Every class/subclass that declares persistent properties—including abstract classes and even interfaces—has its own table.

Unlike the table per concrete class strategy we mapped first, the table here contains columns only for each noninherited property (each property declared by the subclass itself) along with a primary key that is also a foreign key of the superclass table. This approach is shown in figure 5.3.

If an instance of the CreditCard subclass is made persistent, the values of properties declared by the BillingDetails superclass are persisted to a new row of the BILLING_DETAILS table. Only the values of properties declared by the subclass are persisted to a new row of the CREDIT_CARD table. The two rows are linked together by their shared primary key value. Later, the subclass instance may be retrieved from the database by joining the subclass table with the superclass table.

Mapping all classes of the hierarchy to their own table

Figure 5.3 Mapping all classes of the hierarchy to their own table

The primary advantage of this strategy is that the SQL schema is normalized. Schema evolution and integrity constraint definition are straightforward. A polymorphic association to a particular subclass may be represented as a foreign key referencing the table of that particular subclass.

In Hibernate, you use the <joined-subclass> element to create a table per subclass mapping. See listing 5.3.

Listing 5.3 Hibernate <joined-subclass> mapping

Listing 5.3 Hibernate <joined-subclass> mapping tmp9B-70_thumb

O The root class BillingDetails is mapped to the table BILLING_DETAILS. Note that no discriminator is required with this strategy.

© The new <joined-subclass> element maps a subclass to a new table—in this example, CREDIT_CARD. All properties declared in the joined subclass are mapped to this table.

© A primary key is required for the CREDIT_CARD table. This column also has a foreign key constraint to the primary key of the BILLING_DETAILS table. A Credit-Card object lookup requires a join of both tables. A <joined-subclass> element may contain other nested <joined-subclass> elements, until the whole hierarchy has been mapped.

Hibernate relies on an outer join when querying the BillingDetails class:

tmp9B-71_thumbtmp9B72_thumb

The SQL CASE statement detects the existence (or absence) of rows in the subclass tables CREDIT_CARD and BANK_ACCOUNT, so Hibernate can determine the concrete subclass for a particular row of the BILLING_DETAILS table.

To narrow the query to the subclass, Hibernate uses an inner join:

tmp9B73_thumb

As you can see, this mapping strategy is more difficult to implement by hand— even ad-hoc reporting is more complex. This is an important consideration if you plan to mix Hibernate code with handwritten SQL.

Furthermore, even though this mapping strategy is deceptively simple, our experience is that performance can be unacceptable for complex class hierarchies. Queries always require either a join across many tables or many sequential reads.

Let’s map the hierarchy with the same strategy and annotations, here called the JOINED strategy:

tmp9B74_thumb

In subclasses, you don’t need to specify the join column if the primary key column of the subclass table has (or is supposed to have) the same name as the primary key column of the superclass table:

tmp9B75_thumb

This entity has no identifier property; it automatically inherits the BILLING_ DETAILS_ID property and column from the superclass, and Hibernate knows how to join the tables together if you want to retrieve instances of BankAccount. Of course, you can specify the column name explicitly:

Mixing inheritance strategies

You can map whole inheritance hierarchies by nesting <union-subclass>, <subclass>, and <joined-subclass> mapping elements. You can’t mix them—for example, to switch from a table-per-class hierarchy with a discriminator to a normalized table-per-subclass strategy. Once you’ve made a decision for an inheritance strategy, you have to stick to it.

This isn’t completely true, however. With some Hibernate tricks, you can switch the mapping strategy for a particular subclass. For example, you can map a class hierarchy to a single table, but for a particular subclass, switch to a separate table with a foreign key mapping strategy, just as with table per subclass. This is possible with the <join> mapping element:

tmp9B76_thumb

Finally, this is the equivalent mapping in JPA XML descriptors:

tmp9B-77_thumb

Before we show you when to choose which strategy, let’s consider mixing inheritance mapping strategies in a single class hierarchy.

tmp9B78_thumbtmp9B-79_thumb

The <join> element groups some properties and tells Hibernate to get them from a secondary table. This mapping element has many uses, and you’ll see it again later in the topic. In this example, it separates the CreditCard properties from the table per hierarchy into the CREDIT_CARD table. The CREDIT_CARD_ID column of this table is at the same time the primary key, and it has a foreign key constraint referencing the BILLING_DETAILS_ID of the hierarchy table. The BankAccount subclass is mapped to the hierarchy table. Look at the schema in figure 5.4.

At runtime, Hibernate executes an outer join to fetch BillingDetails and all subclass instances polymorphically:

tmp9B80_thumbBreaking out a subclass to its own secondary table

Figure 5.4 Breaking out a subclass to its own secondary table

You can also use the <join> trick for other subclasses in your class hierarchy. However, if you have an exceptionally wide class hierarchy, the outer join can become a problem. Some database systems (Oracle, for example) limit the number of tables in an outer join operation. For a wide hierarchy, you may want to switch to a different fetching strategy that executes an immediate second select instead of an outer join:

tmp9B82_thumb

Java Persistence also supports this mixed inheritance mapping strategy with annotations. Map the superclass BillingDetails with InheritanceType.SINGLE_ TABLE, as you did before. Now map the subclass you want to break out of the single table to a secondary table.

tmp9B83_thumbtmp9B-84_thumb

If you don’t specify a primary key join column for the secondary table, the name of the primary key of the single inheritance table is used—in this case, BILLING_DETAILS_ID. Also note that you need to map all properties that are moved into the secondary table with the name of that secondary table.

You also want more tips about how to choose an appropriate combination of mapping strategies for your application’s class hierarchies.

Choosing a strategy

You can apply all mapping strategies to abstract classes and interfaces. Interfaces may have no state but may contain accessor method declarations, so they can be treated like abstract classes. You can map an interface with <class>, <union-subclass>, <subclass>, or <joined-subclass>, and you can map any declared or inherited property with <property>. Hibernate won’t try to instantiate an abstract class, even if you query or load it.

NOTE Note that the JPA specification doesn’t support any mapping annotation on an interface! This will be resolved in a future version of the specification; when you read this topic, it will probably be possible with Hibernate Annotations.

Here are some rules of thumb:

■ If you don’t require polymorphic associations or queries, lean toward table-per-concrete-class—in other words, if you never or rarely query for Bill-ingDetails and you have no class that has an association to BillingDetails (our model has). An explicit UNION-based mapping should be preferred, because (optimized) polymorphic queries and associations will then be possible later. Implicit polymorphism is mostly useful for queries utilizing non-persistence-related interfaces.

■ If you do require polymorphic associations (an association to a superclass, hence to all classes in the hierarchy with dynamic resolution of the concrete class at runtime) or queries, and subclasses declare relatively few properties (particularly if the main difference between subclasses is in their behavior), lean toward table-per-class-hierarchy. Your goal is to minimize the number of nullable columns and to convince yourself (and your DBA) that a denor-malized schema won’t create problems in the long run.

■ If you do require polymorphic associations or queries, and subclasses declare many properties (subclasses differ mainly by the data they hold), lean toward table-per-subclass. Or, depending on the width and depth of your inheritance hierarchy and the possible cost of joins versus unions, use table-per-concrete-class.

By default, choose table-per-class-hierarchy only for simple problems. For more complex cases (or when you’re overruled by a data modeler insisting on the importance of nullability constraints and normalization), you should consider the table-per-subclass strategy. But at that point, ask yourself whether it may not be better to remodel inheritance as delegation in the object model. Complex inheritance is often best avoided for all sorts of reasons unrelated to persistence or ORM. Hibernate acts as a buffer between the domain and relational models, but that doesn’t mean you can ignore persistence concerns when designing your classes.

When you start thinking about mixing inheritance strategies, remember that implicit polymorphism in Hibernate is smart enough to handle more exotic cases. For example, consider an additional interface in our application, Electronic-PaymentOption. This is a business interface that doesn’t have a persistence aspect—except that in our application, a persistent class such as CreditCard will likely implement this interface. No matter how you map the BillingDetails hierarchy, Hibernate can answer a query from ElectronicPaymentOption correctly. This even works if other classes, which aren’t part of the BillingDetails hierarchy, are mapped persistent and implement this interface. Hibernate always know what tables to query, which instances to construct, and how to return a polymorphic result.

Finally, you can also use <union-subclass>, <subclass>, and <joined-sub-class> mapping elements in a separate mapping file (as a top-level element instead of <class>). You then have to declare the class that is extended, such as <subclass name=”CreditCard” extends=”BillingDetails”>, and the superclass mapping must be loaded programmatically before the subclass mapping file (you don’t have to worry about this order when you list mapping resources in the XML configuration file). This technique allows you to extend a class hierarchy without modifying the mapping file of the superclass.

You now know everything you need to know about the mapping of entities, properties, and inheritance hierarchies. You can already map complex domain models. In the second half of this topic, we discuss another important feature that you should know by heart as a Hibernate user: the Hibernate mapping type system.

The Hibernate type system

We first distinguished between entity and value types—a central concept of ORM in Java. We must elaborate on that distinction in order for you to fully understand the Hibernate type system of entities, value types, and mapping types.

Recapitulating entity and value types

Entities are the coarse-grained classes in your system. You usually define the features of a system in terms of the entities involved. The user places a bid for an item is a typical feature definition; it mentions three entities. Classes of value types often don’t even appear in the business requirements—they’re usually the fine-grained classes representing strings, numbers, and monetary amounts. Occasionally, value types do appear in feature definitions: the user changes billing address is one example, assuming that Address is a value type.

More formally, an entity is any class whose instances have their own persistent identity. A value type is a class that doesn’t define some kind of persistent identity. In practice, this means that entity types are classes with identifier properties, and value type classes depend on an entity.

At runtime, you have a network of entity instances interleaved with value type instances. The entity instances may be in any of the three persistent lifecycle states: transient, detached, or persistent. We don’t consider these lifecycle states to apply to the value type instances.

Therefore, entities have their own lifecycle. The save() and delete() methods of the Hibernate Session interface apply to instances of entity classes, never to value type instances. The persistence lifecycle of a value type instance is completely tied to the lifecycle of the owning entity instance. For example, the username becomes persistent when the user is saved; it never becomes persistent independently of the user.

In Hibernate, a value type may define associations; it’s possible to navigate from a value type instance to some other entity. However, it’s never possible to navigate from the other entity back to the value type instance. Associations always point to entities. This means that a value type instance is owned by exactly one entity when it’s retrieved from the database; it’s never shared.

At the level of the database, any table is considered an entity. However, Hibernate provides certain constructs to hide the existence of a database-level entity from the Java code. For example, a many-to-many association mapping hides the intermediate association table from the application. A collection of strings (more accurately, a collection of value-typed instances) behaves like a value type from the point of view of the application; however, it’s mapped to its own table. Although these features seem nice at first (they simplify the Java code), we have over time become suspicious of them. Inevitably, these hidden entities end up needing to be exposed to the application as business requirements evolve. The many-to-many association table, for example, often has additional columns added as the application matures. We’re almost prepared to recommend that every database-level entity be exposed to the application as an entity class. For example, we would be inclined to model the many-to-many association as two one-to-many associations to an intervening entity class. We’ll leave the final decision to you, however, and come back to the topic of many-to-many entity associations in the future topics.

Entity classes are always mapped to the database using <class>, <union-subclass>, <subclass>, and <joined-subclass> mapping elements. How are value types mapped?

You’ve already met two different kinds of value type mappings: <property> and <component>. The value type of a component is obvious: It’s the class that is mapped as embeddable. However, the type of a property is a more generic notion. Consider this mapping of the CaveatEmptor User and email address:

tmp9B85_thumb

Let’s focus on that type=”string” attribute. You know that in ORM you have to deal with Java types and SQL datatypes. The two different type systems must be bridged. This is the job of the Hibernate mapping types, and string is the name of a built-in Hibernate mapping type.

The string mapping type isn’t the only one built into Hibernate. Hibernate comes with various mapping types that define default persistence strategies for primitive Java types and certain JDK classes.

Built-in mapping types

Hibernate’s built-in mapping types usually share the name of the Java type they map. However, there may be more than one Hibernate mapping type for a particular Java type.

The built-in types may not be used to perform arbitrary conversions, such as mapping a VARCHAR database value to a Java Integer property value. You may define your own custom value types for this kind of conversation, as shown later in this topic.

We now discuss the basic, date and time, locator object, and various other built-in mapping types and show you what Java and SQL datatype they handle.

Java primitive mapping types

The basic mapping types in table 5.1 map Java primitive types (or their wrapper types) to appropriate built-in SQL standard types.

Table 5.1 Primitive types

Primitive types

You’ve probably noticed that your database doesn’t support some of the SQL types mentioned in table 5.1. The listed type names are names of ANSI-standard datatypes. Most database vendors ignore this part of the SQL standard (because their legacy type systems often predate the standard). However, the JDBC driver provides a partial abstraction of vendor-specific SQL datatypes, allowing Hibernate to work with ANSI-standard types when executing DML. For database-specific DDL generation, Hibernate translates from the ANSI-standard type to an appropriate vendor-specific type, using the built-in support for specific SQL dialects. (This means you usually don’t have to worry about SQL datatypes if you’re using Hibernate for data access and SQL schema definition.)

Furthermore, the Hibernate type system is smart and can switch SQL datatypes depending on the defined length of a value. The most obvious case is string: If you declare a string property mapping with a length attribute, Hibernate picks the correct SQL datatype depending on the selected dialect. For MySQL, for example, a length of up to 65535 results in a regular VARCHAR(length) column when Hibernate exports the schema. For a length of up to 16777215, a MEDIUM-TEXT datatype is used. Larger string mappings result in a LONGTEXT. Check your SQL dialect (the source code comes with Hibernate) if you want to know the ranges for this and other mapping types. You can customize this behavior by subclassing your dialect and overriding these settings.

Most dialects also support setting the scale and precision of decimal SQL datatypes. For example, a precision or scale setting in your mapping of a Big-Decimal creates a NUMERIC(precision, scale) datatype for MySQL.

Finally, the yes_no and true_false mapping types are converters that are mostly useful for legacy schemas and Oracle users; Oracle DBMS products don’t have a built-in boolean or truth-valued type (the only built-in datatype actually required by the relational data model).

Date and time mapping types

Table 5.2 lists Hibernate types associated with dates, times, and timestamps. In your domain model, you may choose to represent date and time data using java.util.Date, java.util.Calendar, or the subclasses of java.util.Date defined in the java.sql package. This is a matter of taste, and we leave the decision to you—make sure you’re consistent, however. (In practice, binding your domain model to types from the JDBC package isn’t the best idea.)

A caveat: If you map a java.util.Date property with timestamp (the most common case), Hibernate returns a java.sql.Timestamp when loading the property from the database. Hibernate has to use the JDBC subclass because it includes nanosecond information that may be present in the database. Hibernate can’t just cut off this information. This can lead to problems if you try to compare your java.util.Date properties with the equals() method, because it isn’t symmetric with the java.sql.Timestamp subclass equals() method. First, the right way (in any case) to compare two java.util.Date objects, which also works for any subclass, is aDate . getTime () > bDate . getTime () (for a greater-than comparison). Second, you can write a custom mapping type that cuts off the database nanosecond information and returns a java.util.Date in all cases. Currently (although this may change in the future), no such mapping type is built into Hibernate.

Table 5.2 Date and time types

Date and time types  

Binary and large value mapping types

Table 5.3 lists Hibernate types for handling binary data and large values. Note that only binary is supported as the type of an identifier property.

If a property in your persistent Java class is of type byte[], Hibernate can map it to a VARBINARY column with the binary mapping type. (Note that the real SQL type depends on the dialect; for example, in PostgreSQL, the SQL type is BYTEA, and in Oracle it’s RAW.) If a property in your persistent Java class is of type java.lang.String, Hibernate can map it to an SQL CLOB column, with the text mapping type.

Table 5.3 Binary and large value types

Binary and large value types  

Note that in both cases, Hibernate initializes the property value right away, when the entity instance that holds the property variable is loaded. This is inconvenient when you have to deal with potentially large values.

One solution is lazy loading through interception of field access, on demand. However, this approach requires bytecode instrumentation of your persistent classes for the injection of extra code.

A second solution is a different kind of property in your Java class. JDBC supports locator objects (LOBs) directly.1 If your Java property is of type java.sql.Clob or java.sql.Blob, you can map it with the clob or blob mapping type to get lazy loading of large values without bytecode instrumentation. When the owner of the property is loaded, the property value is a locator object—effectively, a pointer to the real value that isn’t yet materialized. Once you access the property, the value is materialized. This on-demand loading works only as long as the database transaction is open, so you need to access any property of such a type when the owning entity instance is in a persistent and transactional state, not in detached state. Your domain model is now also bound to JDBC, because the import of the java.sql package is required. Although domain model classes are executable in isolated unit tests, you can’t access LOB properties without a database connection.

Mapping properties with potentially large values is slightly different if you rely on Java Persistence annotations. By default, a property of type java.lang.String is mapped to an SQL VARCHAR column (or equivalent, depending on the SQL dialect). If you want to map a java.lang.String, char[], Character[], or even a java.sql.Clob typed property to a CLOB column, you need to map it with the @Lob annotation:

tmp9B89_thumbtmp9B90_thumb

The same is true for any property that is of type byte[], Byte[], or java. sql.Blob. Note that for all cases, except properties that are of java.sql.Clob or java.sql.Blob type, the values are again loaded immediately by Hibernate, and not lazily on demand. Instrumenting bytecode with interception code is again an option to enable lazy loading of individual properties transparently.

To create and set a java.sql.Blob or java.sql.Clob value, if you have these property types in your domain model, use the static Hibernate.createBlob() and Hibernate.createClob() methods and provide a byte array, an input stream, or a string.

Finally, note that both Hibernate and JPA provide a serialization fallback for any property type that is Serializable. This mapping type converts the value of a property to a byte stream that is then stored in a VARBINARY (or equivalent) column. When the owner of the property is loaded, the property value is deserialized. Naturally, you should use this strategy with extreme caution (data lives longer than an application), and it may be useful only for temporary data (user preferences, login session data, and so on).

JDK mapping types

Table 5.4 lists Hibernate types for various other Java types of the JDK that may be represented as a VARCHAR in the database.

You may have noticed that <property> isn’t the only Hibernate mapping element that has a type attribute.

Table 5.4 Other JDK-related types

Other JDK-related types

Using mapping types

All of the basic mapping types may appear almost anywhere in the Hibernate mapping document, on normal property, identifier property, and other mapping elements. The <id>, <property>, <version>, <discriminator>, <index> and <element> elements all define an attribute named type.

You can see how useful the built-in mapping types are in this mapping for the

tmp9B-92_thumb

The BillingDetails class is mapped as an entity. Its discriminator, identifier, and name properties are value typed, and we use the built-in Hibernate mapping types to specify the conversion strategy.

It isn’t often necessary to explicitly specify a built-in mapping type in the XML mapping document. For instance, if you have a property of Java type java.lang.String, Hibernate discovers this using reflection and selects string by default. We can easily simplify the previous mapping example:

tmp9B-93_thumb

Hibernate also understands type=”java.lang.String”; it doesn’t have to use reflection then. The most important case where this approach doesn’t work well is a java.util.Date property. By default, Hibernate interprets a java.util.Date as a timestamp mapping. You need to explicitly specify type=”time” or type=”date” if you don’t wish to persist both date and time information.

With JPA annotations, the mapping type of a property is automatically detected, just like in Hibernate. For a java.util.Date or java.util.Calendar property, the Java Persistence standard requires that you select the precision with a @Temporal annotation:

tmp9B94_thumb

On the other hand, Hibernate Annotations, relaxing the rules of the standard, defaults to TemporalType.TIMESTAMP—options are TemporalType.TIME and Tem-poralType.DATE.

In other rare cases, you may want to add the @org.hibernate.annota-tions.Type annotation to a property and declare the name of a built-in or custom Hibernate mapping type explicitly. This is a much more common extension as soon as you start writing your own custom mapping types, which you’ll do later in this topic.

The equivalent JPA XML descriptor is as follows:

tmp9B-95_thumb

For each of the built-in mapping types, a constant is defined by the class org.hibernate.Hibernate. For example, Hibernate.STRING represents the string mapping type.

tmp9B-96_thumb

Note that you may as well use the setString() argument binding method in this case. Type constants are also useful for programmatic manipulation of the Hibernate mapping metamodel.

Hibernate isn’t limited to the built-in mapping types. We consider the extensible mapping-type system one of the core features and an important aspect that makes Hibernate so flexible.

Creating custom mapping types

Object-oriented languages like Java make it easy to define new types by writing new classes. This is a fundamental part of the definition of object-orientation. If we were then limited to the predefined built-in Hibernate mapping types when declaring properties of our persistent classes, we would lose much of Java’s expressiveness. Furthermore, our domain model implementation would be tightly coupled to the physical data model, because new type conversions would be impossible.

Most ORM solutions that we have seen provide support for user-defined strategies for performing type conversions. These are often called converters. For example, the user can create a new strategy for persisting a property of JDK type Integer to a VARCHAR column. Hibernate provides a similar, much more powerful, feature called custom mapping types.

First you need to understand when it’s appropriate to write your own custom mapping type, and which Hibernate extension point is relevant for you. We’ll then write some custom mapping types and explore the options.

Considering custom mapping types

As an example, take the mapping of the Address class from previous topics, as a component:

tmp9B-97_thumb

This value type mapping is straightforward; all properties of the new user-defined Java type are mapped to individual columns of a built-in SQL datatype. However, you can alternatively map it as a simple property, with a custom mapping type:

tmp9B98_thumb

This is also probably the first time you’ve seen a single <property> element with several <column> elements nested inside. We’re moving the responsibility for translating and converting between an Address value type (it isn’t even named anywhere) and the named three columns to a separate class: auction.persis-tence.CustomAddressType. This class is now responsible for loading and saving this property. Note that no Java code changes in the domain model implementation—the homeAddress property is of type Address.

Granted, the benefit of replacing a component mapping with a custom mapping type is dubious in this case. As long as you require no special conversion when loading and saving this object, the CustomAddressType you now have to write is just additional work. However, you can already see that custom mapping types provide an additional buffer—something that may come in handy in the long run when extra conversion is required. Of course, there are better use cases for custom mapping types, as you’ll soon see. (Many examples of useful Hibernate mapping types can be found on the Hibernate community website.)

Let’s look at the Hibernate extension points for the creation of custom mapping types.

The extension points

Hibernate provides several interfaces that applications may use when defining custom mapping types. These interfaces reduce the work involved in creating new mapping types and insulate the custom type from changes to the Hibernate core. This allows you to easily upgrade Hibernate and keep your existing custom mapping types.

The extension points are as follows:

■ org.hibernate.usertype.UserType —The basic extension point, which is useful in many situations. It provides the basic methods for custom loading and storing of value type instances.

■ org.hibernate.usertype.CompositeUserType —An interface with more methods than the basic UserType, used to expose internals about your value type class to Hibernate, such as the individual properties. You can then refer to these properties in Hibernate queries.

■ org.hibernate.usertype.UserCollectionType —A rarely needed interface that’s used to implement custom collections. A custom mapping type implementing this interface isn’t declared on a property mapping but is useful only for custom collection mappings. You have to implement this type if you want to persist a non-JDK collection and preserve additional semantics persistently. We discuss collection mappings and this extension point in the next topic.

■ org.hibernate.usertype.EnhancedUserType —An interface that extends UserType and provides additional methods for marshalling value types to and from XML representations, or enables a custom mapping type for use in identifier and discriminator mappings.

■ org.hibernate.usertype.UserVersionType —An interface that extends UserType and provides additional methods enabling the custom mapping type for usage in entity version mappings.

■ org.hibernate.usertype.ParameterizedType —A useful interface that can be combined with all others to provide configuration settings—that is, parameters defined in metadata. For example, you can write a single Money-Converter that knows how to translate values into Euro or US dollars, depending on a parameter in the mapping.

We’ll now create some custom mapping types. You shouldn’t consider this an unnecessary exercise, even if you’re happy with the built-in Hibernate mapping types. In our experience, every sophisticated application has many good use cases for custom mapping types.

The case for custom mapping types

The Bid class defines an amount property, and the Item class defines an ini-tialPrice property; both are monetary values. So far, we’ve used only a simple BigDecimal to represent the value, mapped with big_decimal to a single NUMERIC column.

Suppose you want to support multiple currencies in the auction application and that you have to refactor the existing domain model for this (customer-driven) change. One way to implement this change would be to add new properties to Bid and Item: amountCurrency and initialPriceCurrency. You could then map these new properties to additional VARCHAR columns with the built-in currency mapping type. We hope you never use this approach!

Instead, you should create a new MonetaryAmount class that encapsulates both currency and amount. Note that this is a class of your domain model; it doesn’t have any dependency on Hibernate interfaces:

tmp9B-99_thumbtmp9B100_thumb

We have made MonetaryAmount an immutable class. This is a good practice in Java because it simplifies coding. Note that you have to implement equals() and hashCode() to finish the class (there is nothing special to consider here). You use this new MonetaryAmount to replace the BigDecimal of the initialPrice property in Item. You can and should use it for all other BigDecimal prices in any persistent classes, such as the Bid.amount, and in business logic—for example, in the billing system.

Let’s map the refactored initialPrice property of Item, with its new MonetaryAmount type to the database.

Creating a UserType

Imagine that you’re working with a legacy database that represents all monetary amounts in USD. The application is no longer restricted to a single currency (that was the point of the refactoring), but it takes some time for the database team to make the changes. You need to convert the amount to USD when persisting MonetaryAmount objects. When you load from the database, you convert it back to the currency the user selected in his or her preferences.

Create a new MonetaryAmountUserType class that implements the Hibernate interface UserType. This is your custom mapping type, shown in listing 5.4.

Listing 5.4 Custom mapping type for monetary amounts in USD

Listing 5.4 Custom mapping type for monetary amounts in USD tmp9B-102_thumb[1]

O The sqlTypes() method tells Hibernate what SQL column types to use for DDL schema generation. Notice that this method returns an array of type codes. A UserType may map a single property to multiple columns, but this legacy data model has only a single numeric column. By using the Hiber-nate.BIG_DECIMAL.sqlType() method, you let Hibernate decide the exact SQL datatype for the given database dialect. Alternatively, return a constant from java.sql.Types.

© The returnedClass() method tells Hibernate what Java value type class is mapped by this UserType.

© Hibernate can make some minor performance optimizations for immutable types like this one, for example, when comparing snapshots during dirty checking. The isMutable() method tells Hibernate that this type is immutable.

E The UserType is also partially responsible for creating a snapshot of a value in the first place. Because MonetaryAmount is an immutable class, the deepCopy() method returns its argument. In the case of a mutable type, it would need to return a copy of the argument to be used as the snapshot value.

© The disassemble() method is called when Hibernate puts a MonetaryAmount into the second-level cache. As you’ll learn later, this is a cache of data that stores information in a serialized form.

© The assemble() method does the opposite of disassembly: It can transform cached data into an instance of MonetaryAmount. As you can see, implementation of both routines is easy for immutable types.

O Implement replace() to handle merging of detached object state. As you’ll see later in the topic, the process of merging involves an original and a target object, whose state must be combined. Again, for immutable value types, return the first argument. For mutable types, at least return a deep copy of the first argument. For mutable types that have component fields, you probably want to apply a recursive merging routine.

© The UserType is responsible for dirty checking property values. The equals() method compares the current property value to a previous snapshot and determines whether the property is dirty and must by saved to the database. The hash-Code() of two equal value typed instances has to be the same. We usually delegate this method to the actual value type class—in this case, the hashCode() method of the given MonetaryAmount object.

J The nullSafeGet() method retrieves the property value from the JDBC Result-Set. You can also access the owner of the component if you need it for the conversion. All database values are in USD, so you convert it to the currency the user has currently set in his preferences. (Note that it’s up to you to implement this conversion and preference handling.)

1) The nullSafeSet() method writes the property value to the JDBC Prepared-Statement. This method takes whatever currency is set and converts it to a simple BigDecimal USD amount before saving.

You now map the initialPrice property of Item as follows:

tmp9B103_thumb

Note that you place the custom user type into the persistence package; it’s part of the persistence layer of the application, not the domain model or business layer. To use a custom type in annotations, you have to add a Hibernate extension:

tmp9B104_thumb

This is the simplest kind of transformation that a UserType can perform. Much more sophisticated things are possible. A custom mapping type can perform validation; it can read and write data to and from an LDAP directory; it can even retrieve persistent objects from a different database. You’re limited mainly by your imagination.

In reality, we’d prefer to represent both the amount and currency of monetary amounts in the database, especially if the schema isn’t legacy but can be defined (or updated quickly). Let’s assume you now have two columns available and can store the MonetaryAmount without much conversion. A first option may again be a simple <component> mapping. However, let’s try to solve it with a custom mapping type.

(Instead of writing a new custom type, try to adapt the previous example for two columns. You can do this without changing the Java domain model classes— only the converter needs to be updated for this new requirement and the additional column named in the mapping.)

The disadvantage of a simple UserType implementation is that Hibernate doesn’t know anything about the individual properties inside a MonetaryAmount. All it knows is the custom type class and the column names. The Hibernate query engine (discussed in more detail later) doesn’t know how to query for amount or a particular currency.

You write a CompositeUserType if you need the full power of Hibernate queries. This (slightly more complex) interface exposes the properties of the MonetaryAmount to Hibernate queries. We’ll now map it again with this more flexible customization interface to two columns, effectively producing an equivalent to a component mapping.

Creating a CompositeUserType

To demonstrate the flexibility of custom mappings types, you don’t change the MonetaryAmount class (and other persistent classes) at all—you change only the custom mapping type, as shown in listing 5.5.

Listing 5.5 Custom mapping type for monetary amounts in new database schemas

Listing 5.5 Custom mapping type for monetary amounts in new database schemas tmp9B-106_thumb

O The CompositeUserType interface requires the same housekeeping methods as the UserType you created earlier. However, the sqlTypes() method is no longer needed.

© Loading a value now is straightforward: You transform two column values in the result set to two property values in a new MonetaryAmount instance.

© Saving a value involves setting two parameters on the prepared statement.

© A CompositeUserType exposes the properties of the value type through getProp-ertyNames().

© The properties each have their own type, as defined by getPropertyTypes(). The types of the SQL columns are now implicit from this method.

© The getPropertyValue() method returns the value of an individual property of the MonetaryAmount.

O The setPropertyValue() method sets the value of an individual property of the MonetaryAmount.

The initialPrice property now maps to two columns, so you need to declare both in the mapping file. The first column stores the value; the second stores the currency of the MonetaryAmount:

tmp9B107_thumb

If Item is mapped with annotations, you have to declare several columns for this property. You can’t use the javax.persistence.Column annotation several times, so a new, Hibernate-specific annotation is needed:

tmp9B108_thumb

In a Hibernate query, you can now refer to the amount and currency properties of the custom type, even though they don’t appear anywhere in the mapping document as individual properties:

tmp9B109_thumb

You have extended the buffer between the Java object model and the SQL database schema with the new custom composite type. Both representations are now more robust to changes. Note that the number of columns isn’t relevant for your choice of UserType versus CompositeUserType—only your desire to expose value type properties for Hibernate queries.

Parameterization is a helpful feature for all custom mapping types.

Parameterizing custom types

Let’s assume that you face the initial problem again: conversion of money to a different currency when storing it to the database. Often, problems are more subtle than a generic conversion; for example, you may store US dollars in some tables and Euros in others. You still want to write a single custom mapping type for this, which can do arbitrary conversions. This is possible if you add the Parameter-izedType interface to your UserType or CompositeUserType classes:

tmp9B-110_thumb[1]

We left out the usual mandatory housekeeping methods in this example. The important additional method is setParameterValues() of the Parameterized-Type interface. Hibernate calls this method on startup to initialize this class with a convertTo parameter. The nullSafeSet() methods uses this setting to convert to the target currency when saving a MonetaryAmount. The nullSafeGet() method takes the currency that is present in the database and leaves it to the client to deal with the currency of a loaded MonetaryAmount (this asymmetric implementation isn’t the best idea, naturally).

You now have to set the configuration parameters in your mapping file when you apply the custom mapping type. A simple solution is the nested <type> mapping on a property:

tmp9B-111_thumb

However, this is inconvenient and requires duplication if you have many monetary amounts in your domain model. A better strategy uses a separate definition of the type, including all parameters, under a unique name that you can then reuse across all your mappings. You do this with a separate <typedef>, an element (you can also use it without parameters):

tmp9B-112_thumb

What we show here is a binding of a custom mapping type with some arguments to the names monetary_amount_usd and monetary_amount_eur. This definition can be placed anywhere in your mapping files; it’s a child element of <hibernate-mapping> (as mentioned earlier in the topic, larger applications have often one or several MyCustomTypes.hbm.xml files with no class mappings). With Hibernate extensions, you can define named custom types with parameters in annotations:

tmp9B-113_thumb

This annotation metadata is global as well, so it can be placed outside any Java class declaration (right after the import statements) or in a separate file, pack-age-info.java. A good location in this system is in a package-info.java file in the persistence package.

In XML mapping files and annotation mappings, you now refer to the defined type name instead of the fully qualified class name of your custom type:

tmp9B114_thumb

Let’s look at a different, extremely important, application of custom mapping types. The type-safe enumeration design pattern can be found in almost all applications.

Mapping enumerations

An enumeration type is a common Java idiom where a class has a constant (small) number of immutable instances. In CaveatEmptor, this can be applied to credit cards: for example, to express the possible types a user can enter and the application offers (Mastercard, Visa, and so on). Or, you can enumerate the possible ratings a user can submit in a Comment, about a particular auction.

In older JDKs, you had to implement such classes (let’s call them CreditCard-Type and Rating) yourself, following the type-safe enumeration pattern. This is still the right way to do it if you don’t have JDK 5.0; the pattern and compatible custom mapping types can be found on the Hibernate community website.

Using enumerations in JDK 5.0

If you use JDK 5.0, you can use the built-in language support for type-safe enumerations. For example, a Rating class looks as follows:

tmp9B115_thumb

The Comment class has a property of this type:

tmp9B116_thumb

This is how you use the enumeration in the application code:

tmp9B117_thumb

You now have to persist this Comment instance and its Rating. One approach is to use the actual name of the enumeration and save it to a VARCHAR column in the COMMENTS table. This RATING column will then contain EXCELLENT, OK, or BAD, depending on the Rating given.

Let’s write a Hibernate UserType that can load and store VARCHAR-backed enumerations, such as the Rating.

Writing a custom enumeration handler

Instead of the most basic UserType interface, we now want to show you the EnhancedUserType interface. This interface allows you to work with the Comment entity in XML representation mode, not only as a POJO. Furthermore, the implementation you’ll write can support any VARCHAR-backed enumeration, not only Rating, thanks to the additional Parameterized-Type interface.

Look at the code in listing 5.6.

Listing 5.6 Custom mapping type for string-backed enumerations

tmp9B-118_thumbtmp9B119_thumb

O The configuration parameter for this custom mapping type is the name of the enumeration class it’s used for, such as Rating. © It’s also the class that is returned from this method.

© A single VARCHAR column is needed in the database table. You keep it portable by letting Hibernate decide the SQL datatype.

E These are the usual housekeeping methods for an immutable type. © The following three methods are part of the EnhancedUserType and are used for XML marshalling.

© When you’re loading an enumeration, you get its name from the database and create an instance.

O When you’re saving an enumeration, you store its name.

Next, you’ll map the rating property with this new custom type.

Mapping enumerations with XML and annotations

In the XML mapping, first create a custom type definition:

tmp9B120_thumb

You can now use the type named rating in the Comment class mapping:

tmp9B121_thumb

Because ratings are immutable, you map it as update=”false” and enable direct field access (no setter method for immutable properties). If other classes besides Comment have a Rating property, use the defined custom mapping type again.

The definition and declaration of this custom mapping type in annotations looks the same as the one you did in the previous section.

On the other hand, you can rely on the Java Persistence provider to persist enumerations. If you have a property in one of your annotated entity classes of type java.lang.Enum (such as the rating in your Comment), and it isn’t marked as @Transient or transient (the Java keyword), the Hibernate JPA implementation must persist this property out of the box without complaining; it has a built-in type that handles this. This built-in mapping type has to default to a representation of an enumeration in the database. The two common choices are string representation, as you implemented for native Hibernate with a custom type, or ordinal representation. An ordinal representation saves the position of the selected enumeration option: for example, 1 for EXCELLENT, 2 for OK, and 3 for BAD. The database column also defaults to a numeric column. You can change this default enumeration mapping with the Enumerated annotation on your property:

tmp9B122_thumb

You’ve now switched to a string-based representation, effectively the same representation your custom type can read and write. You can also use a JPA XML descriptor:

tmp9B123_thumb

You may (rightfully) ask why you have to write your own custom mapping type for enumerations when obviously Hibernate, as a Java Persistence provider, can persist and load enumerations out of the box. The secret is that Hibernate Annotations includes several custom mapping types that implement the behavior defined by Java Persistence. You could use these custom types in XML mappings; however, they aren’t user friendly (they need many parameters) and weren’t written for that purpose. You can check the source (such as org.hibernate.type.EnumType in Hibernate Annotations) to learn their parameters and decide if you want to use them directly in XML.

Querying with custom mapping types

One further problem you may run into is using enumerated types in Hibernate queries. For example, consider the following query in HQL that retrieves all comments that are rated “bad”:

tmp9B124_thumb

Although this query works if you persist your enumeration as a string (the query parser uses the enumeration value as a constant), it doesn’t work if you selected ordinal representation. You have to use a bind parameter and set the rating value for the comparison programmatically:

tmp9B125_thumb

The last line in this example uses the static helper method Hibernate.custom() to convert the custom mapping type to a Hibernate Type; this is a simple way to tell Hibernate about your enumeration mapping and how to deal with the Rating.BAD value. Note that you also have to tell Hibernate about any initialization properties the parameterized type may need.

Unfortunately, there is no API in Java Persistence for arbitrary and custom query parameters, so you have to fall back to the Hibernate Session API and create a Hibernate Query object.

We recommend that you become intimately familiar with the Hibernate type system and that you consider the creation of custom mapping types an essential skill—it will be useful in every application you develop with Hibernate or JPA.

Summary

In this topic, you learned how inheritance hierarchies of entities can be mapped to the database with the four basic inheritance mapping strategies: table per concrete class with implicit polymorphism, table per concrete class with unions, table per class hierarchy, and the normalized table per subclass strategy. You’ve seen how these strategies can be mixed for a particular hierarchy and when each strategy is most appropriate.

We also elaborated on the Hibernate entity and value type distinction, and how the Hibernate mapping type system works. You used various built-in types and wrote your own custom types by utilizing the Hibernate extension points such as UserType and ParameterizedType.

Table 5.5 shows a summary you can use to compare native Hibernate features and Java Persistence.

Table 5.5 Hibernate and JPA comparison chart


Hibernate Core

Java Persistence and EJB 3.0

Supports four inheritance mapping strategies. Mixing of inheritance strategies is possible.

Four inheritance mapping strategies are standardized; mixing strategies in one hierarchy isn’t considered portable. Only table per class hierarchy and table per subclass are required for JPA-compliant providers.

A persistent supertype can be an abstract class or an interface (with property accessor methods only).

A persistent supertype can be an abstract class; mapped interfaces aren’t considered portable.

Provides flexible built-in mapping types and converters for value typed properties.

There is automatic detection of mapping types, with standardized override for temporal and enum mapping types. Hibernate extension annotation is used for any custom mapping type declaration.

Powerful extendable type system.

The standard requires built-in types for enumerations, LOBs, and many other value types for which you’d have to write or apply a custom mapping type in native Hibernate.

The next topic introduces collection mappings and discusses how you can handle collections of value typed objects (for example, a collection of Strings) and collections that contain references to entity instances.

Next post:

Previous post: