Why is the CMT committed when exiting the EJB method when the transaction attribute is "Required"?

I constantly find that my existing transaction is committed inside any EJB method marked with @ejb.transaction type="Required" . Could this be right?

My expectation: EJB "requires" a transaction means: if it already exists, it will politely leave it uncommitted when it is made so that the one who called begin () can continue to use it for further operations before calling commit() or rollback() . [Of course, if there was no transaction in the first place, then the EJB method would call both begin() and commit() / rollback() .]

Is my expectation wrong or should I look for a configuration error?

It might be interesting to add that I am using Hibernate 3 inside EJB. I get a UserTransaction before calling the EJB method. The generated EJB wrapper calls ServerTransaction.commit() on exit with which Hibernate connects and takes advantage of the ability to close the session. The error I get is a boot exception in Hibernate lazy mode, because the session is closed when I try to access getters on an object with Hibernate preserved. So technically, I'm not 100% sure, regardless of whether I noticed ServerTransaction.commit() that I committed UserTransaction , I started (maybe ServerTransaction.commit() not always executed using a "real" commit? ), But if this happened no - then on what basis does Hibernate close the session?

Update: I believe my assumptions were correct, but my observations were a bit off. See below for my answer to the question.

+8
java-ee java-ee-6 ejb transactions
source share
4 answers

A closer inspection reveals a different answer than suggested above. What I actually see is that I started the UserTransaction open, but CMT created a new transaction when I entered the EJB method, despite the "Required" attribute.

I believe this is happening because I broke the rules. :) You should not access the UserTransaction API when using CMT. CMT happily ignored my UserTransaction and started its own, taking its place as an arbiter of all transaction boundaries. Since he started the transaction, he also committed it and, of course, left my UserTransaction untouched.

It seems fragile and stupid for me, perhaps a naive opinion, but it seems to be consistent with the "rules" when I read them. I do not know why CMT prefers not to play with UserTransactions at a higher level. It is possible to get developers to "do the right thing for J2EE" and create another bean layer to handle a wider transaction context. This will work because the CMT will manage the external transaction and therefore will be good with any internal transactions involved, and I believe in this case, the umbrella transaction will not be committed by internal EJBs; The CMT will wait for the completion of the external transaction, and then complete all this. It would have to be in fact.

I am not set to create more session EJBs in this already launched EJB application, but this may be the only solution not to copy CMT to a whole bunch of places that I would not touch.

+1
source share

REQUIRED may be evil

I personally do not like the REQUIRED transaction attribute and will greatly impede its use.

Lazy creation of transactions (this is what is REQUIRED) leads to the fact that he does not know when and where the transaction was actually launched and when it will be completed. This is not good. People must explicitly create transactional boundaries.

MANDATORY and UserTransaction - soul mates

Your desire to use UserTransaction very good and works with CMT - there is no difference between a JTA transaction launched through a UserTransaction provided by a container or a JTA transaction launched by a container for you.

The approach I would recommend is to switch all the REQUIRED USE to MANDATORY . WITH RESPONSIBILITY the container will not trigger transactions for you. Instead, it will protect your bean, ensuring that it cannot be called if the transaction is not running. This is an amazing and underutilized feature. MANDATORY is the best friend of anyone who wants to create a truly deterministic transactional application and enforce it. With this setting, you can fall in love with CMT.

In this scenario, you start a transaction with UserTransactions , and then the container looks like your big bodyguard, kicking people on the curb, unless they started the proper transaction before trying to call your code.

Questions

  • A servlet or BMT EJB can use UserTransaction.
  • CMT beans cannot use UserTransaction, but they can participate in a transaction launched by UserTransaction (as indicated above, a JTA transaction is a JTA transaction).
  • BMT beans cannot participate in an existing transaction. If you call the BMT bean while the transaction is already in progress, the transaction will be suspended by the container before the BMT bean is called and resumed after this method completes.
  • @Resource UserTransaction you will get user transaction through injection
  • java:comp/UserTransaction you will get user transaction through search
  • @TransactionAttribute(MANDATORY) used at the class level will affect the methods of this exact class (i.e. the fooClass.getDecaredMethods() methods). Methods of superclasses and subclasses will default to @TransactionAttribute(REQUIRED) if these classes are also not explicitly annotated @TransactionAttribute(MANDATORY)
  • If you are unable to call userTransaction.begin() and userTransaction.commit() and do the appropriate exception handling, consider @TransactionAttribute(REQUIRES_NEW) . Your transaction boundaries will continue to be documented and clear.
  • RuntimeException from the CMT bean method will cause the transaction to be marked for rollback, even if you catch and handle the exception in the calling code. Use @ApplicationException to disable this on a case by case basis for custom exception classes at runtime.

Loss of transaction context

A couple of things can lead to the fact that your transaction during the continuation, suspension or otherwise will not be transferred to the called bean.

  • BMT beans terminate the distribution of the transaction. If the current transaction calls the BMT bean, this transaction will be paused until the BMT bean is called and will resume after the bean returns. BMT beans can be a source of transactions, but cannot participate in existing transactions. If the distribution mysteriously doesn't work, make sure there are no inadvertent calls to BMT beans in the middle of the transaction.
  • Do not use anything other than the container-supplied UserTransaction or container that provides methods such as SessionContext.setRollbackOnly to manage transactions. Using a "resource-specific transaction demarcation API", such as JPA EntityTransaction or EntityTransaction java.sql.Connection.commit() , will bypass transaction management.
  • Do not start your own threads. Transactional propagation occurs separately. Java EE has standard no standard APIs that support a transaction that spans multiple threads. If you leave the thread either from starting your own thread, or using @Asynchronous, you will leave your existing transaction.
+19
source share

NClark, consider the following code that I executed on GlassFish 3.1.1. Hope this helps anyway :-)

 @Stateless @TransactionManagement(TransactionManagementType.BEAN) public class ReusingTransaction { // It BMT - we can't control Tx through context - must use... @Resource SessionContext sctx; // ... the UserTransaction instead. @Resource UserTransaction utx; // This CMT EJB will reuse BMT started transaction @EJB AnotherBean reuseTx; public void testMethod() throws Exception { // Begin Tx and check it status - compare value with: // http://java.sun.com/javaee/6/docs/api/constant-values.html#javax.transaction.Status.STATUS_ACTIVE utx.begin(); System.out.println("####testMethod#### Tx status: " + utx.getStatus()); // Our BMT started a Tx - now invoke CMT and reuse this Tx // Notice: AnotherBean has MANDATORY Tx attribute, so if no Tx would // exist, the AnotherBean couldn't be even invoked. reuseTx.testIt(); // Check if the CMT AnotherBean affected Tx we started. System.out.println("####testMethod#### Tx status: " + utx.getStatus()); // Just to prevent exceptions. utx.rollback(); } // Implicitly CMT - must reuse the Tx @Stateless @TransactionAttribute(TransactionAttributeType.MANDATORY) public static class AnotherBean { // It CMT, so Tx control is made through it context. @Resource SessionContext sctx; // Can inject it, but cannot use it - will throw an Exception. @Resource UserTransaction utx; public void testIt() throws Exception { // Give a sign that rollback must be made. sctx.setRollbackOnly(); System.out.println("####testIt#### Tx status: " + getTxStatus()); } } // Small hack to get the status of current thread JTA Tx // http://java.sun.com/javaee/6/docs/api/javax/transaction/TransactionSynchronizationRegistry.html private static int getTxStatus() throws Exception { InitialContext ctx = new InitialContext(); TransactionSynchronizationRegistry tsr = (TransactionSynchronizationRegistry) ctx.lookup("java:comp/TransactionSynchronizationRegistry"); return tsr.getTransactionStatus(); } } 

This EJB can be called, i.e. from a Singleton EJB with @Startup, to immediately see how your ASs will respond.

In Glassfish 3.1.1 you get the following result:

INFO: #### testMethod #### Tx status: 0

INFO: #### testIt #### Tx status: 1

INFO: #### testMethod #### Tx status: 1

Hooray!

+1
source share

The way CMT managed transactions work. The container automatically commits the transaction when the business method returns. If you will not use this behavior, use BMT instead of CMT.

0
source share

All Articles