Announcement Announcement Module
Collapse
No announcement yet.
how to make transaction / hibernate session always available Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • how to make transaction / hibernate session always available

    I have some service methods annotated with @Transactional. As long as Spring creates these service beans, they seem to participate in transactions, which is good. It would be nice to have them always participate even if they are created by something other than Spring, but I haven't figured that out.

    If the service does something simple like:

    Code:
    @Transactional 
    public Service { 
       @Transactional
       public void doSomethingPersistent(Stuff ) { 
         getDao().doStuff(); 
       }
    }
    Then it works fine.

    However, what if within the @Transactional method I call methods on other service objects or domain objects? In that case, how do I make everything participate in the same transaction (and make sure the session is bound to the thread so hibernate doesn't complain about lazy init)?

    For example:

    Code:
       @Transactional
       public void doSomethingPersistent(Stuff ) { 
          
          getSomeOtherService().setUser(user); //how to I make this also participate in the TX? 
    
         //do something on a domain object 
         getUser().addProperty(someProp); //here I am calling a method on a domain object, how to I make it participate in the TX? 
    	
         getDao().doStuff(); 
    
    
       }
    Just annotating everything with @Transactional doesn't seem to be doing the trick. If the domain objects is created within some service, with new Thing(), then how can its methods be transactional as well?

  • #2
    Everything what you call inside the method IS transactional. I suggest you read the reference guide about how transactions work.

    Comment


    • #3
      Originally posted by andrew_m View Post
      I have some service methods annotated with @Transactional. As long as Spring creates these service beans, they seem to participate in transactions, which is good. It would be nice to have them always participate even if they are created by something other than Spring, but I haven't figured that out.
      If you want that transactional annotation on objects created outside Spring (e.g. by 'new') takes effect, then you have to use "true" AOP, i.e. AspectJ
      compile-time or load-time weaving (LTW). Spring reference explain latter quite detailed.

      Regards,
      Oleksandr

      Comment


      • #4
        Originally posted by Marten Deinum View Post
        Everything what you call inside the method IS transactional. I suggest you read the reference guide about how transactions work.
        I have read that guide. Are you saying that I could do manual hibernate code anywhere inside the @Transactional method and it would be transactional?

        I was assuming that just things which use the data source and TX manager injected by spring (in my case these are DAOs) participate in the TX.

        Comment


        • #5
          Originally posted by andrew_m View Post
          I have read that guide. Are you saying that I could do manual hibernate code anywhere inside the @Transactional method and it would be transactional?

          I was assuming that just things which use the data source and TX manager injected by spring (in my case these are DAOs) participate in the TX.
          All code inside transactioal method that uses Hibernate session obtained from SessionFactory wih getCurrentSession() is transactional - Spring supports such usage of Hibernate transparently.

          Sessions obtained from SessionFactory by openSession() do not participate in transactions.

          Regards,
          Oleksandr

          Comment


          • #6
            Originally posted by al0 View Post
            All code inside transactioal method that uses Hibernate session obtained from SessionFactory wih getCurrentSession() is transactional - Spring supports such usage of Hibernate transparently.

            Sessions obtained from SessionFactory by openSession() do not participate in transactions.

            Regards,
            Oleksandr
            No sure it works for Web apps
            Last edited by Sokolov; Dec 17th, 2010, 04:21 PM.

            Comment


            • #7
              Please, provide a working example that prove your point. Or at least as full application context. My guess is that you have misconfigured your session factory.

              For me (and hundreds or thousands other developers) Spring perfectly manages sessions and transactions for many years
              If it does not start transaction on the session then how Hibernate methods that require transaction work and do not throw an exception?

              Then you say "that is gonna return a value only if ALREADY there is a session bound to the thread". This statement is absolutely wrong, especially its emphasized part, see the excerpt from Hibernate ThreadLocalSessionContext
              Code:
              	public final Session currentSession() throws HibernateException {
              		Session current = existingSession( factory );
              		if (current == null) {
              			current = buildOrObtainSession();
              If none exists then buildOrObtainSession is called.
              Code:
              	protected Session buildOrObtainSession() {
              		return factory.openSession(
              				null,
              		        isAutoFlushEnabled(),
              		        isAutoCloseEnabled(),
              		        getConnectionReleaseMode()
              			);
              	}
              Another your statement, "getCurrentSession() has nothing to do with Spring" is as well false. Hibernate (3.1 and up) provides for pluggable implementations of the CurrentSessionContext interface (ThreadLocalSessionContext is one of them, supplied with Hibernate), and Spring provides its own,SpringSessionContext
              [/CODE]
              /**
              * Implementation of Hibernate 3.1's CurrentSessionContext interface
              * that delegates to Spring's SessionFactoryUtils for providing a
              * Spring-managed current Session.
              *
              * <p>Used by Spring's {@link LocalSessionFactoryBean} when told to expose a
              * transaction-aware SessionFactory. This is the default as of Spring 2.5.
              *
              * <p>This CurrentSessionContext implementation can also be specified in custom
              * SessionFactory setup through the "hibernate.current_session_context_class"
              * property, with the fully qualified name of this class as value.
              *
              * @author Juergen Hoeller
              * @since 2.0
              * @see SessionFactoryUtils#doGetSession
              * @see LocalSessionFactoryBean#setExposeTransactionAwareS essionFactory
              */
              public class SpringSessionContext implements CurrentSessionContext {
              [/CODE]

              Originally posted by Sokolov View Post
              No it does not.
              getCurrentSession() has nothing to do with Spring. It is a pure hibernate session factory method that is gonna return a value only if ALREADY there is a session bound to the thread (work for Hibernate 3 only). It is non-transactional session. At least Spring Transaction Manager will not set value of it even getCurrentSession() called within @Transactional method. First of all it does not work because Spring must bound session to the transaction not to the thread as for pure Hibernate3 setup. To make things worse Spring will not bound session even to the transaction. Why ? It is a bug probably.

              Overall Spring Transaction Management is junk that just does not work. If you advice methods with @Transactional, please keep in mind that Spring WILL NOT create a new session for you and bound it to the transaction.

              If you call HibernateTemplate.getSession(protected method) or HibernateDAOSupport.getSession() you will ALWAYS GET a NEW session with a different hash code. This session NEVER gonna get closed.

              Spring just could not manage sessions and manage transactions. It is claimed that it can, but it could not.

              Just run the following test.

              Create a new class and inherit it from HibernateDAOSupport,
              Advice a method of this class with @Transactional add all configuration tx-annotationdriven etc in Spring confit.
              Then within advised method do the following
              System.out.println(getSession().hashCode());
              System.out.println(getSession().hashCode());

              You will get different values. Even Spring must create a new session and connect this session to the transaction, it will not do this.
              Last edited by al0; Dec 17th, 2010, 05:23 PM. Reason: Some info added

              Comment


              • #8
                Ok. This is example

                This is a web app. Transaction originated inside a Struts action.

                Config
                Code:
                <bean id="dataSource"
                    class="org.springframework.jndi.JndiObjectFactoryBean" >
                     <property name="jndiName">
                              <value>java:comp/env/jdbc/xxx</value>
                      </property>
                </bean>
                
                 <bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
                        <property name="properties">
                          <props>
                              <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                              <prop key="connection.driver_class">com.mysql.jdbc.Driver</prop>
                          </props>
                        </property>
                    </bean>
                
                
                
                    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
                        <property name="hibernateProperties">
                          <ref local="hibernateProperties"/>
                        </property>
                        <property name="dataSource" ref="dataSource"/>
                        
                        <property name="mappingResources">
                             <list>
                                <value>com/trade4stas/dbaccess/hbm/P_account_transaction.hbm.xml</value>
                ...
                </bean>
                
                 <tx:annotation-driven transaction-manager="appTransactionManager"/>
                
                    <bean id="appTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
                      <property name="sessionFactory" ref="sessionFactory"/>
                    </bean>
                 
                    <!-- DAO wrappers. Template hibernate DAO -->
                    <bean id="hibernateDBAccess" class="dbaccess.HibernateDBAccess" abstract="true">
                        <property name="sessionFactory" ref="sessionFactory"/>
                    </bean>
                Basic DAO
                Code:
                public class HibernateDBAccess extends HibernateDaoSupport implements Serializable
                {
                
                    private static Log log = LogFactory.getLog(HibernateDBAccess.class);
                    
                    public Session getMySession()
                    {
                        final Session s= getSession();
                        log.debug("!!!! Thread hash code ["+Thread.currentThread().hashCode()+"] Session hash code ["+s.hashCode()+"].");
                        return s;
                    }
                This is a place where transactions are originated. One program can call another program but this method is executed only once per request. There is no chance that during request this particular method is called more than once. No more transaction demarcations within the project. This is the only place where transaction can be originated.
                Code:
                  @Transactional
                    private void runAsTransaction(final AbstractProgram program, final Domain domain)
                    throws LocalException
                    {
                        program.exec(domain);
                    }
                This is log output. The same Thread. Different session
                Code:
                .dbaccess.HibernateDBAccess                                        !!!! Thread hash code [6115524] Session hash code [20656006].
                .dbaccess.HibernateDBAccess                                        !!!! Thread hash code [6115524] Session hash code [32805286].
                .dbaccess.HibernateDBAccess                                        !!!! Thread hash code [6115524] Session hash code [10513800].
                .dbaccess.HibernateDBAccess                                        !!!! Thread hash code [6115524] Session hash code [13772551].
                .program.stocks.SelectStocksProgram                                Entry: exec()
                .program.account.GetAccountProgram                                 Entry: exec()
                .dbaccess.HibernateDBAccess                                        !!!! Thread hash code [6115524] Session hash code [33321371].
                .program.user_registration.GetBrokerageHouseProgram                Entry: exec()
                .dbaccess.HibernateDBAccess                                        !!!! Thread hash code [6115524] Session hash code [4536570].
                .program.user_registration.GetBrokerageHouseProgram                Exit: exec()
                .program.user_registration.GetBrokerageHousesProgram               Entry: exec()
                .dbaccess.HibernateDBAccess                                        !!!! Thread hash code [6115524] Session hash code [23854573].
                .program.user_registration.GetBrokerageHousesProgram               Exit: exec()
                .program.account.GetAccountProgram                                 Exit: exec()
                .program.user_registration.GetRegistrationProgram                  Entry: exec()
                .dbaccess.HibernateDBAccess                                        !!!! Thread hash code [6115524] Session hash code [28948141].
                .dbaccess.HibernateDBAccess                                        !!!! Thread hash code [6115524] Session hash code [26800268].
                .program.user_registration.GetRegistrationProgram                  Exit: exec()
                .program.stocks.GetPortfolioProgram                                Entry: exec()
                .dbaccess.HibernateDBAccess                                        !!!! Thread hash code [6115524] Session hash code [4433196].
                .dbaccess.HibernateDBAccess                                        !!!! Thread hash code [6115524] Session hash code [15049905].
                .program.stocks.GetPortfolioProgram                                Entry: loadPortfolio(p_brokerage_account=97)
                .dbaccess.HibernateDBAccess                                        !!!! Thread hash code [6115524] Session hash code [18382688].
                .program.stocks.GetPortfolioProgram                                Entry: loadPortfolio()
                .program.stocks.GetPortfolioProgram                                Exit: loadPortfolio()
                .program.stocks.GetPortfolioProgram                                Exit: loadPortfolio(p_brokerage_account=97)
                .program.stocks.GetPortfolioProgram                                Exit: exec()
                .program.systemproperties.GetMarketClosedProgram                   entry exec()
                .program.systemproperties.GetMarketClosedProgram                   Market closed day  = false
                .program.systemproperties.GetMarketClosedProgram                   Market closed time = false
                .program.systemproperties.GetMarketClosedProgram                   exit exec()
                .program.user_registration.CommodityPricesProgram                  Entry: exec()
                .program.user_registration.CommodityPricesProgram                  Exit: exec()
                Last edited by Sokolov; Dec 17th, 2010, 08:06 PM.

                Comment


                • #9
                  It is a classical case - you annotated private method, but Spring AOP is proxy-based, so private methods are not covered by it. See Chapter 7.6 of Spring 3 Reference (http://static.springsource.org/sprin...l#aop-proxying) or Chapter 6.6 of Spring 2.5 Reference (http://static.springsource.org/sprin...l#aop-proxying).

                  Comment


                  • #10
                    This is something that I already figured out

                    But "annotating public method" is too expensive. All references have to be changed to interfaces because class reference will not work once AOP proxy is created around object.

                    Overall this is a junk. I can with lesser effort to use connection to begin transaction and commit/rollback. It will require less work than to create an interface for each advised class. Spring transactions do not worth time spending on reading about it.

                    The worst junk is HibernateTemplate.... Who in sane brain can design class to be a wrapper of HibernateSession but completely change signature of all API it provides. Sick person may be.

                    Advice is to stick with hibernate. Ignore all Spring ORM code whatsever. Create transaction by Hibernate means, commit, rollback close. Using Spring is more work.

                    Comment


                    • #11
                      Originally posted by Sokolov View Post
                      But "annotating public method" is too expensive. All references have to be changed to interfaces because class reference will not work once AOP proxy is created around object.
                      Not exactly - only Spring uses standard JDK proxies (it is a default behavior). But it is possible to instruct Spring to use CGLIB class proxies, then All non-private methods will be proxied - are they interface methods or not. The corresponding setting is proxy-target-class="true", see the documentation for details (the chapter 7.6, to which I referred you already).
                      Another alternative is to use AspectJ load-time weaving based AOP instead of proxy-based AOP.

                      Overall this is a junk. I can with lesser effort to use connection to begin transaction and commit/rollback. It will require less work than to create an interface for each advised class.
                      Another wrong point - in most cases programming to interfaces is very desirable (regardless of the Spring usage) as it significantly reduce interdependencies in your code and so make it much more maintainable.
                      Spring transactions do not worth time spending on reading about it.
                      It is up to you - nobody force you to use them.

                      The worst junk is HibernateTemplate.... Who in sane brain can design class to be a wrapper of HibernateSession but completely change signature of all API it provides. Sick person may be.
                      I see that you do not like reading and prefer to step on your own rakes
                      Hibernate template was created in times of Hibernate 2, when Hibernate has no concept of contextual session and when Hibernate exceptions were checked. The main goals of HibernateTemplate were to manage contextual sessions and to transparently convert those checked exceptions to unchecked DataAccess exceptions. As for now Spring documentation recommends to code all new application to the native Hibernate API, HibernateTemplate is kept for backward compatibility with older Spring versions. Spring transactions works nicely even if HibernateTemplate is not used.
                      Advice is to stick with hibernate. Ignore all Spring ORM code whatsever. Create transaction by Hibernate means, commit, rollback close. Using Spring is more work.
                      If it is OK for your application - do it. But it means interweaving of unrelated concepts, so it is rather questionable advice.

                      Comment


                      • #12
                        Re: I see that you do not like reading and prefer to step on your own rakes

                        Re:I see that you do not like reading and prefer to step on your own rakes

                        Listen, I read it long time ago when this stuff got released with Spring 2.0. Did not find it useful and forgot about it. With Spring 3.0 I decided to give it another chance. Probably it did not worth it.

                        Now I am doing everything possible to make it working the way it can, and time after time I run into frustration how primitive Spring Transaction support is.

                        I have defined all the point cuts, converted abstract classes to interfaces to let it be handled by AOP proxies and again this stuff let me down.

                        Within one thread 2 API calls are made. Both correspond to the same point cut and the same transaction advise. Even worse - they are called sequentially. And still I am getting 2 (two) transactions instead of one.

                        Code:
                        progam1.exec(domain_object);
                        progam2.exec(domain_object);
                        I could not believe my eyes. It is all the same thread. Transaction should be bound to the thread and run until exception thrown or thread comes to its end. But no. Session bound to transaction. And transaction is bound to point cut.

                        For each call a new transaction gets created. Why? Because it how AOP proxy works. It begin transaction before API call and commits when the call finishes. So primitive as it can be....

                        So now I have to create something to be a transaction wrapper. I have to do it myself. I have to wrap everything related to the same thread into one artificial interface. Like TransactionForStrutsAction1, TransactionForStrutsAction2, TransactionForStrutsAction3 etc. For each StrutsAction I have to define a transaction orchestrator. Nonsense.

                        And no, I could not set point cut around StrutsAction itself. It never throws an exception. If programX fails, StrutsAction or Spring Controller (depending of project I use both) must chose a correct forward plan. It could not throw an Exception and let TransactionManager be aware what to do next. All UI controllers must handle exceptions themselves w/out exposing them. None of UI controllers have a luxury to throw an exception. They always have to finish nicely. So internally UI controller must call a wrapper that will be surrounded by AOP that handle transaction. It is a pain because all my UI controllers are like this

                        Code:
                        try
                        {
                        
                        //IT ALL SHOULD BE THE SAME TRANSACTION
                         validator.validate(request);
                         Domain domain=mapper.mapFrom(request);
                         program1.exec(domain);
                         program2.exec(domain);
                         //..
                         programN.exec(domain);
                        //END OF TX
                         mapper.mapTo(request,domain);
                         redirectSuccess();
                        }
                        catch (MyException e)
                        {
                          redirectFailure(e);
                        }
                        And this is all templated. It is one class where mapper, validator, and all programs are defined trough Spring configuration. There is only one class that defines this Controller.
                        Last edited by Sokolov; Dec 27th, 2010, 12:06 AM.

                        Comment


                        • #13
                          So now I have to create something to be a transaction wrapper. I have to do it myself. I have to wrap everything related to the same thread into one artificial interface. Like TransactionForStrutsAction1, TransactionForStrutsAction2, TransactionForStrutsAction3 etc. For each StrutsAction I have to define a transaction orchestrator. Nonsense.
                          IMHO if you don't have something like that it means that your services are defined wrong, which basically means your design is wrong. You should have a business method which does all the things which should be done in 1 pass (1 transaction whatever) that should be your business/service method.

                          Your action/controller should be thin it should not contain business logic etc. it should, as you pointed out, orchestrate and react to results of the service call.

                          But of course that is IMHO....

                          If you don't want to use declarative tx management wrap it in a transactiontemplate (or at a last resort directly use the PlatformTransactionManager) and be gone with it.

                          Comment


                          • #14
                            Thanks for the great information codes and design thank you so much i had found this type of forum.its really useful to me.
                            Thanks!

                            Comment


                            • #15
                              If you would read a documentation really carefully, you will understand how declarative transaction management works and, probably, even why it works exactly this way. Then you will save yourself a lot of efforts and frustration.

                              It seems that your design does not fit into the declarative transaction management model (note - not Spring-specific implementation, but the DTM model as such). As I do not know your detailed needs, I would not claim that your design is wrong - but any design that cannot be suited by DTM is at least suspicious.

                              Originally posted by Sokolov View Post
                              Re:I see that you do not like reading and prefer to step on your own rakes

                              Listen, I read it long time ago when this stuff got released with Spring 2.0. Did not find it useful and forgot about it. With Spring 3.0 I decided to give it another chance. Probably it did not worth it.

                              Now I am doing everything possible to make it working the way it can, and time after time I run into frustration how primitive Spring Transaction support is.

                              I have defined all the point cuts, converted abstract classes to interfaces to let it be handled by AOP proxies and again this stuff let me down.

                              Within one thread 2 API calls are made. Both correspond to the same point cut and the same transaction advise. Even worse - they are called sequentially. And still I am getting 2 (two) transactions instead of one.

                              Code:
                              progam1.exec(domain_object);
                              progam2.exec(domain_object);
                              I could not believe my eyes. It is all the same thread. Transaction should be bound to the thread and run until exception thrown or thread comes to its end. But no. Session bound to transaction. And transaction is bound to point cut.

                              For each call a new transaction gets created. Why? Because it how AOP proxy works. It begin transaction before API call and commits when the call finishes. So primitive as it can be....

                              So now I have to create something to be a transaction wrapper. I have to do it myself. I have to wrap everything related to the same thread into one artificial interface. Like TransactionForStrutsAction1, TransactionForStrutsAction2, TransactionForStrutsAction3 etc. For each StrutsAction I have to define a transaction orchestrator. Nonsense.

                              And no, I could not set point cut around StrutsAction itself. It never throws an exception. If programX fails, StrutsAction or Spring Controller (depending of project I use both) must chose a correct forward plan. It could not throw an Exception and let TransactionManager be aware what to do next. All UI controllers must handle exceptions themselves w/out exposing them. None of UI controllers have a luxury to throw an exception. They always have to finish nicely. So internally UI controller must call a wrapper that will be surrounded by AOP that handle transaction. It is a pain because all my UI controllers are like this

                              Code:
                              try
                              {
                              
                              //IT ALL SHOULD BE THE SAME TRANSACTION
                               validator.validate(request);
                               Domain domain=mapper.mapFrom(request);
                               program1.exec(domain);
                               program2.exec(domain);
                               //..
                               programN.exec(domain);
                              //END OF TX
                               mapper.mapTo(request,domain);
                               redirectSuccess();
                              }
                              catch (MyException e)
                              {
                                redirectFailure(e);
                              }
                              And this is all templated. It is one class where mapper, validator, and all programs are defined trough Spring configuration. There is only one class that defines this Controller.

                              Comment

                              Working...
                              X