Simplify enterprise Java development with EJB 3.0, Part 2

news
Sep 12, 200515 mins

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.

A .par file is a jar file of the entity bean classes together with a simple configuration file META-INF/persistence.xml. The persistence.xml file defines the name of this persistence context. It tells the EJB 3.0 container which backend database (DataSource) to use for this set of entity beans. The persistence.xml file also contains implementation-specific configuration properties. For instance, JBoss EJB 3.0 is implemented on top of Hibernate 3.0. So you can pass any Hibernate configuration options in the persistence.xml file. Here is an example persistence.xml file with JBoss- and Hibernate-specific configuration properties on the SQL dialect and the second-level cache:

 <entity-manager>
  <name>cal</name>
  <jta-data-source>java:/DefaultDS</jta-data-source>
  <properties>
    <property name="hibernate.dialect"  
            value="org.hibernate.dialect.MySQLDialect" />
    <property name="hibernate.cache.provider_class"
            value="org.jboss.ejb3.entity.TreeCacheProviderHook"/>
    <property name="hibernate.treecache.mbean.object_name"
            value="jboss.cache:service=EJB3EntityTreeCache"/>
  </properties>
</entity-manager>

The EntityManager

Once you have the entity beans deployed, you must access and manipulate them via the EJB 3.0 EntityManager API. The EJB 3.0 container provides one EntityManager object for each deployed persistence context (i.e., the .par file). From an EJB 3.0 session bean POJO (see Part 1), you can inject the EntityManager object via the @PersistenceContext annotation and pass in the name of the context:

 

@Stateless public class ManagerBean implements Manager {

@PersistenceContext (unitName="cal") protected EntityManager em; // Use "em" // ... ... }

Basic operations

To create a new data object and save it to the database, you can simply use the Java new keyword to create the POJO and pass it to the EntityManager.persist() method:

 Person p = new Person ();
p.setName ("A new baby");
p.setDateOfBirth (new Date ());
em.persist (p);

To retrieve objects from the database, you can use the EJB 3.0 Query Language to search the database. The following example shows how to get all rows in the Person database table to return as a collection of Person Java objects:

 // Get all persons
Collection <Person> persons = (Collection <Person>) 
    em.createQuery("from Person p").getResultList();

Managed POJOs

The objects saved and retrieved by the EntityManager are managed in the persistence context. That means if the objects change later, the changes are automatically detected and persisted to the database. In the following example, we update a property of a managed POJO. The change is automatically detected by the EJB 3.0 container and sent to the database.

 

Person p = em.find(Person.class, personId); p.setName ("Another Name");

// p is automatically updated to the database // at the end of the current transaction. // No additional API calls.

Since EJB 3.0 entity beans are just POJOs, they can be serialized and passed over the network. If an object is not created by the container (e.g., it is passed in from a network connection or is a return value from a remote procedure call), the persistence context does not manage it. You can merge a nonmanaged POJO into the persistence context by calling the EntityManager.merge() method. Below is an example of merging a deserialized POJO into the current persistence context:

 

InputStream in; // Initialize the input stream. Person p = Util.deserialize (in);

// ... ... em.merge (p);

// p is a managed object now. Any change to p // is automatically detected and persisted. p.setName ("Another Name");

Database synchronization

When the EntityManager object is used in a session bean, it is tied with the server’s transaction context. The EntityManager commits and synchronizes its contents to the database when the server’s transaction commits. In a session bean, the server transaction commits at the end of the call stack by default. Of course, you can also specify the detailed transactional properties of each business method via annotations. The example below shows how to declare a new transaction for a session bean method:

 @TransactionAttribute(TransactionAttributeType.REQUIRESNEW)
public void update () {
  // Update Person objects in this method
  // and all updates are committed and flushed
  // to the database at the end of this method.
}  

Flushing database operations in a batch
To only flush changes to the database when a transaction commits, the container can group database operations in a batch and reduce the expensive database roundtrips.

If you need to flush the updates to the database before the transaction commits, you can call the EntityManager.flush() method explicitly. Or you may tag a method with the @FlushMode(FlushModeType.NEVER) annotation, and the transaction manager will not flush the updates to the database at the end of this method (i.e., the end of the transaction). In this case, you can manually flush all database updates to achieve the most control.

In conclusion

EJB 3.0 provides a simple and effective framework for mapping Java POJOs to relational tables in SQL databases. It uses sensible default mapping strategies based on the structure and naming of the Java class. Yet you can also override any defaults and handle complex object relationships using a simple set of annotations.

The EJB 3.0 EntityManager provides simple APIs to persist, find, and search objects from the database. Each EntityManager object is associated with a set of mapped POJOs and has its own database settings. It is also automatically tied to the application server’s transaction manager.

Dr. Michael Yuan works for JBoss. He specializes in end-to-end enterprise solutions. He is the author of three books, Nokia Smartphone Hacks, Enterprise J2ME, and Nokia Series: Developing Scalable Series 40 Applications. Yuan received a Ph.D. degree from the University of Texas at Austin.