3 Replies Latest reply on Mar 24, 2009 4:16 AM by gonorrhea

    EJB3 and Seam: non-atomic transactions with loop scenario

    gonorrhea

      I finally figured out what I was doing wrong all last week.  Apparently you cannot demarcate private methods with @TransactionAttribute(...) in SFSB/SLSB classes.  Unfortunately, the container/JBoss/Eclipse, etc. does not complain about you doing this so I thought I was doing something else wrong.



      Specifying the TransactionAttribute annotation on the bean class means that it applies to all
      applicable business interface methods of the class. 
      JSR220: section 13.3.7.1

      Here is the scenario...


      functional requirement: user inputs multiple serial numbers in a HtmlInputTextarea control and submits to action method in SFSB.  There is a loop in the action method that loops thru each serial number in the List and persists x methods to up to three tables in RDBMS.  We want each iteration for each serial number to be wrapped in its own tx.  This means we are breaking the atomicity rule of the
      ACID properties of database transactions (on purpose as per the functional requirement).  i.e., if the serial numbers persist to the tables prior to a RuntimeException and the others after the exception don't exec/commit, that's fine.  So I modeled this as NOT_SUPPORTED for the public action method and then injected another SFSB with its own persist() methods that are both demarcated as REQUIRED (default).  It works fine when you use @Begin(join=true) for the action method but if when I tried @Begin(join=true, flushMode=FlushModeType.MANUAL) and flushed manually at the end of the persistX() methods, I got the following exception for the persistB() method after flush() execs:


      Caused by: javax.persistence.TransactionRequiredException: no transaction is in progress



      Why does this happen?  Can we not use MANUAL flushMode in this use case?


      Here is the code:


      action SFSB:


      @Stateful
      @Name("testTransactionsAction")
      public class TestTransactionsAction implements TestTransactionsLocal 
      {
           
           @Logger 
          private Log log;
               
          @In
          StringUtils stringUtils;
                  
          private String serialNumbers;
          
          @In
          private TestPersistLocal testPersistBean;
          
          /*--------------------------------------------------------------------------BEGIN METHODS-------------------------------------------------------------------*/
          
          @Begin(join=true)
          @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
          public void searchSerialNumbers()
           {                    
                //parse serial numbers from HtmlInputTextarea control....
                List<String> serialNumberList = parseSerialNumber();
                
                if (serialNumberList != null && serialNumberList.size() > 0)
                {
                     for (String serialNumber : serialNumberList)  //persist records one serialNumber at a time...
                     {
                          this.persistA(serialNumber);
                          this.persistB(serialNumber);
                     }
                }
           }
          
          //Specifying the TransactionAttribute annotation on the bean class means that it applies to all
          //applicable business interface methods of the class.
          // JSR220: section 13.3.7.1
          
          //@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)      
          private void persistA(String serialNumber)
          {
               testPersistBean.persistA(serialNumber);
          }
          
          //@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)      
          private void persistB(String serialNumber)
          {     
               testPersistBean.persistB(serialNumber);
          }
          
           private List<String> parseSerialNumber()
           {
                //parse serialNumber assuming that the regex to use is CR (carriage return), which will be scan gun post-fire append <ENTER>
                List<String> serialNumberList = stringUtils.splitAndTrimStringAsArray(serialNumbers, "\\r");
                          
                return serialNumberList;
           }
      
          public String getSerialNumbers()
           {
                return serialNumbers;
           }
           
           public void setSerialNumbers(String serialNumbers)
           {
                this.serialNumbers = serialNumbers;
           }
           
           @Remove @Destroy
           public void destroy() {}
      
      
      }



      Here is the support SFSB:


      @Name("testPersistBean")
      @Stateful
      @AutoCreate
      public class TestPersistBean implements TestPersistLocal {
           
           @Logger 
          private Log log;
           
          @In 
          FacesMessages facesMessages;
              
          @In 
          private EntityManager entityManager;  
         
          /*-------------------------------------------BEGIN METHODS---------------------------------------------*/
           
           public void persistA(String serialNumber) 
           {
                TestTransactions testTransactions1 = new TestTransactions();
                testTransactions1.setSerialNumber(serialNumber);
                testTransactions1.setAddedDate(new Date());
                entityManager.persist(testTransactions1);
           
                //entityManager.flush();  
           }
      
           public void persistB(String serialNumber) 
           {
                TestTransactions testTransactions2 = new TestTransactions();
                testTransactions2.setSerialNumber(serialNumber);
                testTransactions2.setAddedDate(new Date());
                entityManager.persist(testTransactions2);
           
                //entityManager.flush();  //Caused by: javax.persistence.TransactionRequiredException: no transaction is in progress
           }
           
           @Remove @Destroy
           public void destroy() {}
      
      }
      

        • 1. Re: EJB3 and Seam: non-atomic transactions with loop scenario
          swd847

          Put

          @TransactionAttribute(REQUIRES_NEW)

          on the support beans methods, and it should work.

          • 2. Re: EJB3 and Seam: non-atomic transactions with loop scenario
            swd847

            Actually no it wont. You have to use @PersistenceContext when using REQUIRES_NEW, not @In to inject the entityManager.


            Also you should make the support bean a Stateless session bean, it does not store any state so making it statefull is a waste of resources.


            • 3. Re: EJB3 and Seam: non-atomic transactions with loop scenario
              gonorrhea

              Stuart Douglas wrote on Mar 23, 2009 22:09:


              Actually no it wont. You have to use @PersistenceContext when using REQUIRES_NEW, not @In to inject the entityManager.


              I didn't know about this.  I verified your claim in the new edition of the Seam book by Yuan et al.:



              Since this approach requires method-level tx demarcation, it can only be used on EJB3 session bean components with an EJB3-managed EntityManager (i.e., an EntityManager injected via @PersistenceContext).

              --section 11.2.3



              Also you should make the support bean a Stateless session bean, it does not store any state so making it statefull is a waste of resources.

              that's true, good catch.