Implementing conversations (Hibernate)

 

You’ve tried the examples in previous topics and stored and loaded objects inside transactions. Very likely you’ve noticed that code examples of five lines are excellent to help you understand a particular issue and learn an API and how objects change their state. If you take the next step and try to apply what you’ve learned in your own application, you’ll probably soon realize that you’re missing two important concepts.

The first concept we’ll show you in this topic—persistence context propagation—is useful when you have to call several classes to complete a particular action in your application and they all need database access. So far, we had only a single method that opened and closed a persistence context (a Session or an Entity-Manager) internally. Instead of passing the persistence context between classes and methods manually, we’ll show you the mechanisms in Hibernate and Java Persistence that can take care of propagation automatically. Hibernate can help you to create more complex units of work.

The next design problem you’ll run into is that of application flow when your application user has to be guided through several screens to complete a unit of work. You must create code that controls the navigation from screen to screen— however, this is outside of the scope of persistence, and we won’t have much to say about it in this topic. What is partly the responsibility of the persistence mechanism is the atomicity and isolation of data access for a unit of work that spans possible user think-time. We call a unit of work that completes in several client/server request and response cycles a conversation. Hibernate and Java Persistence offer several strategies for the implementation of conversations, and in this topic we show you how the pieces fit together with realistic examples.

We start with Hibernate and then, in the second half of the topic, discuss JPA conversations. Let’s create more complex data access examples first, to see how several classes can reuse the same persistence context through automatic propagation.

Propagating the Hibernate Session

Recall the use case we introduced in the previous topic: An event that triggers the end of an auction has to be processed. For the following examples, it doesn’t matter who triggered this event; probably an automatic timer ends auctions when their end date and time is reached. It could also be a human operator who triggers the event.

To process the event, you need to execute a sequence of operations: check the winning bid for the auction, charge the cost of the auction, notify the seller and winner, and so on. You could write a single class that has one big procedure. A better design is to move the responsibility for each of these steps into reusable smaller components and to separate them by concern. For now, assume that you followed our advice and that several classes need to be called inside the same unit of work to process the closing of an auction.

The use case for Session propagation

Look at the code example in Listing 11.1, which controls the processing of the event.

Listing 11.1 Controller code that closes and ends an auction

Listing 11.1 Controller code that closes and ends an auction

The ManageAuction class is called a controller. Its responsibility is to coordinate all the steps necessary to process a particular event. The method endAuction() is called by the timer (or user interface) when the event is triggered. The controller doesn’t contain all the code necessary to complete and close the auction; it delegates as much as possible to other classes. First, it needs two stateless service objects called data access objects (DAOs) to complete its work—they’re instantiated directly for each instance of the controller. The endAuction() method uses the DAOs when it needs to access the database. For example, the ItemDAO is used to reattach the detached item and to query the database for the highest bid. The PaymentDAO is used to make a transient new payment persistent. You don’t even need to see how the seller and winner of the auction are notified—you have enough code to demonstrate that context propagation is required.

The code in listing 11.1 doesn’t work. First, there is no transaction demarcation. All the code in endAuction() is to be considered an atomic unit of work: It either all fails or all completes successfully. So, you need to wrap a transaction around all these operations. You’ll do that with different APIs next.

A more difficult problem is the persistence context. Imagine that ItemDAO and PaymentDAO use a different persistence context in every method (they’re stateless). In other words, itemDAO.getMaxBid() and paymentDAO.makePersistentO both open, flush, and close their own persistence context (a Session or an EntityManager). This is an anti-pattern that should be avoided at all times! In the Hibernate world, it’s known as session-per-operation, and it’s the first thing a good Hibernate developer should take care of when examining an application design for performance bottlenecks. A single persistence context shouldn’t be used to process a particular operation, but the whole event (which, naturally, may require several operations). The scope of the persistence context is often the same scope as the database transaction. This is also known as session-per-request; see figure 11.1.

Let’s add transaction demarcation to the ManageAuction controller and propagate the persistence context between data access classes.

A particular event is served with a single persistence context.

Figure 11.1

A particular event is served with a single persistence context.

Propagation through thread-local

Hibernate offers automatic persistence-context propagation for stand-alone Java applications with plain Java SE and for any application that uses JTA with or without EJBs. We strongly encourage you to consider this feature in your own application, because all the alternatives are less sophisticated.

In Hibernate, you can access the current Session to access the database. For example, consider the ItemDAO implementation with Hibernate:

tmp9D126_thumb

The method getSessionFactory() returns the global SessionFactory. How it does that is entirely up to you and how you configure and deploy your application—it could come from a static variable (HibernateUtil), be looked up from the JNDI registry, or be injected manually when the ItemDAO is instantiated. This kind of dependency management is trivial; the SessionFactory is a thread-safe object.

The getCurrentSession() method on the SessionFactory is what we want to discuss. (The PaymentDAO implementation also uses the current Session in all methods.) What is the current Session, what does current refer to? Let’s add transaction demarcation to the controller that calls ItemDAO and PaymentDAO, see listing 11.2.

Listing 11.2 Adding transaction demarcation to the controller

Listing 11.2 Adding transaction demarcation to the controller tmp9D128_thumb

The unit of work starts when the endAuction() method is called. If sessionFac-tory.getCurrentSession() is called for the first time in the current Java thread, a new Session is opened and returned—you get a fresh persistence context. You immediately begin a database transaction on this new Session, with the Hibernate Transaction interface (which translates to JDBC transactions in a Java SE application).

All the data-access code that calls getCurrentSession() on the global shared SessionFactory gets access to the same current Session—if it’s called in the same thread. The unit of work completes when the Transaction is committed (or rolled back). Hibernate also flushes and closes the current Session and its persistence context if you commit or roll back the transaction. The implication here is that a call to getCurrentSession() after commit or rollback produces a new Session and a fresh persistence context.

You effectively apply the same scope to the database transaction and the persistence context. Usually, you’ll want to improve this code by moving the transaction and exception handling outside of the method implementation. A straightforward solution is a transaction interceptor.

Internally, Hibernate binds the current Session to the currently running Java thread. (In the Hibernate community, this is also known as the ThreadLocal Session pattern.) You have to enable this binding in your Hibernate configuration by setting the hibernate.current_session_context_class property to thread.

If you deploy your application with JTA, you can enable a slightly different strategy that scopes and binds the persistence context directly to the system transaction.

Propagation with JTA

In previous sections, we always recommended the JTA service to handle transactions, and we now repeat this recommendation. JTA offers, among many other things, a standardized transaction demarcation interface that avoids the pollution of code with Hibernate interfaces. Listing 11.3 shows the ManageAuction controller refactored with JTA.

Listing 11.3 Transaction demarcation with JTA in the controller

Listing 11.3 Transaction demarcation with JTA in the controller tmp9D130_thumb

This code is free from any Hibernate imports. And, more important, the ItemDAO and PaymentDAO classes, which internally use getCurrentSession(), are unchanged. A new persistence context begins when getCurrentSession() is called for the first time in one of the DAO classes. The current Session is bound automatically to the current JTA system transaction. When the transaction completes, either through commit or rollback, the persistence context is flushed and the internally bound current Session is closed.

The exception handling in this code is slightly different compared to the previous example without JTA, because the UserTransaction API may throw checked exceptions (and the JNDI lookup in the constructor may also fail).

You don’t have to enable this JTA-bound persistence context if you configure your Hibernate application for JTA; getCurrentSession() always returns a Session scoped and bound to the current JTA system transaction.

(Note that you can’t use the Hibernate Transaction interface together with the getCurrentSession() feature and JTA. You need a Session to call begin-Transaction(), but a Session must be bound to the current JTA transaction—a chicken and egg problem. This again emphasizes that you should always use JTA when possible and Hibernate Transaction only if you can’t use JTA.)

Propagation with EJBs

If you write your controller as an EJB and apply container-managed transactions, the code (in listing 11.4) is even cleaner.

Listing 11.4 Transaction demarcation with CMT in the controller

Listing 11.4 Transaction demarcation with CMT in the controller tmp9D132_thumb

The current Session is bound to the transaction that is started for the endAuc-tion() method, and it’s flushed and closed when this method returns. All code that runs inside this method and calls sessionFactory.getCurrentSession() gets the same persistence context.

If you compare this example with the first nonworking example, listing 11.1, you’ll see that you had to add only some annotations to make it work. The @TransactionAttribute is even optional—it defaults to REQUIRED. This is why EJB 3.0 offers a simplified programming model. Note that you haven’t used a JPA interface so far; the data access classes still rely on the current Hibernate Session. You can refactor this easily later—concerns are cleanly separated.

You now know how the persistence context is scoped to transactions to serve a particular event and how you can create more complex data-access operations that require propagation and sharing of a persistence context between several objects. Hibernate internally uses the current thread or the current JTA system transaction to bind the current Session and persistence context. The scope of the Session and persistence context is the same as the scope of the Hibernate Transaction or JTA system transaction.

We now focus on the second design concept that can significantly improve how you design and create database applications. We’ll implement long-running conversations, a sequence of interactions with the application user that spans user think-time.

Conversations with Hibernate

You’ve been introduced to the concept of conversations several times throughout the previous topics. The first time, we said that conversations are units of work that span user think-time. We then explored the basic building blocks you have to put together to create conversational applications: detached objects, reattach-ment, merging, and the alternative strategy with an extended persistence context.

It’s now time to see all these options in action. We build on the previous example, the closing and completion of an auction, and turn it into a conversation.

Providing conversational guarantees

You already implemented a conversation—it just wasn’t long. You implemented the shortest possible conversation: a conversation that spanned a single request from the application user: The user (let’s assume we’re talking about a human operator) clicks the Complete Auction button in the CaveatEmptor administration interface. This requested event is then processed, and a response showing that the action was successful is presented to the operator.

In practice, short conversations are common. Almost all applications have more complex conversations—more sophisticated sequences of actions that have to be grouped together as one unit. For example, the human operator who clicks the Complete Auction button does so because they’re convinced this auction should be completed. They make this decision by looking at the data presented on the screen—how did the information get there? An earlier request was sent to the application and triggered the loading of an auction for display. From the application user’s point of view, this loading of data is part of the same unit of work. It seems reasonable that the application also should know that both events—the loading of an auction item for display and the completion of an auction—are supposed to be in the same unit of work. We’re expanding our concept of a unit of work and adopting the point of view of the application user. You group both events into the same conversation.

The application user expects some guarantees while going through this conversation with the application:

■ The auction the user is about to close and end isn’t modified while they look at it. Completion of the auction requires that the data on which this decision is based is still unchanged when the completion occurs. Otherwise, the operator is working with stale data and probably will make a wrong decision.

■ The conversation is atomic: At any time the user can abort the conversation, and all changes they made are rolled back. This isn’t a big issue in our current scenario, because only the last event would make any permanent changes; the first request only loads data for display. However, more complex conversations are possible and common.

You as the application developer wish to implement these guarantees with as little work as possible.

We now show you how to implement long conversations with Hibernate, with and without EJBs. The first decision you’ll have to make, in any environment, is between a strategy that utilizes detached objects and a strategy that extends the persistence context.

Conversations with detached objects

Let’s create the conversation with native Hibernate interfaces and a detached object strategy. The conversation has two steps: The first step loads an object, and the second step makes changes to the loaded object persistent. The two steps are shown in figure 11.2.

A two-step conversation implemented with detached objects

Figure 11.2 A two-step conversation implemented with detached objects

The first step requires a Hibernate Session to retrieve an instance by its identifier (assume this is a given parameter). You’ll write another ManageAuction controller that can handle this event:

tmp9D134_thumb

We simplified the code a little to avoid cluttering the example—you know exception handling isn’t really optional. Note that this is a much simpler version than the one we showed previously; we want to show you the minimum code needed to understand conversations. You can also write this controller with DAOs, if you like.

A new Session, persistence context, and database transaction begin when the getAuction() method is called. An object is loaded from the databases, the transaction commits, and the persistence context is closed. The item object is now in a detached state and is returned to the client that called this method. The client works with the detached object, displays it, and possibly even allows the user to modify it.

The second step in the conversation is the completion of the auction. That is the purpose of another method on the ManageAuction controller. Compared to previous examples, you again simplify the endAuction() method to avoid any unnecessary complication:

tmp9D135_thumbtmp9D136_thumb

The client calls the endAuction() method and passes back the detached item instance—this is the same instance returned in the first step. The update() operation on the Session reattaches the detached object to the persistence context and schedules an SQL UDPATE. Hibernate must assume that the client modified the object while it was detached. (Otherwise, if you’re certain that it hasn’t been modified, a lock() would be sufficient.) The persistence context is flushed automatically when the second transaction in the conversation commits, and any modifications to the once detached and now persistent object are synchronized with the database.

The saveOrUpdate() method is in practice more useful than upate(), save(), or lock(): In complex conversations, you don’t know if the item is in detached state or if it’s new and transient and must be saved. The automatic state-detection provided by saveOrUpdate() becomes even more useful when you not only work with single instances, but also want to reattach or persist a network of connected objects and apply cascading options. Also reread the definition of the merge() operation and when to use merging instead of reattachment.

So far, you’ve solved only one of the conversation implementation problems: little code was required to implement the conversation. However, the application user still expects that the unit of work is not only isolated from concurrent modifications, but also atomic.

You isolate concurrent conversations with optimistic locking. As a rule, you shouldn’t apply a pessimistic concurrency-control strategy that spans a long-running conversation—this implies expensive and nonscalable locking. In other words, you don’t prevent two operators from seeing the same auction item. You hope that this happens rarely: You’re optimistic. But if it happens, you have a conflict resolution strategy in place.Then, every SQL UPDATE or DELETE at any time during the conversation will include a version check against the state present in the database. You get a StaleObjectStateException if this check fails and then have to take appropriate action. In this case, you present an error message to the user (“Sorry, somebody modified the same auction!”) and force a restart of the conversation from step one.

How can you make the conversation atomic? The conversation spans several persistence contexts and several database transactions. But this isn’t the scope of a unit of work from the point of view of the application user; she considers the conversation to be an atomic group of operations that either all fail or all succeed. In the current conversation this isn’t a problem, because you modify and persist data only in the last (second) step. Any conversation that only reads data and delays all reattachment of modified objects until the last step is automatically atomic and can be aborted at any time. If a conversation reattaches and commits modifications to the database in an intermediate step, it’s no longer atomic.

One solution is to not flush the persistence contexts on commit—that is, to set a FlushMode.MANUAL on a Session that isn’t supposed to persist modifications (of course, not for the last step of the conversation). Another option is to use compensation actions that undo any step that made permanent changes, and to call the appropriate compensation actions when the user aborts the conversation. We won’t have much to say about writing compensation actions; they depend on the conversation you’re implementing.

Next, you implement the same conversation with a different strategy, eliminating the detached object state. You extend the persistence context to span the whole conversation.

Extending a Session for a conversation

The Hibernate Session has an internal persistence context. You can implement a conversation that doesn’t involve detached objects by extending the persistence context to span the whole conversation. This is known as the session-per-conversation strategy, as shown in figure 11.3.

A new Session and persistence context are opened at the beginning of a conversation. The first step, loading of the Item object, is implemented in a first database transaction. The Session is automatically disconnected from the underlying JDBC Connection as soon as you commit the database transaction. You can now hold on to this disconnected Session and its internal persistence context during user think-time. As soon as the user continues in the conversation and executes the next step, you reconnect the Session to a fresh JDBC Connection by beginning a second database transaction. Any object that has been loaded in this conversation is in persistent state: It’s never detached. Hence, all modifications you made to any persistent object are flushed to the database as soon as you call flush() on the Session. You have to disable automatic flushing of the Session by setting a FlushMode.MANUAL—you should do this when the conversation begins and the Session is opened.

A disconnected persistence context extended to span a conversation

Figure 11.3 A disconnected persistence context extended to span a conversation

Modifications made in concurrent conversations are isolated, thanks to optimistic locking and Hibernate’s automatic version-checking during flushing. Atomicity of the conversation is guaranteed if you don’t flush the Session until the last step, the end of the conversation—if you close the unflushed Session, you effectively abort the conversation.

We need to elaborate on one exception to this behavior: the time of insertion of new entity instances. Note that this isn’t a problem in this example, but it’s something you’ll have to deal with in more complex conversations.

Delaying insertion until flush-time

To understand the problem, think about the way objects are saved and how their identifier value is assigned. Because you don’t save any new objects in the Complete Auction conversation, you haven’t seen this issue. But any conversation in which you save objects in an intermediate step may not be atomic.

The save() method on the Session requires that the new database identifier of the saved instance must be returned. So, the identifier value has to be generated when the save() method is called. This is no problem with most identifier generator strategies; for example, Hibernate can call a sequence, do the in-mem-ory increment, or ask the hilo generator for a new value. Hibernate doesn’t have to execute an SQL INSERT to return the identifier value on save() and assign it to the now-persistent instance.

The exceptions are identifier-generation strategies that are triggered after the INSERT occurs. One of them is identity, the other is select; both require that a row is inserted first. If you map a persistent class with these identifier generators, an immediate INSERT is executed when you call save()! Because you’re committing database transactions during the conversation, this insertion may have permanent effects.

Look at the following slightly different conversation code that demonstrates this effect:

tmp9D-138_thumb

You may expect that the whole conversation, the two steps, can be rolled back by closing the unflushed persistence context. The insertion of the newItem is supposed to be delayed until you call flush() on the Session, which never happens in this code. This is the case only if you don’t pick identity or select as your identifier generator. With these generators, an INSERT must be executed in the second step of the conversation, and the INSERT is committed to the database.

One solution uses compensation actions that you execute to undo any possible insertions made during a conversation that is aborted, in addition to closing the unflushed persistence context. You’d have to manually delete the row that was inserted. Another solution is a different identifier generator, such as a sequence, that supports generation of new identifier values without insertion.

The persist() operation exposes you to the same problem. However, it also provides an alternative (and better) solution. It can delay insertions, even with post-insert identifier generation, if you call it outside of a transaction:

tmp9D-139_thumb

The persist() method can delay inserts because it doesn’t have to return an identifier value. Note that the newItem entity is in persistent state after you call persist(), but it has no identifier value assigned if you map the persistent class with an identity or select generator strategy. The identifier value is assigned to the instance when the INSERT occurs, at flush-time. No SQL statement is executed when you call persist() outside of a transaction. The newItem object has only been queued for insertion.

Keep in mind that the problem we’ve discussed depends on the selected identifier generator strategy—you may not run into it, or you may be able to avoid it. The nontransactional behavior of persist() will be important again later in this topic, when you write conversations with JPA and not Hibernate interfaces.

Let’s first complete the implementation of a conversation with an extended Session. With a session-per-conversation strategy, you no longer have to detach and reattach (or merge) objects manually in your code. You must implement infrastructure code that can reuse the same Session for a whole conversation.

Managing the current Session

The current Session support we discussed earlier is a switchable mechanism. You’ve already seen two possible internal strategies: One was thread-bound, and the other bound the current Session to the JTA transaction. Both, however, closed the Session at the end of the transaction. You need a different scope of the Session for the session-per-conversation pattern, but you still want to be able to access the current Session in your application code.

A third built-in option does exactly what you want for the session-per-conversa-tion strategy. You have to enable it by setting the hibernate.current_session_ context_class configuration option to managed. The other built-in options we’ve discussed are thread and jta, the latter being enabled implicitly if you configure Hibernate for JTA deployment. Note that all these built-in options are implementations of the org.hibernate.context.CurrentSessionContext interface; you could write your own implementation and name the class in the configuration. This usually isn’t necessary, because the built-in options cover most cases.

The Hibernate built-in implementation you just enabled is called managed because it delegates the responsibility for managing the scope, the start and end of the current Session, to you. You manage the scope of the Session with three static methods:

tmp9D-140_thumb

You can probably already guess what the implementation of a session-per-conver-sation strategy has to do:

■ When a conversation starts, a new Session must be opened and bound with ManagedSessionContext.bind() to serve the first request in the conversation. You also have to set FlushMode.MANUAL on that new Session, because you don’t want any persistence context synchronization to occur behind your back.

■ All data-access code that now calls sessionFactory.getCurrentSession() receives the Session you bound.

■ When a request in the conversation completes, you need to call Managed-SessionContext.unbind() and store the now disconnected Session somewhere until the next request in the conversation is made. Or, if this was the last request in the conversation, you need to flush and close the Session.

All these steps can be implemented in an interceptor.

Creating a conversation interceptor

You need an interceptor that is triggered automatically for each request event in a conversation. If you use EJBs, as you’ll do soon, you get much of this infrastructure code for free. If you write a non- Java EE application, you have to write your own interceptor. There are many ways how to do this; we show you an abstract interceptor that only demonstrates the concept. You can find working and tested interceptor implementations for web applications in the CaveatEmptor download in the org.hibernate.ce.auction.web.filter package.

Let’s assume that the interceptor runs whenever an event in a conversation has to be processed. We also assume that each event must go through a front door controller and its execute() action method—the easiest scenario. You can now wrap an interceptor around this method; that is, you write an interceptor that is called before and after this method executes. This is shown in figure 11.4; read the numbered items from left to right.

Interception of events to manage the lifecycle of a Session

Figure 11.4 Interception of events to manage the lifecycle of a Session

When the first request in a conversation hits the server, the interceptor runs and opens a new Session ©; automatic flushing of this Session is immediately disabled. This Session is then bound into Hibernate’s ManagedSessionContext. A transaction is started © before the interceptor lets the controller handle the event. All code that runs inside this controller (or any DAO called by the controller) can now call sessionFactory.getCurrentSession() and work with the Session. When the controller finishes its work, the interceptor runs again and unbinds the current Session ©. After the transaction is committed ©, the Session is disconnected automatically and can be stored during user think-time.

Now the server waits for the second request in the conversation.

As soon as the second request hits the server, the interceptor runs, detects that there is a disconnected stored Session, and binds it into the ManagedSessionContext ©. The controller handles the event after a transaction was started by the interceptor ©. When the controller finishes its work, the interceptor runs again and unbinds the current Session from Hibernate. However, instead of disconnecting and storing it, the interceptor now detects that this is the end of the conversation and that the Session needs to be flushed H, before the transaction is committed ©. Finally, the conversation is complete and the interceptor closes the Session ©.

This sounds more complex than it is in code. Listing 11.5 is a pseudoimple-mentation of such an interceptor:

Listing 11.5 An interceptor implements the session-per-conversation strategy

An interceptor implements the session-per-conversation strategy

The invoke(Method) interceptor wraps around the execute() operation of the controller. This interception code runs every time a request from the application user has to be processed. When it returns, you check whether the return value contains a special token or marker. This token signals that this was the last event that has to be processed in a particular conversation. You now flush the Session, commit all changes, and close the Session. If this wasn’t the last event of the conversation, you commit the database transaction, store the disconnected Session, and continue to wait for the next event in the conversation.

This interceptor is transparent for any client code that calls execute(). It’s also transparent to any code that runs inside execute () : Any data access operation uses the current Session; concerns are separated properly. We don’t even have to show you the data-access code, because it’s free from any database transaction demarcation or Session handling. Just load and store objects with getCur-rentSession().

The following questions are probably on your mind:

■ Where is the disconnectedSession stored while the application waits for the user to send the next request in a conversation? It can be stored in the HttpSession or even in a stateful EJB. If you don’t use EJBs, this responsibility is delegated to your application code. If you use EJB 3.0 and JPA, you can bind the scope of the persistence context, the equivalent of a Session, to a stateful EJB— another advantage of the simplified programming model.

■ Where does the special token that marks the end of the conversation come from ? In our abstract example, this token is present in the return value of the exe-cute() method. There are many ways to implement such a special signal to the interceptor, as long as you find a way to transport it there. Putting it in the result of the event processing is a pragmatic solution.

This completes our discussion of persistence-context propagation and conversation implementation with Hibernate. We shortened and simplified quite a few examples in the past sections to make it easier for you to understand the concepts. If you want to go ahead and implement more sophisticated units of work with Hibernate.

On the other hand, if you aren’t using Hibernate APIs but want to work with Java Persistence and EJB 3.0 components, read on.

Conversations with JPA

We now look at persistence context propagation and conversation implementation with JPA and EJB 3.0. Just as with native Hibernate, you must consider three points when you want to implement conversations with Java Persistence:

■ You want to propagate the persistence context so that one persistence context is used for all data access in a particular request. In Hibernate, this functionality is built in with the getCurrentSession() feature. JPA doesn’t have this feature if it’s deployed stand-alone in Java SE. On the other hand, thanks to the EJB 3.0 programming model and the well-defined scope and lifecycle of transactions and managed components, JPA in combination with EJBs is much more powerful than native Hibernate.

■ If you decide to use a detached objects approach as your conversation implementation strategy, you need to make changes to detached objects persistent. Hibernate offers reattachment and merging; JPA only supports merging. We discussed the differences in the previous topic in detail, but we want to revisit it briefly with more realistic conversation examples.

■ If you decide to use the session-per-conversation approach as your conversation implementation strategy, you need to extend the persistence context to span a whole conversation. We look at the JPA persistence context scopes and explore how you can implement extended persistence contexts with JPA in Java SE and with EJB components.

Note that we again have to deal with JPA in two different environments: in plain Java SE and with EJBs in a Java EE environment. You may be more interested in one or the other when you read this section. We previously approached the subject of conversations with Hibernate by first talking about context propagation and then discussing long conversations. With JPA and EJB 3.0, we’ll explore both at the same time, but in separate sections for Java SE and Java EE.

We first implement conversations with JPA in a Java SE application without any managed components or container. We’re often going to refer to the differences between native Hibernate conversations, so make sure you understood the previous sections of this topic. Let’s discuss the three issues we identified earlier: persistence context propagation, merging of detached instances, and extended persistence contexts.

Persistence context propagation in Java SE

Consider again the controller from listing 11.1. This code relies on DAOs that execute the persistence operations. Here is again the implementation of such a data access object with Hibernate APIs:

tmp9D143_thumb

If you try to refactor this with JPA, your only choice seems to be this:

tmp9D-144_thumb

No persistence-context propagation is defined in JPA, if the application handles the EntityManager on its own in Java SE. There is no equivalent to the getCur-rentSession() method on the Hibernate SessionFactory.

The only way to get an EntityManager in Java SE is through instantiation with the createEntityManager() method on the factory. In other words, all your data access methods use their own EntityManager instance—this is the session-per-operation antipattern we identified earlier! Worse, there is no sensible location for transaction demarcation that spans several data access operations.

There are three possible solutions for this issue:

■ You can instantiate an EntityManager for the whole DAO when the DAO is created. This doesn’t get you the persistence-context-per-request scope, but it’s slightly better than one persistence context per operation. However, transaction demarcation is still an issue with this strategy; all DAO operations on all DAOs still can’t be grouped as one atomic and isolated unit of work.

■ You can instantiate a single EntityManager in your controller and pass it into all DAOs when you create the DAOs (constructor injection). This solves the problem. The code that handles an EntityManager can be paired with transaction demarcation code in a single location, the controller.

■ You can instantiate a single EntityManager in an interceptor and bind it to a ThreadLocal variable in a helper class. The DAOs retrieve the current EntityManager from the ThreadLocal. This strategy simulates the getCur-rentSession() functionality in Hibernate. The interceptor can also include transaction demarcation, and you can wrap the interceptor around your controller methods. Instead of writing this infrastructure yourself, consider EJBs first.

We leave it to you which strategy you prefer for persistence-context propagation in Java SE. Our recommendation is to consider Java EE components, EJBs, and the powerful context propagation that is then available to you. You can easily deploy a lightweight EJB container with your application “Introducing EJB components.”

Let’s move on to the second item on the list: the modification of detached instances in long conversations.

Merging detached objects in conversations

We already elaborated on the detached object concept and how you can reattach modified instances to a new persistence context or, alternatively, merge them into the new persistence context. Because JPA offers persistence operations only for merging, review the examples and notes about merging with native Hibernate code.

Here we want to focus on a question we brought up earlier and look at it from a slightly different perspective. The question is, “Why is a persistent instance returned from the merge() operation?”

The long conversation you previously implemented with Hibernate has two steps, two events. In the first event, an auction item is retrieved for display. In the

second event, the (probably modified) item is reattached to a new persistence context and the auction is closed.

Listing 11.6 shows the same controller, which can serve both events, with JPA and merging:

Listing 11.6 A controller that uses JPA to merge a detached object

Listing 11.6 A controller that uses JPA to merge a detached object

There should be no code here that surprises you—you’ve seen all these operations many times. Consider the client that calls this controller, which is usually some kind of presentation code. First, the getAuction() method is called to retrieve an Item instance for display. Some time later, the second event is triggered, and the endAuction() method is called. The detached Item instance is passed into this method; however, the method also returns an Item instance. The returned Item, mergedItem, is a different instance! The client now has two Item objects: the old one and the new one.

As we pointed out in “Merging the state of a detached object” in section 9.3.2, the reference to the old instance should be considered obsolete by the client: It doesn’t represent the latest state. Only the mergedItem is a reference to the up-to-date state. With merging instead of reattachment, it becomes the client’s responsibility to discard obsolete references to stale objects. This usually isn’t an issue, if you consider the following client code:

tmp9D146_thumb

The last line of code sets the merged result as the item variable value, so you effectively update this variable with a new reference. Keep in mind that this line updates only this variable. Any other code in the presentation layer that still has a reference to the old instance must also refresh variables—be careful. This effectively means that your presentation code has to be aware of the differences between reattachment and merge strategies.

We’ve observed that applications that have been constructed with an extended persistence context strategy are often easier to understand than applications that rely heavily on detached objects.

Extending the persistence context in Java SE

We already discussed the scope of a persistence context with JPA in Java SE. “Transactions with Java Persistence.” Now we elaborate on these basics and focus on examples that show an extended persistence context with a conversation implementation.

The default persistence context scope

In JPA without EJBs, the persistence context is bound to the lifecycle and scope of an EntityManager instance. To reuse the same persistence context for all events in a conversation, you only have to reuse the same EntityManager to process all events.

An unsophisticated approach delegates this responsibility to the client of the conversation controller:

tmp9D-147_thumb

The controller expects that the persistence context for the whole conversation is set in its constructor. The client now creates and closes the EntityManager:

tmp9D-148_thumb

Naturally, an interceptor that wraps the getAuction() and endAuction() methods and supplies the correct EntityManager instance can be more convenient. It also avoids the concern leaking upward to the presentation layer. You’d get this interceptor for free if you wrote your controller as a stateful EJB session bean.

When you try to apply this strategy with an extended persistence context that spans the whole conversation, you’ll probably run into an issue that can break atomicity of the conversation—automatic flushing.

Preventing automatic flushing

Consider the following conversation, which adds an event as an intermediate step:

tmp9D-149_thumb

From looking at this new conversation client code, when do you think the updated item description is saved in the database? It depends on the flushing of the persistence context. You know that the default FlushMode in JPA is AUTO, which enables synchronization before a query is executed, and when a transaction is committed. The atomicity of the conversation depends on the implementation of the sellerHasEnoughMoney() method and whether it executes a query or commits a transaction.

Let’s assume you wrap the operations that execute inside that method with a regular transaction block:

tmp9D-150_thumbtmp9D-151_thumb

The code snippet even includes two calls that trigger the flushing of the Entity-Manager’s persistence context. First, FlushMode.AUTO means that the execution of the query triggers a flush. Second, the transaction commit triggers another flush. This obviously isn’t what you want—you want to make the whole conversation atomic and prevent any flushing before the last event is completed.

Hibernate offers org.hibernate.FlushMode.MANUAL, which decouples transaction demarcation from the synchronization. Unfortunately, due to disagreements among the members of the JSR-220 expert group, javax.persis-tence.FlushMode only offers AUTO and COMMIT. Before we show you the “official” solution, here is how you can get FlushMode.MANUAL by falling back to a Hibernate API:

tmp9D-152_thumb

Don’t forget that em.flush() must be called manually, in the last transaction in the third event—otherwise no modifications are made persistent:

tmp9D153_thumb

The official architectural solution relies on nontransactional behavior. Instead of a simple FlushMode setting, you need to code your data-access operations without transaction boundaries. One of the reasons given by expert group members about the missing FlushMode is that “a transaction commit should make all modifications permanent.” So, you can only disable flushing for the second step in the conversation by removing transaction demarcation:

tmp9D154_thumb

This code doesn’t trigger a flush of the persistence context, because the Entity-Manager is used outside of any transaction boundaries. The EntityManager that executes this query is now working in autocommit mode, with all the interesting consequences we covered earlier in section 10.3, “Nontransactional data access.” Even worse, you lose the ability to have repeatable reads: If the same query is executed twice, the two queries each execute on their own database connection in autocommit mode. They can return different results, so the database transaction isolation levels repeatable read and serializable have no effect. In other words, with the official solution, you can’t get repeatable-read database transaction isolation and at the same time disable automatic flushing. The persistence-context cache can provide repeatable read only for entity queries, not for scalar queries.

We highly recommend that you consider Hibernate’s FlushMode.MANUAL setting if you implement conversations with JPA. We also expect that this problem will be fixed in a future release of the specification; (almost) all JPA vendors already include a proprietary flush mode setting with the same effect as org.hibernate.FlushMode.MANUAL.

You now know how to write JPA conversations with detached entity instances and extended persistence contexts. We laid the foundation in the previous sections for the next step: the implementation of conversations with JPA and EJBs. If you now have the impression that JPA is more cumbersome than Hibernate, we think you may be surprised at how easy conversations are to implement once you introduce EJBs.

Conversations with EJB 3.0

We have to go through our list again: persistence context propagation, handling of detached objects, and extended persistence contexts that span the whole conversation. This time, you’ll add EJBs to the mix.

We don’t have much more to say about detached entity instances and how you can merge modifications between persistence contexts in a conversation—the concept and the API to use are exactly the same in Java SE and with EJBs.

On the other hand, persistence-context propagation and extended persistence-context management with JPA become much easier when you introduce EJBs and then rely on the standardized context propagation rules and the integration of JPA with the EJB 3.0 programming model.

Let’s first focus on the persistence-context propagation in EJB invocations.

Context propagation with EJBs

JPA and EJB 3.0 define how the persistence context is handled in an application and the rules that apply if several classes (or EJB components) use an EntityManager. The most common case in an application with EJBs is a container-managed and injected EntityManager. You turn the ItemDAO class into a managed stateless EJB component with an annotation, and rewrite the code to use EntityManager:

tmp9D155_thumbtmp9D156_thumb

The EJB container injects an EntityManager when a client of this bean calls get-MaxBid(). The persistence context for that EntityManager is the current persistence context (more about this soon). If no transaction is in progress when getMaxBid() is called, a new transaction is started and committed when getMax-Bid() returns.

NOTE Many developers didn’t use EJB session beans for DAO classes with EJB 2.1. In EJB 3.0, all components are plain Java objects and there is no reason you shouldn’t get the container’s services with a few simple annotations (or an XML deployment descriptor, if you don’t like annotations).

Wiring EJB components

Now that ItemDAO is an EJB component (don’t forget to also refactor PaymentDAO if you follow the examples from earlier conversation implementations with Hibernate), you can wire it into the also refactored ManageAuction component through dependency injection and wrap the whole operation in a single transaction:

tmp9D157_thumbtmp9D158_thumb

The EJB container injects the desired components based on your declaration of fields with @EJB—the interface names ItemDAO and PaymentDAO are enough information for the container to find the required components.

Let’s focus on the transaction and persistence-context propagation rules that apply to this component assembly.

Propagation rules

First, a system transaction is required and is started if a client calls ManageAuc-tion.endAuction(). The transaction is therefore committed by the container when this method returns. This is the scope of the system transaction. Any other stateless component methods that are called and that either require or support transactions (like the DAO methods) inherit the same transaction context. If you use an EntityManager in any of these stateless components, the persistence context you’re working with is automatically the same, scoped to the system transaction. Note that this isn’t the case if you use JPA in a Java SE application: The EntityManager instance defines the scope of the persistence context (we elaborated on this earlier).

When ItemDAO and PaymentDAO, both stateless components, are invoked inside the system transaction, both inherit the persistence context scoped to the transaction. The container injects an EntityManager instance into itemDAO and pay-mentDAO with the current persistence context behind the scenes.

(Internally, if a client obtains a ManageAuction controller, the container grabs an idle ManageAuctionBean instance from its pool of stateless beans, injects an idle stateless ItemDAOBean and PaymentDAOBean, sets the persistence context on all the components that need it, and returns the ManageAuction bean handle to the client for invocation. This is of course somewhat simplified.)

These are the formalized rules for persistence-context scoping and propagation:

■ If a container-provided (through injection or obtained through lookup) EntityManager is invoked for the first time, a persistence context begins. By default, it’s transaction-scoped and closes when the system transaction is committed or rolled back. It’s automatically flushed when the transaction is committed.

■ If a container-provided (through injection or obtained through lookup) EntityManager is invoked for the first time, a persistence context begins. If no system transaction is active at that time, the persistence context is short and serves only the single method call. Any SQL triggered by any such method call executes on a database connection in autocommit mode. All entity instances that are (possibly) retrieved in that EntityManager call become detached immediately.

■ If a stateless component (such as ItemDAO) is invoked, and the caller has an active transaction and the transaction is propagated into the called component (because ItemDAO methods require or support transactions), any persistence context bound to the JTA transaction is propagated with the transaction.

■ If a stateless component (such as ItemDAO) is invoked, and the caller doesn’t have an active transaction (for example, ManageAuction.endAuc-tion() doesn’t start a transaction), or the transaction isn’t propagated into the called component (because ItemDAO methods don’t require or support a transaction), a new persistence context is created when the EntityMan-ager is called inside the stateless component. In other words, no propagation of a persistence context occurs if no transaction is propagated.

These rules look complex if you read only the formal definition; however, in practice they translate into a natural behavior. The persistence context is automatically scoped and bound to the JTA system transaction, if there is one—you only have to learn the rules for transaction propagation to know how the persistence context is propagated. If there is no JTA system transaction, the persistence context serves a single EntityManager call.

You used TransactionAttributeType.REQUIRED in almost all the examples so far. This is the most common attribute applied in transaction assemblies; after all, EJB is a programming model for transactional processing. Only once did we show TransactionAttributeType.NOT_SUPPORTED.

Also remember that you need nontransactional data access in JPA, to disable automatic flushing of the persistence context in a long conversation—the problem of the missing FlushMode.MANUAL again.

We now take a closer look at the transaction attribute types and how you can implement a conversation with EJBs and manual flushing of an extended persistence context.

Extended persistence contexts with EJBs

In the previous section, you only worked with persistence contexts that were scoped to the JTA system transaction. The container injected an EntityMan-ager automatically, and it transparently handled the persistence context flushing and closing.

If you want to implement a conversation with EJBs and an extended persistence context, you have two choices:

■ You can write a stateful session bean as a conversation controller. The persistence context can be automatically scoped to the lifecycle of the stateful bean, which is a convenient approach. The persistence context is closed automatically when the stateful EJB is removed.

■ You can create an EntityManager yourself with the EntityManagerFactory. The persistence context of this EntityManager is application-managed— you must flush and close it manually. You also have to use the joinTransac-tion() operation to notify the EntityManager if you call it inside JTA transaction boundaries. You’ll almost always prefer the first strategy with stateful session beans.

You implement the same conversation as before in Java SE: three steps that must be completed as one atomic unit of work: Retrieval of an auction item for display and modification, a liquidity check of the seller account, and finally the closing of the auction.

You again have to decide how you want to disable automatic flushing of the extended persistence context during the conversation, to preserve atomicity. You can choose between the Hibernate vendor extension with FlushMode.MANUAL and the official approach with nontransactional operations.

Disabling flushing with a Hibernate extension

Let’s first write a stateful EJB, the conversation controller, with the easier Hibernate extension:

tmp9D159_thumbtmp9D160_thumb

This bean implements the three methods of the ManageAuction interface (we don’t have to show you this interface). First, it’s a stateful EJB; the container creates and reserves an instance for a particular client. When a client obtains a handle to this EJB for the first time, a new instance is created and a new extended persistence context is injected by the container. The persistence context is now bound to the lifecycle of the EJB instance and is closed when the method marked as @Remove returns. Notice how you can read the methods of the EJB like a story of your conversation, one step after another. You can annotate several methods with @Remove; for example, you can add a cancel() method to undo all conversation steps. This is a strong and convenient programming model for conversations, all built-in for free with EJB 3.0.

Next is the problem of automatic flushing. All methods of the ManageAuction-Bean require a transaction; you declare this on the class level. The sellerHasE-noughMoney() method, step two in the conversation, flushes the persistence context before executing the query and again when the transaction of that method returns. To prevent that, you declare that the injected persistence context should be in FlushMode.MANUAL, a Hibernate extension. It’s now your responsibility to flush the persistence context whenever you want to write the queued SQL DML to the database—you do this only once at the end of the conversation.

Your transaction assembly is now decoupled from the flush behavior of the persistence engine.

Disabling flushing by disabling transactions

The official solution, according to the EJB 3.0 specification, mixes these two concerns. You prevent automatic flushing by making all steps of the conversation (except the last one) nontransactional:

tmp9D-161_thumb

In this implementation, you switch to a different default for all methods, Trans-actionAttributeType.NOT_SUPPORTED, and require a transaction only for the endAuction() method. This last transaction also flushes the persistence context at commit time.

All methods that now call the EntityManager without transactions are effectively running in autocommit mode, which we discussed in the previous topic.

Complex transaction assemblies

You’ve now used a few different TransactionAttributeType annotations; see the complete list of available options in Table 11.1.

The most commonly used transaction attribute type is REQUIRED, which is the default for all stateless and stateful EJB methods. To disable automatic flushing of the extended persistence context for a method in a stateful session bean, switch to NOT_SUPPORTED or even NEVER.

Table 11.1 EJB 3.0 declarative transaction attribute types


Attribute name

Description

REQUIRED

A method must be invoked with a transaction context. If the client doesn’t have a transaction context, the container starts a transaction and enlists all resources (datasources, and so on) used with that transaction. If this method calls other transactional components, the transaction is propagated. The container commits the transaction when the method returns, before the result is send to the client.

NOT_SUPPORTED

If a method is invoked within the transaction context propagated from the client, the caller’s transaction is suspended and reactivated when the method returns. If the caller has no transaction context, no transaction is started for the method. All used resources aren’t enlisted with a transaction (autocom-mit occurs).

SUPPORTS

If a method is invoked within the transaction context propagated from the client, it joins that transaction context with the same result as REQUIRED. If the caller has no transaction context, no transaction is started, with the same result as NOT_SUPPORTED. This transaction attribute type should be used only for methods that can handle both cases correctly.

REQUIRES_NEW

A method is always executed inside a new transaction context, with the same consequences and behavior as with REQUIRED. Any propagated client transaction is suspended and resumes when the method returns and the new transaction is completed.

MANDATORY

A method must be called with an active transaction context. It then joins this transaction context and propagates it further, if needed. If no transaction context is present at call time, an exception is thrown.

NEVER

This is the opposite of MANDATORY. An exception is thrown if a method is called with an active transaction context.

You have to be aware of the transaction- and persistence-context propagation rules when you design your conversations with stateful EJBs, or if you want to mix stateless and stateful components:

■ If a stateful session bean that has an extended persistence context calls (effectively instantiates) another stateful session bean that also has a persistence context, the second stateful session bean inherits the persistence context from the caller. The lifecycle of the persistence context is bound to the first stateful session bean; it’s closed when both session beans have been removed. This behavior is recursive if more stateful session beans are involved. This behavior is also independent of any transaction rules and transaction propagation.

■ If an EntityManager is used in a method of stateful session bean that has a bound extended persistence context, and this method requires/supports the client’s JTA transaction, then an exception is raised if the caller of the method also propagates a different persistence context with its transaction. (This is a rare design issue.)

One gotcha is hidden in these rules: Imagine that the stateful ManageAuction controller doesn’t call the EntityManager directly, but that it delegates to other components (data access objects, for example). It still has and is responsible for the extended persistence context, although the EntityManager is never used directly. This persistence context has to be propagated to all other components that are called: i.e., into ItemDAO and PaymentDAO.

If you implement your DAOs as stateless session beans, as you did before, they won’t inherit the extended persistence context if they are called from a nontrans-actional method in a stateful controller. This is the stateful controller again, calling a DAO:

tmp9D162_thumb

The sellerHashEnoughMoney() method doesn’t start a transaction, to avoid automatic flushing of the persistence context on commit in the middle of the conversation. The problem is the call to the DAO, which is a stateless EJB. To get the persistence context propagated into the stateless EJB call, you need to propagate a transaction context. If paymentDAO.checkLiquidity() uses an EntityManager, it gets new persistence context!

The second problem lies in the PaymentDAO stateless session bean:

tmp9D163_thumbtmp9D164_thumb

Because no persistence context is propagated into the checkLiquidity() method when it’s called, a new persistence context is created to server this single operation. This is the session-per-operation antipattern! Worse, you now have two (or more) persistence contexts in one request and in the conversation, and you’ll run into data aliasing problems (no identity scope guarantee).

If you implement your DAOs as stateful session beans, they inherit the persistence context from the calling stateful session bean controller. In this case, the persistence context is propagated through instantiation, not through transaction propagation.

Write your DAOs as stateful EJBs if you write your controller as a stateful session bean. This issue is another nasty side effect of the missing FlushMode.MANUAL that can seriously impact how you design and layer applications. We recommend you rely on the Hibernate extension until the EJB 3.0 (or 3.1?) specification is fixed. With FlushMode.MANUAL, your controllers don’t have to use TransactionAt-tributeType.NOT_SUPPORTED, and the persistence context is always propagated along with your transaction (and you can mix stateless and stateful EJB calls easily).

Summary

In this topic, you implemented conversations with Hibernate, JPA, and EJB 3.0 components. You learned how to propagate the current Hibernate Session and the persistence context to create more complex layered applications without leaking concerns. You’ve also seen that persistence-context propagation is a deeply integrated feature of EJB 3.0 and that a persistence context can be easily bound to the JTA (or CMT) transaction scope. You’ve seen how FlushMode.MANUAL, a Hibernate feature, can disable flushing of your persistence context independently from your transaction assembly.

Table 11.2 shows a summary you can use to compare native Hibernate features and Java Persistence.

Table 11.2 Hibernate and JPA comparison chart

Hibernate Core

Java Persistence and EJB 3.0

Persistence context propagation is available with thread or JTA transaction binding in Java SE and Java EE. Persistence contexts are either scoped to the transaction, or managed by the application.

Java Persistence standardizes a persistence context propagation model for Java EE only, deeply integrated with EJB 3.0 components. Persistence context scoping, to transactions or to stateful session beans, is well defined.

Hibernate supports a conversation implementation with detached objects, these objects can be reattached or merged during a conversation.

Java Persistence standardizes merging of detached objects, but has no support for reattachment.

Hibernate supports disabling automatic flushing of persistence contexts for long conversations with the FlushMode.MANUAL option.

Disabling automatic flushing of an extended persistence context requires nontransactional event processing (with serious restrictions on application design and layering) or a Hibernate fallback to

FlushMode.MANUAL.

In the next topic, we’ll look at various options you should rely on whenever you need to work with more complex and larger datasets. You’ll see how transitive persistence works with Hibernate’s cascading model, how to execute batch and bulk operations efficiently, and how to hook into and manipulate the Hibernate default behavior when objects are loaded and stored.

Next post:

Previous post: