Developing with POJOs
Long before the EJB 3 specification was written, some developers disillusioned with EJB started to look for alternative frameworks. POJOs are an especially compelling alternative to EJBs. A POJO is simply a Java object that does not implement any special interfaces such as those defined by the EJB framework. The name was coined by Fowler, Rebbecca Parsons, and Josh MacKenzie [Fowler POJO] to give regular Java objects an exciting-sounding name. Later in this section you will see how this simple idea has some surprisingly important benefits.
However, POJOs by themselves are insufficient. In an enterprise application you need services such as transaction management, security, and persistence, which were previously provided by the EJB container. The solution is to use the increasingly popular so-called "lightweight" frameworks that replace some "heavyweight" parts of the J2EE stack. They do not completely replace the J2EE stack but can be used in combination with some parts of it to provide important enterprise services.
The four lightweight frameworks that I describe in this book are Hibernate, JDO, iBATIS, and Spring. Except for JDO, which is a specification, they are open source projects, which have helped drive the adoption of POJOs and lightweight frameworks by the community. Hibernate and JDO are persistence frameworks, which map POJOs to a relational database. They are layered on top of JDBC and significantly increase developer productivity. iBATIS is also layered on top of JDBC, but it maps POJOs to SQL statements and is a very convenient way to execute SQL statements. The Spring framework has a wide range of features that make it easier to use than EJB, including the equivalent of container-managed transactions for POJOs.
An important feature of these technologies is that they are nonintrusive. Unlike EJBs, they provide transactions and persistence without requiring the application classes to implement any special interfaces. Even when your application's classes are transactional or persistent, they are still POJOs, which means that you continue to experience the benefits of POJOs that I describe in this article.
Some excellent books are available that describe these frameworks in depth: Hibernate in Action [Bauer 2005], Spring in Action [Walls 2005], iBATIS in Action [Begin, forthcoming], and Java Data Objects [Russell 2003]. You do not need to read these books to understand and benefit from this book. But to apply what you learn here you do need to read them to learn the details.
In this section I will provide an overview of how to use POJOs and lightweight frameworks to redesign the money transfer service and make it easier to develop, test, and maintain. This new design is object-oriented POJO-based instead of a procedural EJB-based. It accesses the database using a persistence framework that is layered on top of JDBC instead of using JDBC directly. The business logic is encapsulated by a POJO façade instead of a session bean, and transactions are managed by the Spring framework instead of the EJB container. The business logic returns real business objects to the presentation tier instead of DTOs. The application is assembled by passing a component's dependencies as setter or constructor arguments instead of the component using Java Naming and Directory Interface (JNDI) lookups. Because the design is object-oriented and uses these lightweight technologies, it is much more developer-friendly than the EJB version we saw earlier.
Table 1.1 summarizes the differences between the two designs.
Table 1.1 Comparing classic EJB and POJO approaches
|Classic EJB approach||POJO approach|
|Organization||Procedural-style business logic||Object-oriented design|
|Database access||JDBC/SQL or Entity beans||Persistence framework|
|Returning data to the presentation tier||DTOs||Business objects|
|Transaction management||EJB container-managed transactions||Spring framework|
|Application assembly||Explicit JNDI lookups||Dependency injection|
Don't worry if you are not familiar with all of these terms. In this section, I'll examine each difference and explain and justify the POJO approach. You will see how to develop business logic using the POJO approach. I use the money transfer application from section 1.1.2 as an example.
Using an object-oriented design
Rather than structuring the money transfer example around methods such as transfer() and its helper methods, the code should be structured around an object model, which is a collection of classes that typically corresponds to realworld concepts. For example, in the money transfer application, the object model consists of classes such as Account, OverdraftPolicy, and BankingTransaction. In addition, there is a TransferService that coordinates the transfer of money from one account to another. Figure 1.1 shows the design.
Figure 1.1 An object model for the money transfer application
An Account maintains its balance and has an OverdraftPolicy, which determines what happens when the account is about to become overdrawn. OverdraftPolicy is an example of a Strategy pattern [Gang of Four] and there are two implementations of OverdraftPolicy: one for each type of real-world policy. Better yet, an OverdraftPolicy could encapsulate a rules engine and thereby enable the business rules for overdrafts to be changed dynamically. TransferTransaction, which is a subclass of BankingTransaction, records the transfer of money between two accounts.
Using an object-oriented design has a number of benefits. First, the design is easier to understand and maintain. Instead of consisting of one big class that does everything, it consists of a number of small classes that each have a small number of responsibilities. In addition, classes such as Account, BankingTransaction, and OverdraftPolicy closely mirror the real world, which makes their role in the design easier to understand.
Second, our object-oriented design is easier to test: each class can and should be tested independently. For example, we could write unit tests for Account and for each implementations of OverdraftPolicy. In comparison, an EJB can only be tested by calling its public methods, for example, transfer(), which is a lot more difficult. You can only test the complex functionality exposed by the public methods rather than test the simpler pieces of the design.
Finally, the object-oriented design in figure 1.2 is easier to extend because it can use well-known design patterns, such as the Strategy pattern and the Template Method pattern [Gang of Four]. Adding a new type of overdraft policy simply requires defining a new subclass of OverdraftPolicy. By contrast, extending an EJB-style procedural design usually requires changing the core code, and rewriting or chaining procedure calls together.
As you can see, our object-oriented design has some important benefits. But it is essential to know when it is not a good choice.