Jonathan Wetherbee

Subscribe to Jonathan Wetherbee: eMailAlertsEmail Alerts
Get Jonathan Wetherbee: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


Related Topics: Java Developer Magazine, Open Web Magazine

Java Developer : Article

Java Feature — Inheritance Hierarchies in JPA

Understanding and comparing inheritance hierarchies in Java Persistence API

The persistence model introduced in EJB 3.0 as a replacement for entity beans is known as the Java Persistence API (JPA). The JPA borrows from both proprietary and open source models, such as Oracle TopLink, Hibernate, Spring, and other frameworks, which have gained traction as popular alternatives to the often heavyweight and cumbersome persistence directives required by earlier EJB revisions. Among the new features introduced in EJB 3.0 through the JPA is support for entity inheritance. In this article, we will examine inheritance strategies supported by the JPA and apply these strategies to a simple entity hierarchy, exploring the strengths and weaknesses of each approach. This comparison is intended to help you understand how to set up entity hierarchies, and to decide which mapping approach to take for the entity hierarchies in your own application.

Mapping JPA Entity Inheritance Hierarchies
Java has supported single-class inheritance - in which a non-interface class can extend a single other class - since its inception. While it's been common practice to exploit the code reuse and polymorphism benefits of inheritance in many areas of the business domain, data inheritance hasn't been well handled in the EJB persistence domain until now. This has been a major shortcoming, since in the real world, data is often hierarchical, and the lack of standard, built-in support for the inheritance of data objects has required countless workarounds and headaches. Leveraging the ease of use of Java SE annotations, JPA delivers declarative support for defining and mapping entity inheritance hierarchies, including abstract entities and polymorphic relationships and queries.

JPA Entity Inheritance Mapping Strategies
JPA provides declarative support for three main implementation strategies that dictate how the entities in a hierarchy map to underlying tables. To illustrate how these three strategies are manifested in code, Figure 1 shows a sample entity hierarchy that demonstrates both inheritance and polymorphic relationships.

In Figure 1, the Person entity serves as the root class in an entity hierarchy, and is extended by the Employee entity. Employee is further specialized to produce two other entities: FullTimeEmployee and PartTime-Employee.

Object/Relational Inheritance Mapping Strategies
Now that we've defined our entity hierarchy, let's look at how each of the three O/R strategies supported natively by JPA can be used to map this Person entity hierarchy, and the associated Address entity, to a relational schema. Here's a summary of each strategy defined by the InheritanceType enum:

public enum InheritanceType
{SINGLE_TABLE, JOINED, TABLE_PER_CLASS};

  • SINGLE_TABLE: Single-table-per-class inheritance hierarchy. This is the default strategy. The entity hierarchy is essentially flattened into the sum of its fields, and these fields are mapped down to a single table.
  • JOINED: Common base table, with joined subclass tables. In this approach, each entity in the hierarchy maps to its own dedicated table that maps only the fields declared on that entity. The root entity in the hierarchy is known as the base table, and the tables for all other entities in the hierarchy join with the base table.
  • TABLE_PER_CLASS: Single-table-per-outermost concrete entity class. This strategy maps each leaf (i.e., outermost, concrete) entity to its own dedicated table. Each such leaf entity branch is flattened, combining its declared fields with the declared fields on all of its super-entities, and the sum of these fields is mapped onto its table.
Single-Table-per-Class Inheritance Hierarchy
The default inheritance mapping strategy is SINGLE_TABLE in which all the entities in the class hierarchy map onto a single table. A dedicated discriminator column on this table identifies the specific entity type associated with each row, and each entity in the hierarchy is given a unique value to store in this column. By default, the discriminator value for an entity is its entity name, although an entity may override this value using the @DiscriminatorValue annotation. This approach performs well for querying, since only a single table is involved, and if your type hierarchy can abide by the practical limitations, this is probably the best approach to use. Figure 2 shows a diagram of a schema that maps our example entities using the SINGLE_TABLE strategy.

All the properties across the entity hierarchy rooted by the Person entity map to columns on a single table, CH04_ST_PERSON. This table holds a foreign key reference, bound to the HOME_ADDRESS column, to CH04_ST_ADDRESS, which is mapped to the Address entity. This column also has a unique key constraint, ensuring that each row in the CH04_ST_ADDRESS table corresponds to at most one row in the CH04_ST_PERSON table. It also holds a foreign key reference, using the MANAGER column, back to itself. This foreign key isn't constrained to be unique, indicating that multiple rows may hold the same value in their MANAGER column.

Listings 1 through 4 show how the entities in the Person hierarchy are mapped using the SINGLE_TABLE inheritance strategy. The strategy is declared on the root entity in the hierarchy and applies to all sub-entities in the hierarchy as well.

Let's take a look at the annotations that were introduced.

The @DiscriminatorColumn Annotation
By default, the persistence manager looks for a column named DTYPE in the root entity's table (CH04_ST_PERSON, in this case). In our example, we've named the discriminator column TYPE, so we explicitly annotate this setting, using the @DiscriminatorColumn(name = "TYPE") annotation. Were we to use a column named DTYPE, we could have skipped this annotation altogether and accepted the default value.

The @DiscriminatorValue Annotation
Each concrete entity declares, either explicitly or by tacitly accepting the default, a unique discriminator value that serves to identify the concrete entity type associated with each row in the table. The discriminator value defaults to the entity name, and in this example we've accepted this default value for each of the entities in the hierarchy.

Pros and Cons of the SINGLE_TABLE Strategy
Table 1 offers a look at some of the strengths and weaknesses of the SINGLE_TABLE entity inheritance strategy.

Common Base Table with Joined Subclass Tables
In the JOINED strategy, each entity in the hierarchy introduces its own table but only to map fields that are declared on that entity type. The root entity in the hierarchy maps to a root table that defines the primary key structure to be used by all tables in the entity hierarchy, as well as the discriminator column and optionally a version column. Each of the other tables in the hierarchy defines a primary key that matches the root table's primary key, and optionally adds a foreign key constraint from their ID column(s) to the root table's ID column(s). The non-root tables don't hold a discriminator type or version columns. Since each entity instance in the hierarchy is represented by a virtual row that spans its own table as well as the tables for all of its super-entities, it eventually joins with a row in the root table that captures this discriminator type and version information. Querying all the fields of any type requires a join across all the tables in the supertype hierarchy.

Figure 3 illustrates the schema that maps our entities using the JOINED inheritance strategy. As in the previous example, we've prefixed the tables with the strategy indicator, in this case CH04_JOIN_.

Listings 5 shows how the abstract entity in the Person hierarchy is mapped using the JOINED inheritance strategy.

Pros and Cons of the JOINED Strategy
Table 2 offers a look at some of the strengths and weaknesses of a JOINED entity inheritance strategy.

Single-Table-per-Outermost Concrete Entity Class
This inheritance mapping option maps each outermost concrete entity to its own dedicated table. Each table maps all of the fields in that entity's entire type hierarchy; since there's no shared table, no columns are shared. The only table structure requirement is that all tables must share a common primary key structure, meaning that the name(s) and type(s) of the primary key column(s) must match across all tables in the hierarchy. For good measure, Figure 4 illustrates our third type hierarchy using the joined table approach, which demonstrates the use of the single-table-per-outermost entity subclass strategy. The tables are required to share nothing in common except the structure of their primary key, and since the table implicitly identifies the entity type, no discriminator column is required. With this inheritance mapping strategy, only concrete entities - FullTimeEmployee, PartTimeEmployee, and Address in our example - require tables.

Listing 6 shows how the Person entity is mapped and annotated.

Pros and Cons of the TABLE_PER_CLASS Strategy
Table 3 offers a look at some of the strengths and weaknesses of the TABLE_PER_CLASS entity inheritance strategy.

Comparison of O/R Implementation Approaches
Now that we've explored the three inheritance mapping implementations, let's look at some of the characteristics of a class inheritance hierarchy to consider when choosing which implementation approach to use for your type hierarchies. The following list contains subjective questions about your own entity hierarchies. They don't have precise answers, but are meant to stimulate design consideration when building your application.

  • Class hierarchies can be static, with a fixed number of sub-types, or they can be dynamic, with varying numbers of sub-types. How often will you need to incorporate new sub-types into your hierarchy?
  • Hierarchies can be deep, with lots of sub-classes, or they can be shallow, with only a few. How granular is your hierarchy?
  • The types in a hierarchy may diverge greatly, with very different sets of properties on the sub-classes than the base class, or with very little difference in properties.
  • How much do the persistent property sets of your entities diverge from one another? Will other entities define relationships with classes in this type hierarchy, and if so, will the base classes frequently be the referenced type?
  • Will types in this hierarchy be frequently queried, updated, or deleted? How will the presence or absence of SQL JOIN or UNION operations impact your application's performance?
  • During the life of your application, how frequently will you be updating the structure of the type hierarchy itself? The impact of this type of change varies for each inheritance strategy, with considerations that include the following:
    - Adding or removing new types to the hierarchy (as when refactoring classes).
    - Adding, removing, or modifying fields on an entity in the hierarchy.
    - Adding, removing, or modifying relationships involving types in this hierarchy.
Summary
To support entity inheritance hierarchies, the JPA offers three mapping approaches for developers to choose from:
  • SINGLE_TABLE: Single-table-per-class inheritance hierarchy
  • JOINED: Common base table, with joined subclass tables
  • TABLE_PER_CLASS: Single-table-per-outermost concrete entity class
In this article we took a sample entity inheritance hierarchy and explored how to map the entities in this hierarchy using each of these three inheritance mapping strategies. We also examined some of the strengths and weaknesses in each strategy, to help you choose the approach that best serves each of the entity hierarchies in your application.

Now that you are familiar with how to set up an entity inheritance hierarchy in the JPA, you may wish to explore the many related areas also introduced in the JPA, including polymorphic relationships and JPQL queries, the use of non-entity mapped superclasses, and composite primary keys. For an examination of these features, with code samples, check out Beginning EJB 3 Application Development: From Novice to Professional (Apress, 2006).

More Stories By Raghu R. Kodali

Raghu R. Kodali is consulting product manager and SOA evangelist for Oracle Application Server. He leads next-generation SOA initiatives and J2EE feature sets for Oracle Application Server, with particular expertise in EJB, J2EE deployment, Web services, and BPEL. He holds a Masters degree in Computer Science and is a frequent speaker at technology conferences. Raghu is also a technical committee member for the OASIS SOA Blueprints specification, and a board member of Web Services SIG in OAUG. He maintains an active blog at Loosely Coupled Corner (www.jroller.com/page/raghukodali).

More Stories By Jonathan Wetherbee

Jon Wetherbee is a consulting engineer and tech lead for EJB development tools on Oracle's JDeveloper IDE. He has over 12 years of experience in development at Oracle, having built a variety of O/R mapping tools and holding responsibility for Oracle's core EJB toolset since EJB 1.1. In 1999, he received a patent for his work on integrating relational databases in an object-oriented environment.

Jon is co-author of 'Beginning EJB 3 Application Development: From Novice to Professional' (Apress, 2006), and enjoys speaking at user groups on EJB and related topics. Jon holds a BS in cognitive science from Brown University.

Comments (1) View Comments

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


Most Recent Comments
Scott Stanlick 12/05/06 04:36:57 PM EST

I really enjoyed your article on JPA. I have a question about "the CH04_ST_ADDRESS table corresponds to at most one row in the CH04_ST_PERSON table..." The UML depicts a many to (0,1) between Person/Address. Also, does the <> Home_Address on table CH04_ST_PERSON prevent only duplicate HOME_ADDRESS's from appearing within ID? I was a little confused about this. For example, my wife and I work for the same company and share the same address. Would this be permitted?

Thanks,
Scott