Simplify enterprise Java development with EJB 3.0, Part 2

POJO persistence made easy

In Part 1 of this series, I discussed the annotation-driven POJO (plain-old Java objects) programming model in Enterprise JavaBeans 3.0 (EJB). I explained how to develop POJO services, how to deliver container services to POJOs, and how to assemble applications using dependency injection. Those POJO services are typically used to encapsulate an application's business logic. Behind the business logic, most of today's enterprise applications have a data-model layer backed by a high-performance relational database.

In Part 2, I discuss how EJB 3.0 entity beans leverage POJO and annotations to greatly simplify your data model and its persistence-to-backend relational databases. Before we get into the details of EJB 3.0 entity beans, let's first discuss why data modeling and persistence are such big challenges in enterprise Java.

Object-relational mapping (ORM)

Inside the JVM, all data is modeled and encapsulated in a tree of classes and objects. However, in the backend relational database, the data is modeled as relational tables, which are interlinked via shared key fields. Those two different views of the same data represent a difficult challenge for enterprise Java developers: when you must save or retrieve data to or from the persistence datastore, you must convert the data back and forth between the object and relational representations, a process called object-relational mapping (ORM). In Java EE (Java Enterprise Edition, previously called J2EE), you can complete object-relational mapping in two ways:

  • Manually: You can use Java Database Connectivity to handle persistence directly—a straightforward solution for simple applications. The JDBC API's classes are closely modeled after tables, rows, and columns in the relational database. You must manually convert data between the application's internal object model to the JDBC object model. The JDBC approach is best if your application's internal model already resembles 2D relational tables.
  • Automatically: You can delegate the ORM task to a framework. The framework typically provides an API for you to work with arbitrary data objects. Through that API, you can save, retrieve, and search the database. The framework completes the object-relational conversion behind the scenes. Since the relational-specific SQL query does not fit the object interface, the ORM framework typically defines its own query language and automatically generates the correct SQL statements for the current relational database. For applications with complex data models, the framework-based approach could save you a lot of time and reduce errors.
Object database
An object database stores, retrieves, and searches objects directly in the datastore, which could be a good fit for Java applications since no ORM is needed. Unfortunately, today's object database technology remains relatively immature and slow compared with relational databases. You could reasonably say that a good ORM framework essentially provides an object database interface for a relational database. It gives you the best of both worlds.

In this article, I focus on the automated framework approach for ORM in enterprise Java applications. In the next section, I cover several popular ORM frameworks and the key innovations in EJB 3.0.

ORM frameworks

The EJB entity bean is the "official" ORM solution in Java EE. However, in EJB 1.x and 2.x, the entity beans are notoriously difficult to use for two reasons:

  • The EJB 1.x and 2.x entity beans must conform to a rigid component model. Each bean class must implement a home and a business interface. They must inherit from certain abstract classes and implement all methods even if many are empty. Such a rigid component model makes building object-oriented data models from EJB 1.x and 2.x entity beans impossible.
  • The EJB 1.x and 2.x container requires extremely verbose XML configuration files to map the entity beans to tables in the relational database. Those files are tedious and error prone.

In short, the EJB 1.x and 2.x entity bean is a poorly designed ORM framework that addresses the needs of neither the Java object data model nor the relational table data model. Unsatisfied with EJB 1.x and 2.x entity beans, developers look to other solutions for ORM. In the real world, the open source Hibernate (developed by JBoss) and Oracle's TopLink are the two most successful Java ORM frameworks. Both Hibernate and TopLink are POJO-based: they do not rely on any predefined component model. Instead, they take POJO data objects (in simple JavaBeans style) and automatically decipher how to map them, as well as the relationships among them, to relational databases. Usually one JavaBeans class maps to one database table, and relationships between the classes are mapped via foreign key fields in the tables. You can specify ORM metadata, such as the JavaBeans class's corresponding table name and the property's corresponding column name, in a simple and intuitive XML configuration file. You operate on those POJOs (e.g., saving, retrieving, and searching) via a utility class in the framework (e.g., the Session class in Hibernate).

The EJB 3.0 entity bean builds upon the ideas and success of Hibernate and TopLink. It provides a standard POJO ORM framework for Java EE. In addition, EJB 3.0 has two crucial innovations over existing POJO persistence solutions:

  • Instead of using XML files to specify ORM metadata, EJB 3.0 allows developers to annotate the mapping information directly in the POJO code. For instance, you can use annotations to specify the corresponding relational column name for each JavaBeans property. You will see more examples later in this article. Annotations make the mapping more intuitive and easier to maintain.
  • EJB 3.0 defines a new archive format for entity beans. Each archive defines a persistence context, with an independent set of configurations for the backend database and the ORM behaviors. I will discuss the persistence context later in this article.

Now, let's check out how EJB 3.0 accomplishes POJO ORM via several simple examples.

Map a simple object

In EJB 3.0, each entity bean is a simple JavaBeans-style class. To tell the EJB 3.0 container that this class should be mapped for persistence, you should annotate the class with the @Entity annotation.

Each entity bean class is mapped to a relational database table. By default, the table name matches the class name. You can specify another table name for the class using the @Table annotation. Each JavaBeans property of the bean class is mapped to a column in the table. Again, the column name is the property name by default, and you can change that by tagging the @Column annotation to the property's setter method. Below is a simple example of an EJB 3.0 entity bean class:

 @Entity
// @Table (name="AlternativeTableName")
public class Person implements Serializable {
  
  protected int id;
  protected String name;
  protected Date dateOfBirth;
  
  public void setId (int id) {
    this.id = id;
  }
  
  @Id(generate = GeneratorType.AUTO)
  public int getId () {
    return id;
  }
  
  public void setName (String name) {
    this.name = name;
  }
  
  // @Column (name="AlternativeColumnName")
  public String getName () {
    return name;
  }
  
  public void setDateOfBirth (Date dateOfBirth) {
    this.dateOfBirth = dateOfBirth;
  }
  
  public Date getDateOfBirth () {
    return dateOfBirth;
  }
}

After the container maps the Person class to the Person SQL database table, each Person instance is a row of data in the table.

Mapping a simple JavaBeans class is easy. But the advantage of automatic ORM frameworks really kicks in when you need to map interrelated objects. In the next section, let's see how EJB 3.0 handles object relationships.

Relationships

In a data model, classes typically have relationships with each other. For instance, a Person object can be associated with one Resume object and vice versa (one-to-one relationship); a Person object can be associated with multiple CreditCard objects, while a CreditCard only corresponds to one Person (one-to-many relationship); multiple Person objects can be associated with an Address object, while one Address only corresponds to one Person (many-to-one relationship).

In an object model, object references handle those relationships. For instance, a Person object can have a property (i.e., field) that references a Resume object and another property that is a collection of CreditCard objects. To tell the EJB 3.0 container about the relationship between the objects, you simply annotate those JavaBeans properties in the POJO:

 

@Entity public class Person implements Serializable {

// ... ... protected Resume resume; protected CreditCard [] cards; protected Address addr; // ... ... @OneToOne public Resume getResume () { return resume; } // ... ... @ManyToOne // @JoinColumn (name="MyCustomId") public Address getAddr () { return addr; } // ... ... @OneToMany public Collection <CreditCard> getCards () { return cards; } }

In relational databases, those relationships are automatically reconstructed by the EJB 3.0 container using foreign key fields. For instance, the Person table has a foreign key field that contains the primary key of the corresponding row in the Resume table. At runtime, the EJB 3.0 container enforces the one-to-one relationship: it guarantees that the Resume key value must be unique for each row in the Person table. To enable two-way lookup from the Resume table to the Person table, you can also define a Person property in the Resume and annotate it with the @OneToOne annotation as well.

The Person table also has a foreign key field that contains the primary key of the corresponding row in the Address table. In this case, the same Address primary key can appear in several Person rows due to the many-to-one relationship. For one-to-many relationships, the mapping is a little more complex as the foreign key column is defined in the source of the many-to-one table. So, in the CreditCard class, you must define a Person property with the @ManyToOne annotation.

Change the foreign key column name
The name of the foreign key column used in ORM is determined by the container or explicitly specified with the @JoinColumn annotation.

The association relationship discussed above is only one type of entity relationship. Another important relationship between entity bean classes is inheritance.

Inheritance

A key concept behind object-oriented design is inheritance. Using inheritance, you can build a complex tree of objects without duplicated code. For instance, a consultant is a person who provides consulting services for a fee. Hence, in our data-object model, the Consultant class inherits from the Person class with an additional rate property. Unfortunately, the inheritance concept does not exist in the world of relational databases. The ORM framework relies on mainly two approaches for mimicking this behavior:

  • The framework could generate a separate table for each class. The table for the subclass duplicates all the columns from the table for the superclass. Instances of the sub- and superclasses are saved to the appropriate tables.
  • The framework could use one table that contains columns for all subclass properties. Instances of both classes are stored in the same table—the rows of the superclass objects just have null values in the columns unavailable in the class (i.e., they are specific to the subclass). To make the inheritance mapping more robust, the table can also have a "differentiator" column that stores a flag to indicate which class each row maps to.

EJB 3.0 entity bean supports both inheritance mapping strategies, with the one-table mapping strategy being the default. You can simply annotate the subclass to specify the inheritance strategy and the name of the differentiator column. Below is the example of the Consultant class, which inherits from the Person class:

 

@Entity @Inheritance(discriminatorValue="C") @DiscriminatorColumn(name="person_type") public class Consultant extends Person {

protected double rate; public void setRate (double rate) { this.rate = rate; } public double getRate () { return rate; } }

In the above example, the container uses the default strategy to map the Consultant class in the same table as the Person class. If the person_type column in the table has value C, the current row represents a Consultant object. Otherwise, the current row represents a regular Person object.

The persistence archive

Now that you have a set of annotated EJB 3.0 entity bean classes for your data model, you can bundle them together and deploy them into a server environment. EJB 3.0 defines a special archive file format, known as the persistence archive (.par file suffix), for entity beans.

1 2 Page 1
Page 1 of 2