Chris Richardson - enterprise POJOs

My book

Calendar

««Aug 2008»»
SMTWTFS
      12
3456789
10111213141516
17181920212223
24252627282930
31

My Top Tags

                                                                               

My RSS Feeds








 

I run a consulting and training company that helps organizations build better software faster.

We provide a variety of services including:

  • Development - we can build your application for you
  • Deployment - we can find a hosting partner or deploy your application on Amazon EC2
  • Training classes for Spring, Hibernate and Acegi Security
  • Jumpstarts to get your project off to the right start
  • Reviews to improve your architecture, code and development process

For more information contact me.

 

My bookmarks

Mailing List

Returning to the EJB cult - developing with EJB 3 Part1

posted Wednesday, 20 April 2005

About a year ago, with the help of Spring and Hibernate, I escaped from the EJB cult which had held me captive for five long years. It was such a relief to no longer perform the painful EJB development rituals that I had been brainwashed into believing were a good thing. With Spring and Hibernate I was able to develop and test POJO business logic without having to wait for my code to deploy in an application server. I had the freedom to map a complex domain model to database schema. I could develop loosely coupled, easy to test, transactional components. Life without EJB was pretty good. So it was with some trepidation that I began to port the sample application from my book to EJB3 using JBoss EJB 3 preview 5, which conforms to the February 2005 specification.

About the application

The example application is the order processing system for a fictitious company that delivers takeout orders from restaurants to customer’s homes and offices. The business tier consists of a domain model that is encapsulated by a façade. Originally, it was an EJB application. The domain model consisted of entity beans encapsulated by a session façade. In the second version the entity bean domain model was replaced with a POJO domain model that used JDO for persistence. The latest version has a POJO domain model, which uses either Hibernate or JDO for persistence, and a POJO façade, which uses Spring AOP for transaction management.

Overview of the domain model

The first step in porting the application to EJB3 was to migrate the domain model from JDO/Hibernate so let’s have a look at its structure. The following diagram shows some of the persistent classes.

 

 

The key classes are as follows:

  • PendingOrder - this application’s shopping cart. PendingOrder has a delivery time and a delivery address, a list of line items, an associated restaurant and a coupon.
  • PendingOrderLineItem - each line item has a quantity and an associated MenuItem.
  • Restaurant - represents a restaurant that prepares food for delivery. A restaurant has a name attribute and a list of menu items available for delivery, a geographic service area, which consists of a set of zip codes and opening hours.
  • MenuItem – describes a menu item and has a name, a description and a price
  • TimeRange – describes when a restaurant is open and consists of a day of the week, start time and end time
  • Coupon - represents a discount that can be applied to an order. Coupons are identified by a code and are only valid for a specified time period. The Coupon class is another example of the Strategy pattern

The object/relational mapping for this domain model is pretty straightforward:

  • Each class is mapped to its own table except for the PaymentInformation and Address classes, which are mapped to the parent object’s table, i.e. the PendingOrder.
  • The PendingOrder-PendingOrderLineItem, Restaurant-MenuItem and Restaurant-TimeRange relationships, which are unidirectional one-to-many relationships, are represented by foreign keys from the child table (e.g. PENDING_ORDER_LINE_ITEM) to the parent table (e.g. PENDING_ORDER).
  • The ordered relationships also have an index column in the child table that indicates the row’s position in the list.
  • The Coupon class hierarchy is mapped to a single COUPON table, which has a COUPON_TYPE column that specifies the type of the coupon.

All of this is handled quite easily by JDO and Hibernate. So what about EJB3?

Persisting the domain model with EJB3

I want to start off by saying that EJB3 persistence is huge improvement over EJB2 CMP. Entity beans are very POJO-like and do not have to implement any special interfaces or any boilerplate methods etc. To make a class persistent you simply have to add an @Entity annotation to the class and annotate some of its fields. However, as I described previously the current version of the draft specification has many significant limitations that impact the design of simple domain model like this one. Consequently, I had to change many of the classes.

No support for persistent interfaces or abstract classes

The first problem is that EJB3 currently does not support persisting interfaces or abstract classes. I had to turn the Coupon interface into a concrete class:

@Entity(access = AccessType.FIELD)
@Table(name = "FTGO_COUPON")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE, discriminatorType = DiscriminatorType.STRING)
@DiscriminatorColumn(name = "COUPON_TYPE")
public class Coupon implements Serializable {

@Id(generate = GeneratorType.AUTO)
private int id;

private String code;

public Coupon() {
}

protected Coupon(String code) {
this.code = code;
}

public String getCode() {
return code;
}

// TODO - must be overriden
public double getDeliveryChargeDiscount(
PendingOrder pendingOrder) {
throw new RuntimeException(
"Must be implemented by subclass");
}

// TODO - must be overriden
public double getSubtotalDiscount(
PendingOrder pendingOrder) {
throw new RuntimeException(
"Must be implemented by subclass");
}

}

This is pretty nasty especially because you are forced to implement methods that should be abstract.

No support for lists

One-to-many relationships such as PendingOrder-PendingOrderLineItem and Restaurant-MenuItem are implemented by lists:

 

class PendingOrder {

List<PendingOrderLineItem> lineItems;

}

 

Sadly, the current EJB 3 specification does not let you persist lists. Like EJB2 you can only persist sets or collections and so I had to make the following change:

@Entity(access = AccessType.FIELD)
@Table(name = "FTGO_PENDING_ORDER")
class PendingOrder {

@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name="PENDING_ORDER_ID",referencedColumnName="ID")
protected Collection<PendingOrderLineItem> lineItems;

}

On a positive note I was happy to discover that JBOSS EJB 3 supports the unidirectional one-to-many foreign key mapping, which for some strange reason is currently an optional feature of the EJB 3 spec.

No support for collections of primitive types

A restaurant’s serviceArea is a collection of Strings:

class Restaurant {

protected Set<String> serviceArea;

}

Unfortunately, the current EJB3 specification does not you persist collections of non-entities. I had to define a ZipCode entity bean that wraps the String and has many-to-many relationship with the Restaurant:


@Entity (access=AccessType.FIELD)
@Table(name="FTGO_RESTAURANT")
class Restaurant {

@ManyToMany
protected Set<ZipCode> serviceArea;

}

This is very unfortunate because POJO domain models often have collections of non-entities.

No support for defining the mapping for doubly embedded classes

Defining the mapping for PendingOrder.address was pretty straightforward using the @AttributeOverride annotation:

class PendingOrder {

@Embedded( {
@AttributeOverride(name = "street1", column = { @Column(name = "DELIVERY_STREET1") }),
@AttributeOverride(name = "street2", column = { @Column(name = "DELIVERY_STREET2") }),
@AttributeOverride(name = "city", column = @Column(name = "DELIVERY_CITY")),
@AttributeOverride(name = "state", column = @Column(name = "DELIVERY_STATE")),
@AttributeOverride(name = "zip", column = @Column(name = "DELIVERY_ZIP")) })
private Address deliveryAddress;

However, it turns out that you currently cannot write annotations in the parent class that define the mapping for doubly embedded objects such as PendingOrder.paymentInformation.address. I had to use @AttributeOverride inside the PaymentInformation class to change the mapping for PaymentInformation.address, which incorrectly assumes that PaymentInformation is only used in one place.

 

Summary
Some aspects of using EJB3 persistence are quite straightforward. For example, because it has sensible defaults for the object/relational mapping you can start off by only using minimal annotations. However, as you can see, the current EJB3 persistence specification supports only some of the basic requirements of a POJO domain model. You must make significant changes to even a simple domain model.

In the next installment I will describe the classes that use the EntityManager to create, find and delete persistent objects.

 

 

tags:            

links: digg this    del.icio.us    technorati    reddit




1. eieio left...
Friday, 4 November 2005 8:59 pm

"No support for defining the mapping for doubly embedded classes"

-- good finding.. I found this today... it sucks.


2. eieio left...
Friday, 4 November 2005 8:59 pm

"No support for defining the mapping for doubly embedded classes"

-- good finding.. I found this today... it sucks.


3. Frank Klomp left...
Monday, 9 April 2007 11:26 am

Thanks to your comments, I found the reason why my List-based OneToMany relationships were not persisted, regardless of the cascade settings.


4. ShahulR left...
Wednesday, 29 August 2007 6:20 am

"The first problem is that EJB3 currently does not support persisting interfaces or abstract classes. I had to turn the Coupon interface into a concrete class:"

Why would you expect abstract things that don't exist to be persisted! By their very definitions, interfaces and abstract classes are just blueprints, no concretes, so it's a bad expectation.