12 Replies Latest reply on Nov 10, 2011 5:08 PM by Matthew Casperson

    User defined transactions with Seam POJO component - example?

    Steve Tynor Newbie

      Well, we seem to be between forums, so I'm going to risk it and repost my question to the new forum.  (original thread was here:  http://www.jboss.com/index.html?module=bb&op=viewtopic&t=129546)





      Bump...


      Can someone point me at some documentation, or an example of how to


      1) mark a Seam POJO's action method to not be transactional (is it sufficient to add @Transactional(NEVER) annotation?


      2) and then how to start and commit a manual user transaction in a way compatible with an application otherwise using Seam managed transactions?


      Thanks!

        • 1. Re: User defined transactions with Seam POJO component - example?
          Clint Popetz Apprentice

          Steve Tynor wrote on Feb 13, 2008 01:35 AM:


          Can someone point me at some documentation, or an example of how to

          1) mark a Seam POJO's action method to not be transactional (is it
          sufficient to add @Transactional(NEVER) annotation?



          Well, a Seam POJO that isn't marked @Transactional will never start a transaction upon method entry.  But if you want any method in your bean to throw an exception if entered when a transaction is active, then yes, @Transactional(NEVER) will do the trick.



          2) and then how to start and commit a manual user transaction in a way compatible with an application otherwise using Seam managed transactions?


          You can do:



          import org.jboss.seam.transaction.Transaction;
          UserTransaction tx = Transaction.instance();
          if (!tx.isActiveOrMarkedRollback())
             tx.begin();
          




          or if you're calling code you wish to be transactional, this is easier:


          import org.jboss.seam.util.Work;
          new Work() {
            protected Object work() throws Exception {
               //do something here
            }
          }.workInTransaction();
          
          

          • 2. Re: User defined transactions with Seam POJO component - example?
            Steve Tynor Newbie
            Well, a Seam POJO that isn't marked @Transactional will never start a transaction upon method entry. But if you want any method in your bean to throw an exception if entered when a transaction is active, then yes, @Transactional(NEVER) will do the trick.


            I guess I'm sloppy in my description -- it's not a Seam POJO - it's a (non-EJB) Seam Component's action method -- and it's most definitely being called inside a transaction from my JSF EL (it's behavior of committing the transaction upon return of the action method is what prompted this thread on the old forum).  How I can stop the Seam container transaction from being started automagically for just this one action method?


            Thanks for the Work() example -- that looks very clean (should I have been able to find that documented somehwere? Perhaps I'm just not looking in the right places?)


            • 3. Re: User defined transactions with Seam POJO component - example?
              Clint Popetz Apprentice

              Steve Tynor wrote on Feb 13, 2008 02:34 PM:


              Well, a Seam POJO that isn't marked @Transactional will never start a transaction upon method entry. But if you want any method in your bean to throw an exception if entered when a transaction is active, then yes, @Transactional(NEVER) will do the trick.


              I guess I'm sloppy in my description -- it's not a Seam POJO - it's a (non-EJB) Seam Component's action method


              To be clear, what I meant by Seam POJO was any java class with a @Name attribute that's not an EJB session or entity bean.  So it sounds like we're talking about a method in such a bean.



              -- and it's most definitely being called inside a transaction from my JSF EL (it's behavior of committing the transaction upon return of the action method is what prompted this thread on the old forum).  How I can stop the Seam container transaction from being started automagically for just this one action method?


              Entering/exiting a non-EJB Seam component's method won't do anything with respect to transactions unless you've marked the method or the class as @Transactional.  If it's marked @Transactional, it won't cause an existing transaction to commit.  The seam interceptor for transactional components says does this method need a transaction, and if it does it starts one and then commits it after the method returns, or rolls it back if an exception is thrown.  (It's more complicated than that, but that's the main idea.)




              Thanks for the Work() example -- that looks very clean (should I have been able to find that documented somehwere? Perhaps I'm just not looking in the right places?)


              Not as far as I know; it's in the source.  So Work() may be considered by Seam to be an internal class and thus its API or semantics may be subject to change.  But it's a nice paradigm, pretty small in code size, and if they rip it out, I'll just clone it an put it in my own tree :P

              • 4. Re: User defined transactions with Seam POJO component - example?
                Steve Tynor Newbie

                Well... Marking the action method @Transactional(NEVER) appears to be not enough to tell the Seam transaction interceptor to not create a transaction for my action method...


                @Transactional(TransactionPropagationType.NEVER)
                public void generate() throws Exception {        
                ...
                



                I get the following:


                Caused by: java.lang.IllegalStateException: Transaction active on call to NEVER method
                        at org.jboss.seam.annotations.TransactionPropagationType.isNewTransactio
                nRequired(TransactionPropagationType.java:38)
                



                How do I tell Seam to not transact my action?

                • 5. Re: User defined transactions with Seam POJO component - example?
                  Clint Popetz Apprentice

                  Steve Tynor wrote on Feb 13, 2008 04:04 PM:


                  Well... Marking the action method @Transactional(NEVER) appears to be not enough to tell the Seam transaction interceptor to not create a transaction for my action method...

                  @Transactional(TransactionPropagationType.NEVER)
                  public void generate() throws Exception {        
                  ...
                  



                  I get the following:

                  Caused by: java.lang.IllegalStateException: Transaction active on call to NEVER method
                          at org.jboss.seam.annotations.TransactionPropagationType.isNewTransactio
                  nRequired(TransactionPropagationType.java:38)
                  



                  How do I tell Seam to not transact my action?



                  Ok, I misunderstood you at first.  You want your action, which is invoked as part of the jsf lifecycle, to not have a transaction started at all.  There's no way to do that on a per-action basis (because seam starts the transaction before it could know what actions are going to be invoked...it starts the transaction as part of participating in the jsf lifecycle as a phase listener, and not directly before invoking your action method.)   But you can turn off seam-managed transactions by putting this in components.xml:



                  <core:init transaction-management-enabled="false"/>



                  Of course, there are real disadvantages to that...see the manual.


                  • 6. Re: User defined transactions with Seam POJO component - example?
                    Steve Tynor Newbie

                    Thanks Clint.  Yes - you've diagnosed my problem correctly.


                    Turning off global transactions is not an acceptable solution for my application - as you say, it has significant disadvantages and would require significant rewrite of my application.


                    But I really do need to manage my own transaction for this one case (I need to ensure that all data is committed to the database so I can invoke a BIRT report which uses that data before I return from the JSF-invoked action method).


                    Perhaps instead of preventing the Seam transaction interceptor from creating a transaction, I can just run my updates inside a nested transaction for which I can commit before the outer transaction does. Is that possible?


                    Thanks,
                    Steve



                    Clint Popetz wrote on Feb 13, 2008 04:17 PM:



                    Ok, I misunderstood you at first.  You want your action, which is invoked as part of the jsf lifecycle, to not have a transaction started at all.  There's no way to do that on a per-action basis (because seam starts the transaction before it could know what actions are going to be invoked...it starts the transaction as part of participating in the jsf lifecycle as a phase listener, and not directly before invoking your action method.)   But you can turn off seam-managed transactions by putting this in components.xml:


                    <core:init transaction-management-enabled="false"/>


                    Of course, there are real disadvantages to that...see the manual.




                    • 7. Re: User defined transactions with Seam POJO component - example?
                      Steve Tynor Newbie
                      FWIW,  I think I've solved my problem by using the following snippet to commit, then begin another transaction -- this seems to work. Can someone confirm that this is the proper and clean way to do commit a Seam managed transaction started by the transaction interceptor before returning from a JSF invoked action method?

                      `
                      Transaction.instance().commit();
                      Transaction.instance().begin();
                      `

                      <blockquote>
                      _Steve Tynor wrote on Feb 13, 2008 04:41 PM:_<br/>
                      ...
                      But I really do need to manage my own transaction for this one case (I need to ensure that all data is committed to the database so I can invoke a BIRT report which uses that data before I return from the JSF-invoked action method).
                      ...
                      <blockquote>
                      • 8. Re: User defined transactions with Seam POJO component - example?
                        Clint Popetz Apprentice

                        Steve Tynor wrote on Feb 13, 2008 04:41 PM:


                        But I really do need to manage my own transaction for this one case (I need to ensure that all data is committed to the database so I can invoke a BIRT report which uses that data before I return from the JSF-invoked action method).

                        Perhaps instead of preventing the Seam transaction interceptor from creating a transaction, I can just run my updates inside a nested transaction for which I can commit before the outer transaction does. Is that possible?



                        First, to be clear, the seam transaction interceptor isn't creating the transaction...if it was, you could just annotate your bean to not use transactions.  It's the seam JSF PhaseListener that's creating the transaction.


                        I've had no end of woes with EJB REQUIRES_NEW (nested transactions) under jboss and hibernate.  So if you go there, go with god ;)


                        Why do you need to commit before you can invoke a BIRT report...if you flush, and the data is there, any operations will see it without a commit.


                        1 of 1 people found this helpful
                        • 9. Re: User defined transactions with Seam POJO component - example?
                          Clint Popetz Apprentice

                          Steve Tynor wrote on Feb 13, 2008 06:20 PM:


                          FWIW,  I think I've solved my problem by using the following snippet to commit, then begin another transaction -- this seems to work. Can someone confirm that this is the proper and clean way to do commit a Seam managed transaction started by the transaction interceptor before returning from a JSF invoked action method?

                          Transaction.instance().commit();
                          Transaction.instance().begin();
                          





                          That will probably work, because Seam won't commit the transaction you committed when it sees it's already committed.  But IMHO that's a fragile approach...and if it were me I'd search for a different one.  Committing a transaction you didn't begin just sounds like a bad idea.


                          • 10. Re: User defined transactions with Seam POJO component - example?
                            Steve Tynor Newbie

                            Clint,


                            Thanks for clarifying where the transaction is getting started.


                            Before starting this whole thread about committing transactions, we had simply assumed that the following would work:


                            // insert some data into the database
                            entityManager.flush();
                            // run BIRT
                            



                            But in fact, BIRT does NOT see the data after that flush.  Nor can the mysql command line client.  The data is not visible outside the entityManager until after the transaction commits (when my action method returns).


                            Thanks to your earlier hints, I poked around enough to find the Transaction.instance().commit() and Transaction.instance().begin() which I've been able to use to prematurely commit the transaction and create a new one so that the PhaseListener doesnt complain:


                            // insert some data into the database
                            entityManager.flush();
                            Transaction.instance().commit();
                            Transaction.instance().begin();
                            // run BIRT
                            



                            This works, but I have no idea if this is considered right and proper.  If not, what should I do?



                            Clint Popetz wrote on Feb 13, 2008 11:45 PM:


                            First, to be clear, the seam transaction interceptor isn't creating the transaction...if it was, you could just annotate your bean to not use transactions.  It's the seam JSF PhaseListener that's creating the transaction.

                            I've had no end of woes with EJB REQUIRES_NEW (nested transactions) under jboss and hibernate.  So if you go there, go with god ;)

                            Why do you need to commit before you can invoke a BIRT report...if you flush, and the data is there, any operations will see it without a commit.



                            • 11. Re: User defined transactions with Seam POJO component - example?
                              Clint Popetz Apprentice

                              Steve Tynor wrote on Feb 14, 2008 12:02 AM:


                              Before starting this whole thread about committing transactions, we had simply assumed that the following would work:

                              // insert some data into the database
                              entityManager.flush();
                              // run BIRT
                              



                              But in fact, BIRT does NOT see the data after that flush.  Nor can the mysql command line client. 


                              I can understand the command line not seeing the data, because if you're at READ COMMITTED then it's not visible outside the transaction.  Whether BIRT sees it depends on whether BIRT is using your hibernate session, and therefore the same jdbc connection.   If BIRT is an external tool that won't let you specify the jdbc connection to use, you're stuck. 



                              The data is not visible outside the entityManager until after the transaction commits (when my action method returns).

                              Thanks to your earlier hints, I poked around enough to find the Transaction.instance().commit() and Transaction.instance().begin() which I've been able to use to prematurely commit the transaction and create a new one so that the PhaseListener doesnt complain:

                              // insert some data into the database
                              entityManager.flush();
                              Transaction.instance().commit();
                              Transaction.instance().begin();
                              // run BIRT
                              



                              This works, but I have no idea if this is considered right and proper.  If not, what should I do?



                              I think that if you can't feed your BIRT code a jdbc connection, I'd break the action up into two actions, one which commits the data, and one which runs birt, and redirect between the two.  But if that's not feasible, your existing solution isn't wrong really, just a little unclean in my opinion.  But we've all written unclean code before :P

                              • 12. Re: User defined transactions with Seam POJO component - example?
                                Matthew Casperson Newbie

                                I realise this is an old thread, but I had a similar problem. I wanted to log exceptions in the database, although because of the way Seam manages transactions in response to an exception I could never really be sure if a transaction was still in effect.


                                The solution I came up with was to persist the new entity in a thread. It does involve some overhead with the creation of a new EntityManager, but you can be assured that Seam is not managing the transactions.


                                new Thread(new Runnable()
                                          {
                                               public void run()
                                               {
                                                    TransactionManager transactionManager = null;
                                                    EntityManager entityManager = null;
                                
                                                    try
                                                    {
                                                         final InitialContext initCtx = new InitialContext();
                                                         transactionManager = (TransactionManager) initCtx.lookup("java:jboss/TransactionManager");
                                                         final EntityManagerFactory entityManagerFactory = (EntityManagerFactory) initCtx.lookup("java:jboss/EntityManagerFactory");
                                
                                                         transactionManager.begin();
                                
                                                         entityManager = entityManagerFactory.createEntityManager();
                                
                                                         final ExceptionInfo exception = new ExceptionInfo();
                                                         exception.setDescription(explaination);
                                                         exception.setDetails(result.toString());
                                                         exception.setExpected(isExpected);
                                                         entityManager.persist(exception);
                                                         entityManager.flush();
                                
                                                         transactionManager.commit();
                                                    }
                                                    catch (final Exception ex)
                                                    {
                                                         ex.printStackTrace();
                                                         
                                                         try
                                                         {
                                                              transactionManager.rollback();
                                                         }
                                                         catch(final Exception ex2)
                                                         {
                                                              ex2.printStackTrace();
                                                         }
                                                    }
                                                    finally
                                                    {
                                                         if (entityManager != null)
                                                              entityManager.close();
                                                    }
                                
                                               }
                                          }).start();