Announcement Announcement Module
Collapse
No announcement yet.
Transactions not working with Spring 1.2.2/Hibernate3 Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Transactions not working with Spring 1.2.2/Hibernate3

    I am trying to test my transactions and Spring doesn't seem to be wrapping my method in a transaction.

    Here is my applicationContext.xml:
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http&#58;//www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    
    	<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
           <property name="locations">
              <list>
                 <value>classpath&#58;/hibernate.properties</value>
              </list>
           </property>
        </bean> 
    	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    		<property name="url"><value>$&#123;hibernate.connection.url&#125;</value>
    		</property>
    		<property name="driverClassName"><value>$&#123;hibernate.connection.driver_class&#125;</value>
    		</property>
    		<property name="username"><value>$&#123;hibernate.connection.username&#125;</value>
    		</property>
    		<property name="password"><value>$&#123;hibernate.connection.password&#125;</value>
    		</property>
    	</bean>
    	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    		<property name="dataSource" ref="dataSource" />
    		<property name="mappingResources">
    			<list>
    				<value>my/path/UserTypes.hbm.xml</value>
    				<value>my/path/Airport.hbm.xml</value>
    				<value>my/path/AirSegment.hbm.xml</value>
    				<value>my/path/Carrier.hbm.xml</value>
    				<value>my/path/FlightLeg.hbm.xml</value>
    			</list>
    		</property>
    		<property name="hibernateProperties">
    			<props>
    				<prop key="hibernate.dialect">$&#123;hibernate.dialect&#125;</prop>
      				<prop key="hibernate.show_sql">$&#123;hibernate.show_sql&#125;</prop> 
    			</props>
    		</property>
    	</bean>
    
    	<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    		<property name="sessionFactory" ref="sessionFactory"/>
    	</bean>
    
    	<bean id="airportDao" class="com.ngc.dts.domain.dao.hibernate.AirportHibernateDao">
     		<property name="sessionFactory" ref="sessionFactory"/>
    	</bean>
    	<bean id="airSegmentDao" class="com.ngc.dts.domain.dao.hibernate.AirSegmentHibernateDao">
     		<property name="sessionFactory" ref="sessionFactory"/>
    	</bean>
    	<bean id="carrierDao" class="com.ngc.dts.domain.dao.hibernate.CarrierHibernateDao">
     		<property name="sessionFactory" ref="sessionFactory"/>
    	</bean>
    	<bean id="flightLegDao" class="com.ngc.dts.domain.dao.hibernate.FlightLegHibernateDao">
     		<property name="sessionFactory" ref="sessionFactory"/>
    	</bean>
    	
    	<bean id="flightService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    	    <property name="transactionManager" ref="transactionManager"/>
    	  	<property name="target" ref="flightServiceTarget"/>
    	    <property name="transactionAttributes">
    	      	<props>
    	        	<prop key="testFlightLeg">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop>
    	      	</props>
    	    </property>
      	</bean>  
    
    	<bean id="flightServiceTarget" class="com.ngc.dts.dao.hibernate.FlightLegHDaoTest" init-method="setUp"/>
    
    </beans>
    My class with testFlightLeg method that should be wrapped in a transaction:
    Code:
    package my.package;
    
    public class FlightLegHDaoTest
        implements ApplicationContextAware
    &#123;
        private AirportDao aDao;
        private CarrierDao cDao;
        private FlightLegDao flDao;
        
        private ApplicationContext applicationContext;
        protected static final DtsLogService logger = MyLogServiceFactory.createLogService&#40;MyTestCase.class&#41;;
    
        public void setUp&#40;&#41; &#123; /* get DAOs from context */ &#125;
        
        public void testFlightLeg&#40;&#41;
        &#123;
            FlightLegImpl fl = new FlightLegImpl&#40;&#41;;
            
            AirSegment as1 = new AirSegment&#40;&#41;;
    		...
    		//set data to object
    		        
            fl.addAirSegment&#40;as1&#41;;
    
            AirSegment as2 = new AirSegment&#40;&#41;;
    		...
    		//set data to object
            
            fl.addAirSegment&#40;as2&#41;;
    
            flDao.createFlightLeg&#40;fl&#41;;
            logger.debug&#40;"createFlightLeg&#58; " + fl&#41;;
        &#125;
    
        public void setApplicationContext&#40;ApplicationContext applicationContext&#41; 
        	throws BeansException 
        &#123; this.applicationContext = applicationContext; &#125;
    
    &#125;
    When this executes, Spring is opening a Hibernate session, creating a DB connection, running the SQL statement and closing the session for each SQL statement. This one method has 7 SQL statements and Spring is repeating this process 7 times. If I get a SQL exception which causes a rollback, it's only rolling back the one SQL statement instead of all of the insert and update statements generated within the method.

    The Spring log output for what should be one transaction:
    Code:
    Opening Hibernate Session
    Creating new JDBC Connection to &#91;jdbc&#58;mysql&#58;///iris&#93;
    Hibernate&#58; select carrier0_.AIRCARRIER_ID as AIRCARRIER1_, carrier0_.altName as altName5_, carrier0_.code as code5_, carrier0_.name as name5_ from AirCarrier carrier0_ where carrier0_.code=?
    Eagerly flushing Hibernate session
    Closing Hibernate Session
    Looking up default SQLErrorCodes for DataSource &#91;[email protected]c&#93;
    Database product name found in cache for DataSource &#91;[email protected]c&#93;&#58; name is 'MySQL'
    SQL error codes for 'MySQL' found
    Opening Hibernate Session
    Creating new JDBC Connection to &#91;jdbc&#58;mysql&#58;///iris&#93;
    Hibernate&#58; select airport0_.AIRPORT_ID as AIRPORT1_, airport0_.city as city3_, airport0_.country as country3_, airport0_.iata as iata3_, airport0_.icao as icao3_, airport0_.name as name3_, airport0_.state as state3_ from Airport airport0_ where airport0_.iata=?
    Eagerly flushing Hibernate session
    Closing Hibernate Session
    Opening Hibernate Session
    Creating new JDBC Connection to &#91;jdbc&#58;mysql&#58;///iris&#93;
    Hibernate&#58; select airport0_.AIRPORT_ID as AIRPORT1_, airport0_.city as city3_, airport0_.country as country3_, airport0_.iata as iata3_, airport0_.icao as icao3_, airport0_.name as name3_, airport0_.state as state3_ from Airport airport0_ where airport0_.iata=?
    Eagerly flushing Hibernate session
    Closing Hibernate Session
    Looking up default SQLErrorCodes for DataSource &#91;[email protected]c&#93;
    Database product name found in cache for DataSource &#91;[email protected]c&#93;&#58; name is 'MySQL'
    SQL error codes for 'MySQL' found
    Opening Hibernate Session
    Creating new JDBC Connection to &#91;jdbc&#58;mysql&#58;///iris&#93;
    Hibernate&#58; insert into FlightLeg &#40;index_col&#41; values &#40;?&#41;
    Hibernate&#58; insert into AirSegment &#40;arrivalTime, carrier_fk, departureTime, destination_airport_fk, equipmentCode, flightNumber, origin_airport_fk, index_col&#41; values &#40;?, ?, ?, ?, ?, ?, ?, ?&#41;
    Eagerly flushing Hibernate session
    Hibernate&#58; update AirSegment set flightLeg_fk=?, index_col=? where AIRSEGMENT_ID=?
    Closing Hibernate Session
    Opening Hibernate Session
    Creating new JDBC Connection to &#91;jdbc&#58;mysql&#58;///iris&#93;
    Hibernate&#58; insert into FlightLeg &#40;index_col&#41; values &#40;?&#41;
    Hibernate&#58; insert into AirSegment &#40;arrivalTime, carrier_fk, departureTime, destination_airport_fk, equipmentCode, flightNumber, origin_airport_fk, index_col&#41; values &#40;?, ?, ?, ?, ?, ?, ?, ?&#41;
    Eagerly flushing Hibernate session
    Closing Hibernate Session
    What do I have wrong in my transaction setup that is preventing this from running in a transaction?

  • #2
    If you want to rollback the whole 7 INSERTS then all of them have to be inside the same transaction which means all of them have to be on the same session - in your case every INSERT is made inside a separate session meaning separate transaction - as you can see, the session is flushed, transaction committed - when the exception occurs only the last insert will be rolledback (i.e. the current transaction at that moment).
    To solve this problem you need to use the same session - use either OpenSessionInViewInterceptor/Filter or create your own mecanishm to make sure the calls are made on the same session.

    Comment


    • #3
      It seems like you're simply not calling through your transactional proxy. The log output you get is exactly what I would expect when you access the "flightServiceTarget" bean directly, respectively fire up the FlightLegHDaoTest class directly, without touching the "flightService" proxy.

      Actually, your FlightLegHDaoTest class seems to be a kind-of JUnit test case, which you usually wouldn't setup as a Spring bean definition in the first place. What your context seems to be missing is a proper service facade, the usual level where transactions get demarcated (what you would design as Stateless Session Bean in an EJB architecture). A unit test class would then access that facade, implicitly triggering transactions.

      So make sure that you actually go through a "flightService" proxy that is a proper service facade, which will open an transaction before your DAOs get invoked. Such a service facade should always have a service interface, which TransactionProxyFactoryBean will create a dynamic proxy for.

      Juergen

      Comment


      • #4
        Thank you, I changed my test to be a service and that seemed to make the transaction work for inserting and updating the DB. The insert/update queries are all done within one JDBC connection and Hibernate Session.

        Log if no SQL exception thrown:
        Code:
        Creating new JDBC Connection to &#91;jdbc&#58;mysql&#58;///iris&#93;
        Hibernate&#58; insert into FlightLeg &#40;index_col&#41; values &#40;?&#41;
        Hibernate&#58; insert into AirSegment &#40;arrivalTime, carrier_fk, departureTime, destination_airport_fk, equipmentCode, flightNumber, origin_airport_fk, index_col&#41; values &#40;?, ?, ?, ?, ?, ?, ?, ?&#41;
        Hibernate&#58; insert into AirSegment &#40;arrivalTime, carrier_fk, departureTime, destination_airport_fk, equipmentCode, flightNumber, origin_airport_fk, index_col&#41; values &#40;?, ?, ?, ?, ?, ?, ?, ?&#41;
        Eagerly flushing Hibernate session
        Hibernate&#58; update AirSegment set flightLeg_fk=?, index_col=? where AIRSEGMENT_ID=?
        Hibernate&#58; update AirSegment set flightLeg_fk=?, index_col=? where AIRSEGMENT_ID=?
        Closing Hibernate Session
        However, if I change my code to throw SQL exception on one of the inserts. It is only rolling back the inserts to one table not to both. So it seems like it's not really transactional.

        Log if SQL exception thrown:
        Code:
        Creating new JDBC Connection to &#91;jdbc&#58;mysql&#58;///iris&#93;
        Hibernate&#58; insert into FlightLeg &#40;index_col&#41; values &#40;?&#41;
        Hibernate&#58; insert into AirSegment &#40;arrivalTime, carrier_fk, departureTime, destination_airport_fk, equipmentCode, flightNumber, origin_airport_fk, index_col&#41; values &#40;?, ?, ?, ?, ?, ?, ?, ?&#41;
        Unable to translate SQLException with errorCode '1048', will now try the fallback translator
        Closing Hibernate Session
        The AirSegment record is rolled back but the FlightLeg record is still inserted to the DB. Am I still missing something in my XML/code for it to be truly transactional?

        Comment


        • #5
          Is your target database configured for proper transactions? That is, in case of MySQL, to use the InnoDB table manager rather than the default MyISAM?

          For a different potential reason: Make sure that your transaction proxy configuration actually triggers a rollback when your exception gets thrown. By default, it only triggers a rollback for RuntimeException and Errors but not for checked exceptions: You need to specify a rollback rule like "-MyCheckedException" or the more general "-Exception" in your transaction attribute to cover a checked exception as well. If you turn on debug logging, you should see details on whether and why a rollback has been caused.

          Juergen

          Comment


          • #6
            Thanks for the quick response, the latest MySQL defaults to InnoDB.

            Doh! I figured out my problem, in my test class, I was instantiating the target bean instead of the service bean.

            Thanks again for your help!

            Comment

            Working...
            X