1 2 Previous Next 16 Replies Latest reply on Aug 28, 2009 7:07 PM by Arbi Sookazian

    Transactional on private method

    Arbi Sookazian Master
      @SuppressWarnings("unchecked")
           @Transactional
           private void insertStatusAndErsNote(){...}



      This is a private method (i.e. not a public business method).  Can I apply the @Transactional on this method?

        • 1. Re: Transactional on private method
          Marco Röösli Newbie

          No this will not work because @Transactional is handled by the seam interceptors. This interceptors are active by accessing a public method from out side (other seam component). @Transactional will also not work if you call a method from a method in the same seam component as the method is decleared.


          What you can do is looking what the seam interceptor for @Transactional makes and try to rebuild that (doing join or create transaction manually).


          Greetz Marco

          • 2. Re: Transactional on private method
            Arbi Sookazian Master

            Ok, that's what I thought.  This is similar to EJB 3.0 CMT transactional semantics (public business interface methods are transactional candidates only, unless you call via the proxy to the method itself).


            Here's a response I got from Kenneth Saks of the EJB 3.1 EG:


            Hello,
            
            I would like to draw attention to this specific part of JSR 318, proposed final draft dated Feb 24, 2009, which is the same as in JSR 220, section 13.3.7.1:
            
            Specifying the TransactionAttribute annotation on the bean class means that it applies to all applicable business interface methods of the class.
            
            
            
            In EJB 3.1 (or later), I’m wondering if it will be possible to demarcate transaction attribute value (e.g. REQUIRED, REQUIRES_NEW, etc.) on private, protected, or package-private in a SFSB or SLSB (i.e. any method with less than public visibility).  The current behavior in EJB 3.0 seems to be that the EJB container (in my case JBoss AS) ignores the transaction demarcation annotation on a private method and there is no warning from Eclipse, or exception/warning during deployment or runtime exception.  
            
            I ran into this problem recently and it took me over *two days* to determine the root cause of the behavior in terms of transaction semantics and CMT in my SFSB.  My workaround to this problem was to refactor the private method to a public method defined in the local interface implemented by a new SFSB.  The REQUIRES_NEW transaction demarcation for the public method in the new SFSB is now “honored” by the ejb container.  
            
            Why is it necessary to do this?  We should be able to demarcate non-local interface methods or non-remote interface methods with transaction demarcation when using CMT.
            
            
            
            Hi Arbi,
            
            
            The behavior you're seeing is not specific to transaction attributes.  All the special semantics associated with an EJB component invocation (method authorization, container exception handling, threading guarantees, container-managed transactions, etc.) only apply to invocations made through an EJB reference.  When code already running within a business method invocation calls another method on the same class through the "this" pointer, the EJB container isn't involved.  Such a method invocation is just a plain Java SE method call.  
            
            
            If you want to invoke a business method on your bean from another business method on the same bean, you'll need to acquire an EJB reference to yourself.  The easiest way is to call SessionContext.getBusinessObject().  
            
            
            
            
            TestTransactionsLocal ejbRefToMyself =  sessionCtx.getBusinessObject(TestTransactionsLocal.class)
            ejbRefToMyself.otherMethod();
            
            
            Regards,
            
            
            Ken

            • 4. Re: Transactional on private method
              Stuart Douglas Master

              I think the seam equivilent would be


              private Integer myMethod()
              {
              return (new org.jboss.seam.util.Work<Integer>()
              {
                 //do some work here
              
              })
              .workInTransaction();
              }
              

              • 5. Re: Transactional on private method
                Francisco Jose Peredo Noguez Master

                Whoa! I didn't know you could do that! That could be a really nice way to solve Arbi's transactional propagation problem! (I would solve it that way)

                • 6. Re: Transactional on private method
                  Stuart Douglas Master

                  I don't think it helps with Arbi's problem,  Arbi needs to suspend a transaction. The Transactional interceptor uses Work internally, so it is not much different to just using @Transactional.

                  • 7. Re: Transactional on private method
                    Francisco Jose Peredo Noguez Master

                    Stuart Douglas wrote on Aug 27, 2009 12:59:


                    I don't think it helps with Arbi's problem,  Arbi needs to suspend a transaction. The Transactional interceptor uses Work internally, so it is not much different to just using @Transactional.


                    Now, he does not need to suspend a transactions, because what he wants to do just to have things happen in 2 different transactions, he just has to forget about @Transactional and:


                    
                    public void apply() {
                       insertSomethingInOneTransaction();
                       insertSomethingElseInOtherTransaction();
                    }
                    
                    private Integer insertSomethingInOneTransaction(){
                    {
                    return (new org.jboss.seam.util.Work<Integer>()
                    {
                       //do some work here
                    
                    })
                    .workInTransaction();
                    }
                    
                    private Integer insertSomethingElseInOtherTransaction(){
                    {
                    return (new org.jboss.seam.util.Work<Integer>()
                    {
                       //do some other work here
                    
                    })
                    .workInTransaction();
                    }
                    
                    



                    No?

                    • 8. Re: Transactional on private method
                      Stuart Douglas Master

                      No, because this has to happen inside an already running transaction:



                      Arbi Sookazian wrote on Aug 26, 2009 18:15:


                      I have a functional requirement as follows:

                      insert record into remote db via web service call

                      insert record into local db

                      Do not treat as a distribute tx (i.e. if one or the other fails if SQLServerException is thrown or network error, etc. do not rollback the other one)

                      So if there is already a tx running, then this business method is called, I need to suspend the tx, execute the method with both inserts, then continue tx.

                      I could probably create two methods instead of one, but I am considering this one logical unit of work w/o tx.

                      • 10. Re: Transactional on private method
                        Stuart Douglas Master

                        Ahh no, i missed that part :)


                        • 11. Re: Transactional on private method
                          Arbi Sookazian Master

                          Well I just tried Stuart's recommendation and got this:


                          09:58:59,442 ERROR [EquipmentProcessingViewAction] error: 
                          javax.persistence.TransactionRequiredException: no transaction is in progress



                          This line caused the above exception:


                          entityManager.flush();



                          The anonymous inner class really threw me off for a while and I'm not even sure I coded it properly but the compiler was ultimately satisfied.  I don't understand why the Work class is generified.  I'm returning an Integer value for apparently no good reason.


                          Here's a snippet of the failing method:



                          private Integer insertStatusAndErsNote(){
                                    
                                    try {
                                         return (new Work<Integer>(){
                                              protected Integer work(){                              
                                                   try{
                                                        
                                    ...                    
                                                        if (ersclList != null && ersclList.size() > 0){
                                                             EquipmentRecoveryStatusChangeLog myErscl = ersclList.get(0);
                                                             myErscl.setIsCurrent(false);               
                                                             entityManager.flush();
                                                        }
                          .........
                                                                                                
                                                   }
                                    
                                                   catch (Exception e){
                                                        log.error("error: ", e);
                                                   }
                                                   return 1;
                                              }
                                    
                                         }).workInTransaction();
                                    } 
                                    catch (Exception e) {                    
                                         log.error("error: ", e);
                                    }
                                    return 0;
                                    
                               }



                          This is getting WAAAAAAAAAAAAAY over my head for something I could have accomplished in 5 min's using EJB3.


                          Ultimately, I need a transaction for the local db operations but I don't really need one for the web service call (that's only one insert statment).


                          And honestly, I don't even understand how/why the work() method is even getting called.  I have very little experience with anonymous classes...

                          • 12. Re: Transactional on private method
                            Francisco Jose Peredo Noguez Master

                            Arbi Sookazian wrote on Aug 27, 2009 19:05:


                            Well I just tried Stuart's recommendation and got this:

                            09:58:59,442 ERROR [EquipmentProcessingViewAction] error: 
                            javax.persistence.TransactionRequiredException: no transaction is in progress



                            This line caused the above exception:

                            entityManager.flush();




                            Guess Stuart was wrong when he said that Work is a the equivalent to Spring Transactional callback...





                            The anonymous inner class really threw me off for a while and I'm not even sure I coded it properly but the compiler was ultimately satisfied.  I don't understand why the Work class is generified.  I'm returning an Integer value for apparently no good reason.


                            That is why, in Spring, there is a TransactionalCallbackWithoutResult (so that you do not always need to return stuff).



                            Here's a snippet of the failing method:

                            This is getting WAAAAAAAAAAAAAY over my head for something I could have accomplished in 5 min's using EJB3.


                            You mean you can reestructure you WAR in to an EAR in 5 min? amazing! ;-) (please also share how are you going to avoid the performance penalty you are going to get each time you start your app now). One thing is for sure, this is a very good clue on why so many people use Spring.



                            Ultimately, I need a transaction for the local db operations but I don't really need one for the web service call (that's only one insert statment).

                            And honestly, I don't even understand how/why the work() method is even getting called.  I have very little experience with anonymous classes...


                            Mmmmm, maybe your problem is that your entityManager is not joining the transaction (Stuart mentioned that you needed to call entityManager.joinTransaction() remember?) like this:



                            private Integer insertStatusAndErsNote(){
                                      
                                      try {
                                           return (new Work<Integer>(){
                                                protected Integer work(){                              
                                                     try{
                                                          
                                      ...                    
                                                          if (ersclList != null && ersclList.size() > 0){
                                                               EquipmentRecoveryStatusChangeLog myErscl = ersclList.get(0);
                                                               myErscl.setIsCurrent(false);               
                            entityManager.joinTransaction();                                   
                            entityManager.flush();
                                                          }
                            .........
                                                                                                  
                                                     }
                                      
                                                     catch (Exception e){
                                                          log.error("error: ", e);
                                                     }
                                                     return 1;
                                                }
                                      
                                           }).workInTransaction();
                                      } 
                                      catch (Exception e) {                    
                                           log.error("error: ", e);
                                      }
                                      return 0;
                                      
                                 }


                            • 13. Re: Transactional on private method
                              Stuart Douglas Master

                              Did your entityManager exist before the transactio was created? If so try doing an entityManager.joinTransaction().

                              • 14. Re: Transactional on private method
                                Arbi Sookazian Master

                                I did subsequently try

                                entityManager.joinTransaction();

                                but I got an exception similar to there is no active tx to join.  So that didn't work either.


                                If you think about it:


                                javax.persistence.TransactionRequiredException: no transaction is in progress



                                means there is no active tx to join, right?


                                The root cause is that there is no active tx...

                                1 2 Previous Next