Bean-managed transactions offer the stateless session bean developer additional flexibility, but at the cost of additional complexity.
There are two alternate techniques for demarcating transaction boundaries in your bean code: use the server's javax.transaction.UserTransaction or use the PersistenceManager's javax.jdo.Transaction. If you use UserTransaction, you can begin and complete distributed transactions managed by the server's TransactionManager. If you use JDO's Transaction, you begin and complete local transactions that are managed completely by the JDO implementation, without any help (or interference) from the container.
To use UserTransaction, you obtain it via getUserTransaction( ) from the SessionContext instance, begin the transaction, and then obtain the PersistenceManager from the PersistenceManagerFactory. During getPersistenceManager( ), the PersistenceManagerFactory will automatically associate the PersistenceManager with the active UserTransaction.
When your bean invokes methods of beans that use container-managed transactions, the container automatically associates transactional resources used by the other beans in the current UserTransaction. The transactional resources can be JDO PersistenceManagers, JDBC Connections, or connector resources.
If you require nontransactional access to JDO, you must obtain the PersistenceManager when the UserTransaction is not active. After beginning a UserTransaction, if your application needs a PersistenceManager for transactional access, a different PersistenceManager must be obtained for this purpose. Your application must keep track of which PersistenceManager is being used for which purpose. Once you complete the UserTransaction by calling commit( ) or rollback( ), the PersistenceManager associated with that transaction can no longer be used.
Consider the following code fragment, in which ctx is the SessionContext instance:
UserTransaction utx = ctx.getUserTransaction( ); PersistenceManager pm1 = pmf.getPersistenceManager( ); utx.begin( ); PersistenceManager pm2 = pmf.getPersistenceManager( ); PersistenceManager pm3 = pmf.getPersistenceManager( ); utx.commit( ); PersistenceManager pm4 = pmf.getPersistenceManager( ); PersistenceManager pm5 = pmf.getPersistenceManager( ); utx.begin( ); PersistenceManager pm6 = pmf.getPersistenceManager( ); PersistenceManager pm7 = pmf.getPersistenceManager( ); utx.commit( );
In this example, pm1, pm4, and pm5 are references to unique instances of PersistenceManager, and transaction completion is managed independently by each of the associated Transaction instances. pm2 and pm3 are references to the same instance, and transaction completion is controlled by the utx instance. pm6 and pm7 are references to the same instance, and transaction completion is controlled by the utx instance.
As the bean developer, if you choose to use the same PersistenceManager for multiple serial transactions, you must demarcate transaction boundaries by using the javax.jdo.Transaction instance associated with the PersistenceManager. Obtaining a PersistenceManager without having an active UserTransaction results in your being able to manage transaction boundaries via begin( ), commit( ), and rollback( ) of javax.jdo.Transaction. In this mode, the JDO implementation does not access UserTransaction.
Your bean can invoke methods of beans that use container-managed transactions, but since the container doesn't know about JDO transactions, it cannot automatically associate transactional resources used by the other beans in the transaction.
You establish transaction boundaries using one of the techniques detailed in the previous section, but the bean's state (including the PersistenceManager) cannot be retained across business-method boundaries. Therefore, each business method must obtain a PersistenceManager and close it before it returns.
The major difference between stateful and stateless session beans with bean-managed transactions is that with stateful session beans you can save states between method invocations, including PersistenceManager, and you can even keep transactions active. However, we recommend that you do not keep transactions open between business methods.
If you use UserTransaction, the server knows that the transaction is open at the end of the business method and it will leave the bean in a state that cannot be passivated. Since the bean can't be passivated, it will continue to tie up server resources until the timeout period elapses. If the server does time out the bean, the server automatically rolls back the transaction and you lose everything in the current transaction.
If you use JDO Transaction instead, the server might not even be aware of your transaction and might passivate the bean. In this case, you have to close the PersistenceManager in ejbPassivate( ), since the PersistenceManager cannot be serialized. Again, you lose the current transaction.