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

    Transactional on private method

    asookazian
      @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

          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
            asookazian

            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

            • 3. Re: Transactional on private method

              One more reason to love Spring TransactionalCallback

              • 4. Re: Transactional on private method
                swd847

                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

                  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
                    swd847

                    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

                      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
                        swd847

                        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.

                        • 9. Re: Transactional on private method
                          • 10. Re: Transactional on private method
                            swd847

                            Ahh no, i missed that part :)


                            • 11. Re: Transactional on private method
                              asookazian

                              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

                                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
                                  swd847

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

                                  • 14. Re: Transactional on private method
                                    asookazian

                                    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