Announcement Announcement Module
Collapse
No announcement yet.
Help! Transactions not working -- newbie question Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Help! Transactions not working -- newbie question

    I'm new to the spring framework, and am liking it so far. However, I cannot get transactions to work for the life of me.... I am trying to use programmatic transactions at the moment, I'll play with the declarative ones later. Here is the code I am using:
    Code:
    	private void sendHeartbeat() {
    		PlatformTransactionManager transactionManager = Spring.getTransactionManager();
    
    		DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    		def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    
    		TransactionStatus status = transactionManager.getTransaction(def);
    
    		DataSource ds = Spring.getDataSource();
    
    		JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
    		
    
    
    		jdbcTemplate.update(HEARTBEAT_SQL, new Object[]{NODE_NAME});
    		status.setRollbackOnly();
    		transactionManager.rollback(status);
    		log.info("Rolled back!");
    	}
    This has been modified several times based on forum messages I've read and what I've gathered from the docs, so it's probably a lot more verbose than it needs to be.

    The basi c problem is that even though I am forcing a rollback (remeber, this code is just for testing and learning the framework) the rollback never happens. I always see the results committed to the database.

    My relevent configuration is:

    Code:
    	<!-- Hibernate SessionFactory -->
    	<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
    		<property name="dataSource">
    			<ref local="dataSource"/>
    		</property>
    		<property name="mappingResources">
    			<value>/test.hbm.xml</value>
    		</property>
    		<property name="hibernateProperties">
    			<props>
    				<prop key="hibernate.dialect">net.sf.hibernate.dialect.Oracle9Dialect</prop>
    			</props>
    		</property>
    	</bean>
    
    	<!-- Transaction manager for a single Hibernate SessionFactory &#40;alternative to JTA&#41; -->
    	<bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
    		<property name="sessionFactory">
    			<ref local="sessionFactory"/>
    		</property>
    	</bean>

    What am I doing wrong?


    -- EDIT --

    Turned on full debugging, got this as output:

    Code:
         &#91;java&#93; 10&#58;48&#58;13,516 INFO  &#91;SQLErrorCodesFactory&#93; Looking up default SQLErrorCodes for DataSource
         &#91;java&#93; 10&#58;48&#58;13,516 INFO  &#91;SQLErrorCodesFactory&#93; Database product name found in cache for DataSource &#91;[email protected]40&#93;. Name is 'Oracle'.
         &#91;java&#93; 10&#58;48&#58;13,522 DEBUG &#91;JdbcTemplate&#93; Executing SQL query &#91;select    node_name from    node_info where    active = 'T'    and rownum = 1 order by   node_info_id &#93;
         &#91;java&#93; 10&#58;48&#58;13,523 DEBUG &#91;DriverManagerDataSource&#93; Creating new JDBC connection to &#91;jdbc&#58;oracle&#58;thin&#58;@192.168.0.233&#58;1521&#58;meddev&#93;
         &#91;java&#93; 10&#58;48&#58;13,715 INFO  &#91;ClusterNode&#93; Assuming role of master node
         &#91;java&#93; 10&#58;48&#58;18,723 DEBUG &#91;DefaultListableBeanFactory&#93; Returning cached instance of singleton bean 'transactionManager'
         &#91;java&#93; 10&#58;48&#58;18,725 DEBUG &#91;HibernateTransactionManager&#93; Using transaction object &#91;[email protected]893&#93;
         &#91;java&#93; 10&#58;48&#58;18,726 DEBUG &#91;HibernateTransactionManager&#93; Creating new transaction
         &#91;java&#93; 10&#58;48&#58;18,726 DEBUG &#91;SessionFactoryUtils&#93; Opening Hibernate session
         &#91;java&#93; 10&#58;48&#58;18,726 DEBUG &#91;HibernateTransactionManager&#93; Opened new session &#91;net.sf.hibernate.impl.SessionImpl@159e154&#93; for Hibernate transaction
         &#91;java&#93; 10&#58;48&#58;18,726 DEBUG &#91;HibernateTransactionManager&#93; Exposing Hibernate transaction as JDBC transaction &#91;C3P0ProxyConnection &#91;Invocation Handler&#58; com.mchange.v2.c3p0.impl.C3P0PooledConnection$ProxyConnectionInvocationHandler@1d87b85&#93;&#93;
         &#91;java&#93; 10&#58;48&#58;18,727 DEBUG &#91;TransactionSynchronizationManager&#93; Bound value &#91;org.springframework.jdbc.datasource.ConnectionHolder@1958bf9&#93; for key &#91;com.mchange.v2.c3p0.ComboPooledDataSource@a522a6&#93; to thread &#91;Thread-5&#93;
         &#91;java&#93; 10&#58;48&#58;18,727 DEBUG &#91;TransactionSynchronizationManager&#93; Bound value &#91;org.springframework.orm.hibernate.SessionHolder@118958e&#93; for key &#91;net.sf.hibernate.impl.SessionFactoryImpl@19fe451&#93; to thread &#91;Thread-5&#93;
         &#91;java&#93; 10&#58;48&#58;18,727 DEBUG &#91;TransactionSynchronizationManager&#93; Initializing transaction synchronization
         &#91;java&#93; 10&#58;48&#58;18,727 DEBUG &#91;DefaultListableBeanFactory&#93; Returning cached instance of singleton bean 'dataSource'
         &#91;java&#93; 10&#58;48&#58;18,727 INFO  &#91;SQLErrorCodesFactory&#93; Looking up default SQLErrorCodes for DataSource
         &#91;java&#93; 10&#58;48&#58;18,727 INFO  &#91;SQLErrorCodesFactory&#93; Database product name found in cache for DataSource &#91;[email protected]40&#93;. Name is 'Oracle'.
         &#91;java&#93; 10&#58;48&#58;18,728 DEBUG &#91;JdbcTemplate&#93; Executing SQL update &#91;update    node_info set    last_heartbeat = sysdate where    node_info_id in &#40;       select          max&#40;node_info_id&#41;       from          node_info       where          active='T'          and node_name = ?    &#41;    and rownum = 1 &#93;
         &#91;java&#93; 10&#58;48&#58;18,728 DEBUG &#91;DriverManagerDataSource&#93; Creating new JDBC connection to &#91;jdbc&#58;oracle&#58;thin&#58;@192.168.0.233&#58;1521&#58;meddev&#93;
         &#91;java&#93; 10&#58;48&#58;18,881 DEBUG &#91;DataSourceUtils&#93; Registering transaction synchronization for JDBC connection
         &#91;java&#93; 10&#58;48&#58;18,882 DEBUG &#91;TransactionSynchronizationManager&#93; Bound value &#91;org.springframework.jdbc.datasource.ConnectionHolder@102b2b6&#93; for key &#91;[email protected]40&#93; to thread &#91;Thread-5&#93;
         &#91;java&#93; 10&#58;48&#58;18,882 DEBUG &#91;TransactionSynchronizationManager&#93; Retrieved value &#91;org.springframework.jdbc.datasource.ConnectionHolder@102b2b6&#93; for key &#91;[email protected]40&#93; bound to thread &#91;Thread-5&#93;
         &#91;java&#93; 10&#58;48&#58;18,882 DEBUG &#91;StatementCreatorUtils&#93; Setting SQL statement parameter value; columnIndex=1, parameter value='Test Node', valueClass=java.lang.String, sqlType=unknown
         &#91;java&#93; 10&#58;48&#58;18,885 DEBUG &#91;JdbcTemplate&#93; SQL update affected 1 rows
         &#91;java&#93; 10&#58;48&#58;18,887 INFO  &#91;ClusterNode&#93; Rolled back!
         &#91;java&#93; 10&#58;48&#58;18,888 DEBUG &#91;HibernateTransactionManager&#93; Transactional code has requested rollback
         &#91;java&#93; 10&#58;48&#58;18,888 DEBUG &#91;HibernateTransactionManager&#93; Triggering beforeCompletion synchronization
         &#91;java&#93; 10&#58;48&#58;18,888 DEBUG &#91;TransactionSynchronizationManager&#93; Removed value &#91;org.springframework.jdbc.datasource.ConnectionHolder@102b2b6&#93; for key &#91;[email protected]40&#93; from thread &#91;Thread-5&#93;
         &#91;java&#93; 10&#58;48&#58;18,921 DEBUG &#91;HibernateTransactionManager&#93; Initiating transaction rollback
         &#91;java&#93; 10&#58;48&#58;18,921 DEBUG &#91;HibernateTransactionManager&#93; Rolling back Hibernate transaction on session &#91;net.sf.hibernate.impl.SessionImpl@159e154&#93;
         &#91;java&#93; 10&#58;48&#58;18,921 DEBUG &#91;HibernateTransactionManager&#93; Triggering afterCompletion synchronization
         &#91;java&#93; 10&#58;48&#58;18,922 DEBUG &#91;TransactionSynchronizationManager&#93; Clearing transaction synchronization
         &#91;java&#93; 10&#58;48&#58;18,922 DEBUG &#91;TransactionSynchronizationManager&#93; Removed value &#91;org.springframework.orm.hibernate.SessionHolder@118958e&#93; for key &#91;net.sf.hibernate.impl.SessionFactoryImpl@19fe451&#93; from thread &#91;Thread-5&#93;
         &#91;java&#93; 10&#58;48&#58;18,922 DEBUG &#91;TransactionSynchronizationManager&#93; Removed value &#91;org.springframework.jdbc.datasource.ConnectionHolder@1958bf9&#93; for key &#91;com.mchange.v2.c3p0.ComboPooledDataSource@a522a6&#93; from thread &#91;Thread-5&#93;
         &#91;java&#93; 10&#58;48&#58;18,922 DEBUG &#91;HibernateTransactionManager&#93; Closing Hibernate session &#91;net.sf.hibernate.impl.SessionImpl@159e154&#93; after transaction
         &#91;java&#93; 10&#58;48&#58;18,922 DEBUG &#91;SessionFactoryUtils&#93; Closing Hibernate session
         &#91;java&#93; 10&#58;48&#58;18,923 DEBUG &#91;DefaultListableBeanFactory&#93; Returning cached instance of singleton bean 'dataSource'
    It appears that the transaction is trying to get rolled back, but I don't see it happening....???

  • #2
    More information

    This is driving me crazy.

    Spent some time debugging the spring data access code, and I see that in the middle of JdbcTemplate.execute(PreparedStatementCreator psc, PreparedStatementCallback action) that my datasource has autocommit = true! But, it was my understanding that the transaction layer would automatically set autocommit to false.

    I turned more debug output on, and I can see that the HibernateTransactionManager says that it turns autocommit off
    Code:
    2004-10-15 13&#58;43&#58;14,243 DEBUG &#91;net.sf.hibernate.transaction.JDBCTransaction&#93; begin
    2004-10-15 13&#58;43&#58;14,243 DEBUG &#91;net.sf.hibernate.transaction.JDBCTransaction&#93; current autocommit status&#58;true
    2004-10-15 13&#58;43&#58;14,243 DEBUG &#91;net.sf.hibernate.transaction.JDBCTransaction&#93; disabling autocommit
    2004-10-15 13&#58;43&#58;14,246 DEBUG &#91;org.springframework.orm.hibernate.HibernateTransactionManager&#93; Exposing Hibernate transaction as JDBC transaction &#91;C3P0ProxyConnection &#91;Invocation Handler&#58; com.mchange.v2.c3p0.impl.C3P0PooledConnection$ProxyConnectionInvocationHandler@b32ed4&#93;&#93;
    2004-10-15 13&#58;43&#58;14,247 DEBUG &#91;org.springframework.transaction.support.TransactionSynchronizationManager&#93; Bound value &#91;org.springframework.jdbc.datasource.ConnectionHolder@1c7980c&#93; for key &#91;com.mchange.v2.c3p0.ComboPooledDataSource@a522a6&#93; to thread &#91;Thread-5&#93;
    2004-10-15 13&#58;43&#58;14,247 DEBUG &#91;org.springframework.transaction.support.TransactionSynchronizationManager&#93; Bound value &#91;org.springframework.orm.hibernate.SessionHolder@1077fc9&#93; for key &#91;net.sf.hibernate.impl.SessionFactoryImpl@cf710e&#93; to thread &#91;Thread-5&#93;
    2004-10-15 13&#58;43&#58;14,247 DEBUG &#91;org.springframework.transaction.support.TransactionSynchronizationManager&#93; Initializing transaction synchronization
    2004-10-15 13&#58;43&#58;14,277 DEBUG &#91;org.springframework.beans.factory.support.DefaultListableBeanFactory&#93; Returning cached instance of singleton bean 'dataSource'
    20
    ...
    ...
    ...
    
    2004-10-15 13&#58;43&#58;15,975 DEBUG &#91;net.sf.hibernate.transaction.JDBCTransaction&#93; rollback
    2004-10-15 13&#58;43&#58;15,976 DEBUG &#91;net.sf.hibernate.impl.SessionImpl&#93; transaction completion
    2004-10-15 13&#58;43&#58;15,976 DEBUG &#91;net.sf.hibernate.transaction.JDBCTransaction&#93; re-enabling autocommit
    So my thought would be I am still doing something wrong and getting an incorrect data source somehow -- But I am just not seeing it.... I hope someone can tell me what I'm doing wrong here.

    Thanks

    Comment


    • #3
      Could this be the problem?

      Code:
      2004-10-15 14&#58;04&#58;51,457 DEBUG &#91;org.springframework.transaction.support.TransactionSynchronizationManager&#93; Bound value &#91;org.springframework.jdbc.datasource.ConnectionHolder@7244ca&#93; for key &#91;com.mchange.v2.c3p0.ComboPooledDataSource@1e0512a&#93; to thread &#91;Thread-5&#93;
      2004-10-15 14&#58;04&#58;51,457 DEBUG &#91;org.springframework.transaction.support.TransactionSynchronizationManager&#93; Bound value &#91;org.springframework.orm.hibernate.SessionHolder@17f11fb&#93; for key &#91;net.sf.hibernate.impl.SessionFactoryImpl@23756&#93; to thread &#91;Thread-5&#93;
      2004-10-15 14&#58;04&#58;51,457 DEBUG &#91;org.springframework.transaction.support.TransactionSynchronizationManager&#93; Initializing transaction synchronization
      
      ....
      ....
      ....
      
      2004-10-15 14&#58;04&#58;53,005 DEBUG &#91;org.springframework.jdbc.core.JdbcTemplate&#93; Executing SQL update &#91;INSERT into NODE_INFO &#40;NODE_NAME, LAST_HEARTBEAT&#41; values &#40;?, sysdate&#41; &#93;
      2004-10-15 14&#58;04&#58;53,008 DEBUG &#91;org.springframework.transaction.support.TransactionSynchronizationManager&#93; Retrieved value &#91;org.springframework.jdbc.datasource.ConnectionHolder@457d21&#93; for key &#91;[email protected]bc&#93; bound to thread &#91;Thread-5&#93;
      2004-10-15 14&#58;04&#58;53,231 DEBUG &#91;org.springframework.transaction.support.TransactionSynchronizationManager&#93; Retrieved value &#91;org.springframework.jdbc.datasource.ConnectionHolder@457d21&#93; for key &#91;[email protected]bc&#93; bound to thread &#91;Thread-5&#93;
      2004-10-15 14&#58;04&#58;53,234 DEBUG &#91;org.springframework.jdbc.core.StatementCreatorUtils&#93; Setting SQL statement parameter value; columnIndex=1, parameter value='Test Node', valueClass=java.lang.String, sqlType=unknown
      2004-10-15 14&#58;04&#58;53,451 DEBUG &#91;org.springframework.jdbc.core.JdbcTemplate&#93; SQL update affected 1 rows
      Note that the values retrieved from the cache are NOT the same as the ones entered....

      Comment


      • #4
        OK -- getting somewhere.

        First off, I was looking in the wrong config file... :oops: I saw that I had connection pooling via c3p0. I changed the config to NOT use connection pooling, and rollbacks work properly. So that leads to the (hopefully) final question -- Given the fact that this code works
        Code:
        	private void sendHeartbeat&#40;&#41; &#123;
        		PlatformTransactionManager transactionManager = Spring.getTransactionManager&#40;&#41;;
        		TransactionTemplate tt = new TransactionTemplate&#40;transactionManager&#41;;
        		tt.execute&#40;new TransactionCallbackWithoutResult&#40;&#41; &#123;
        			protected void doInTransactionWithoutResult&#40;TransactionStatus status&#41; &#123;
        				DataSource ds = Spring.getDataSource&#40;&#41;;
        				JdbcTemplate jdbcTemplate = new JdbcTemplate&#40;ds&#41;;
        				jdbcTemplate.update&#40;HEARTBEAT_SQL, new Object&#91;&#93;&#123;NODE_NAME&#125;&#41;;
        				status.setRollbackOnly&#40;&#41;;
        				log.info&#40;"Rolled back!"&#41;;
        			&#125;
        		&#125;&#41;;
        	&#125;
        when NOT using c3p0 connection pooling, but DOES NOT work when pooling is active -- what am I doing wrong?

        Comment


        • #5
          Hibernate _does_ explicitly turn off auto-commit when a transaction is started. In the non-Hibernate case, with Spring's DataSourceTransactionManager, Spring itself will turn-off auto-commit.

          To be honest I've always used dbcp over c3po when not using JTA, but I would guess you should look at yur c3po config and see if you can set it up to turn off auto-commit. It may be overriding things somehow...

          Comment


          • #6
            Still not working

            I switched to use DBCP instead of C3P0, but rollbacks still don't work for me.

            My config is:
            Code:
            	<bean id="dbcpDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            		<property name="driverClassName">
            			<value>$&#123;jdbc.driverClassName&#125;</value>
            		</property>
            
            
            		<property name="maxActive">
            			<value>100</value>
            		</property>
            		<property name="maxIdle">
            			<value>30</value>
            		</property>
            		<property name="maxWait">
            			<value>100</value>
            		</property>
            		<property name="url">
            			<value>$&#123;jdbc.url&#125;</value>
            		</property>
            		<property name="username">
            			<value>$&#123;jdbc.username&#125;</value>
            		</property>
            		<property name="password">
            			<value>$&#123;jdbc.password&#125;</value>
            		</property>
            	</bean>
            
            <!-- Hibernate SessionFactory -->
            	<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
            		<property name="dataSource">
            			<ref local="dbcpDataSource"/>
            		</property>
            		<property name="mappingResources">
            			<value>/medware.hbm.xml</value>
            		</property>
            		<property name="hibernateProperties">
            			<props>
            				<prop key="hibernate.dialect">net.sf.hibernate.dialect.Oracle9Dialect</prop>
            			</props>
            		</property>
            	</bean>
            
            	<!-- Transaction manager for a single Hibernate SessionFactory &#40;alternative to JTA&#41; -->
            	<bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
            		<property name="sessionFactory">
            			<ref local="sessionFactory"/>
            		</property>
            	</bean>
            and the code in question is:

            Code:
                private void sendHeartbeat&#40;&#41; &#123;
                    log.debug&#40;"sendHeartbeat&#40;&#41;"&#41;;
                    PlatformTransactionManager transactionManager = Spring.getTransactionManager&#40;&#41;;
                    TransactionTemplate tt = new TransactionTemplate&#40;transactionManager&#41;;
                    tt.execute&#40;new TransactionCallbackWithoutResult&#40;&#41; &#123;
                        protected void doInTransactionWithoutResult&#40;TransactionStatus status&#41; &#123;
                            DataSource ds = Spring.getDataSource&#40;&#41;;
                            JdbcTemplate jdbcTemplate = new JdbcTemplate&#40;ds&#41;;
                            jdbcTemplate.update&#40;HEARTBEAT_SQL, new Object&#91;&#93;&#123;NODE_NAME&#125;&#41;;
            				status.setRollbackOnly&#40;&#41;;
            				log.info&#40;"Rolled back!"&#41;;
                        &#125;
                    &#125;&#41;;
                &#125;
            I think the problem may be with the
            Code:
            DataSource ds = Spring.getDataSource&#40;&#41;;
            line inside of my function -- I ask for the datasource bean so that I can use it in the template -- Is this what I should be doing? I suspect I am getting a new connection from the pool instead of using the one in the transaction, but how do I get the "proper" one to pass to the JDBCTemplate?

            Comment


            • #7
              What is the 'Spring' class that you are using? Is it a singleton of some sort?

              As long as you are creating the jdbcTemplate specifying the same datasource that is being fed to the Hibernate session factory, then you will in fact share the same connection. The Hibernate classes do expose the connection on the current thread, syncrhonized to the transaction, and the jdbc classes will key off the datasource object to get the existing connection...

              Comment


              • #8
                AHA! That was it! My 'Spring' class is a simple wrapper around common operations, and the getDataSource() function was returning the original, non-pooled datasource, so it didn't know about the transaction. I feel stupid now Thanks so much for the help, I was losing my mind

                Comment

                Working...
                X