May 10, 2011, 10:30 p.m.
posted by franni
Problems with an All-Entity EJB Solution
To understand why the patterns discussed in this chapter are important, let's first talk about the different types of EJBs that we've seen so far, discuss what architectures can be developed using them, then weigh the pros and cons of each architectural choice.
In Chapter 19 we discussed the idea of the session façade, whose purpose is to simplify the API of your system by limiting the number of business methods that EJB clients must deal with. In Chapter 21, we implemented a simple session façade that sat on top of a set of data mapper objects that hid the fact that our Employee domain objects were being persisted in a relational database. However, that design still entailed a lot of complexity. When we covered data mappers in detail in Chapter 16 you saw that there are many considerations that must go into building data mappers that persist objects into JDBC.
In Chapters 23 through 26 we investigated a part of the EJB specification that helps alleviate some of the complexity of building Data Mappers on top of JDBC—CMP entity beans. Are we done now? Can we simply declare that all of our domain objects should only be implemented as CMP beans and go home early? Unfortunately, it's not that simple. Let's consider what would happen if we took that approach.
Remember the TimeSheet domain model we introduced in Chapter 2, and implemented as a set of CMP entity beans in Chapters 23-25. What we've not done is consider how to obtain and manipulate our TimeSheet objects from an EJB client like a servlet or a struts action. So, let's examine a simple example, and work through the ramifications of some different design choices. Let's say we want to retrieve and display the TimeSheets for a particular Employee. No problem, you say! You would simply implement a findByEmployee() method on the TimeSheetLocalHome, code the appropriate EJB QL in the deployment descriptor, and then you may obtain a collection of TimeSheet entity beans.
So now our hypothetical GetTimeSheetsForEmployee servlet can receive back from the TimeSheetLocalHome a collection that would contain EJB client references to the corresponding local TimeSheet entity EJBs. The servlet could then iterate through the collection and ask each bean reference for the week ending Date, current approval state, and TimeSheetEntries for its EJB. However, therein lays the crux of our problem. You see, each call to the methods of our TimeSheet or TimeSheetEntry CMP entity beans from our servlet would be a different EJB transaction.
Recall from Chapter 28 how we described the transaction attribute settings for EJBs, and how if a call is made to an EJB where there is not an existing transaction context that the EJB may (depending on the setting) start a new EJB transaction, throw an exception, or start a local resource transaction. Let's say that the transaction setting for our TimeSheet CMP was set to required, which is the default. In this case, the call to TimeSheet.getWeekEndingDate() would start a new transaction, since none exists so far. The ramification of this would be that the TimeSheet entity bean would have to reload all its data from the database based on the primary key value (e.g., it would perform a SELECT statement on the database) and then return the week ending date.
The problem then is that when the servlet would send the getState() method to display the approval state, the same thing would occur again. The container would perform another SELECT statement, refetch all of the data for the EJB, and then (in effect) throw it away again at the end of the transaction. As the servlet would proceed to retrieve each TimeSheetEntry, the number of SELECT statements would grow as every EJB call results in yet another SELECT statement.
One potential way around this would be to use the facilities of JTA (described in Chapter 28) to obtain a UserTransaction within the servlet, and thus provide a shared transactional context for all of the entity bean calls within the servlet method. The downside of this is that you are now placing a requirement on all of your EJB client methods. Missing this and allowing bare entity bean access in just a single method could have an enormous performance impact on your system. What's more, this complication is simply at the wrong level. You've forced what is a business-layer or persistence-layer decision into the Controller layer, where it does not belong. One of the points of EJBs was to avoid this sort of complication in the first place.
As if that weren't bad enough, let's say that our client wasn't a servlet or struts action, but instead an application client. In that case, we couldn't use a local EJB; we would have to expose our entity beans through remote interfaces. Then, in addition to the overhead caused by all of the SELECT statements, we would additionally have serialization and network call overhead added on.
So, it seems we're at an impasse. We want the benefits that CMP entity beans give us in isolating us from the complexity of database coding, but we can't stand the performance impact that an all-entity solution would create. Luckily, our old friend, the Session Façade pattern, can help us out of this situation.