Announcement Announcement Module
Collapse
No announcement yet.
HibernateTransactionManager rollback won't work Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • HibernateTransactionManager rollback won't work

    I'm facing a serious problem when dealing with the HibernateTransactionManager rollback method.
    (I have to reimplement a jdbc layer with hibernate, while the api of the layer has to remain the same. Therefore I have to substitute a method wich uses the jdbc connection rollback method under the hood with a method that now uses the HibernateTransactionManager rollback method.)
    I use the transactionManager the following way:

    Code:
    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    TransactionStatus status = transactionManager.getTransaction(def);
    transactionManager.rollback(status);
    I know that my transactionManager is configured properly and rollback works. This is because i implemented some tests with AbstractTransactionalDataSourceSpringContextTests. After every test everything is rolled back properly. Also calling

    Code:
    this.transactionManager.rollback(this.transactionStatus);
    in a Testcase works fine.

    But when doing it in another bean (with properly injected transactionManager) a rollback won't work. I've played with several PropagationBehaviors and combinations on where in the code I set them.

    I also took the AbstractTransactionalDataSourceSpringContextTests source code and designed my class like this (especially the behavior of TransactionDefinition and TransactionStatus).

    When debugging the code I discovered that "my variant" behaves very similiar to the one of the AbstractTransactionalDataSourceSpringContextTests class.
    In the Method "private void processRollback(DefaultTransactionStatus status)" of AbstractPlatformTransactionManager my variant does either a "doRollback(status);" or a"doSetRollbackOnly(status);" while the AbstractTransactionalDataSourceSpringContextTests rollback always does "doRollback(status);". This propagates forword to a "jdbcContext.connection().rollback();" and so on...

    So to me, everything looks fine, but the results aren't...

    Can anybody help with some hints on this?

    Thanks in advance

    ps: I have to stick to this pattern, so declarative or programmatic transaction demarcation is out of the way.

    pps: i also tried the simple

    Code:
    Connection conn = DataSourceUtils.getConnection(dataSource);
    conn.rollback();
    or the more sophisticated?

    Code:
    TransactionAwareDataSourceProxy proxy = new TransactionAwareDataSourceProxy(dataSource); 
    proxy.getConnection().rollback();
    approach... but they won't work. The only rollback that worked so far was with AbstractTransactionalDataSourceSpringContextTests (same datasource, same transactionManager).

    ppps:
    spring 2.0.6
    hibernate 3.2.4.sp1
    hsql database (i also tried db2, but same result)
    Last edited by callisto; Jun 25th, 2007, 01:46 PM. Reason: changed title

  • #2
    ps: I have to stick to this pattern, so declarative or programmatic transaction demarcation is out of the way.
    Ehr... Don't want to ruin your fun but you currently ARE using programmtic transactions...

    Also I wonder WHY you cannot use declarative transactions. Because that way your transaction management would be a lot simpler and your issue would then easily be solved by using a HibernateCallback.

    However can you post some configuration and code, please remember to use the [ code][ /code] tags.

    Comment


    • #3
      I'm not exactly sure what the problem even is here. If you are using the transactional unit tests then you code is going to run in the supplied transaction unless you supply one. You seen to be suggesting using some form of programmatic transactions but I don't really understand what you are doing. If you use TransactionTemplate, this will shield you from lots of the details of the transaction management. I would suggest having a look at this. It would also be useful if you could explain more about what you trying to do.
      Last edited by karldmoore; Aug 29th, 2007, 12:00 PM.

      Comment


      • #4
        solved

        i have legacy code the uses an db-access layer that is implemented in plain jdbc. this layer is going to be substituted by aspring manageg mixed hibernate, jdbc layer.

        i have one pooled datasource, a hibernate session factory and a hibernateTransaction Manager defined in my applicationContext.xml:

        Code:
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        		....
        	</bean>
        	
        
        	<!-- Hibernate Session Factory -->
        
        	<bean id="sessionFactory"
        		class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        		<property name="dataSource" ref="dataSource" />
        		<property name="mappingDirectoryLocations">
        			<list>
        				<value>classpath:...</value>
        			</list>
        		</property>
        
        		<property name="hibernateProperties">
        			...
        		</property>
        	</bean>
        	
        
        	<!-- Hibernate Transaction Manager -->
        
            <bean id="txManagerHibernate" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
                <property name="sessionFactory" ref="sessionFactory"/>
            </bean>
        legacy code looks like this (where db is the db-access layer):

        Code:
        db.connect();
        
        
        // access dataSource with jdbcTemplate or hibernateTemplate (CRUD):
        
        db.execute("INSERT ....");
        
        db.delete(entity);
        
        // and so on (any combination possible
        
        db.commit();
        
        
        db.rollback(); // on exception
        
        
        db.disConnect(); // finally
        between rollback or commit the db class tries to manage the transaction under the hood. in the new version i designed it like AbstractTransactionalDataSourceSpringContextTests.

        commit/rollback now works! i've tried with several things. with the newest combination it WORKS all of a sudden. => so far ok

        here is some code from the db class (excerpt)

        Code:
        	public void connect() throws Exception {
        		transactionDefinition = new DefaultTransactionDefinition();
        	}
        
                public void rollback() throws SQLException {
        		
        		/**
        		 * this works for jdbc:
        		 */
        		Connection conn = DataSourceUtils.getConnection((DataSource) 
        				Spring.getFactory().getBean("dataSource"));
        		conn.rollback();			
        
        		/**
        		 * this for hibernate:
        		 */
        this.transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        		this.transactionStatus = hibernateTransactionManager.getTransaction(this.transactionDefinition);
        		
        		hibernateTransactionManager.rollback(transactionStatus);
                }
        
        	public void disConnect() throws SQLException {
        		transactionDefinition = null;
        	}
        
        	public void commit() throws SQLException {
        
        		hibernateTransactionManager.commit(this.transactionStatus);		
        		this.transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        		this.transactionStatus = hibernateTransactionManager.getTransaction(this.transactionDefinition);
        	}
        
        	public void exec(String sql) throws Exception {
        		try {			this.transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        			this.transactionStatus = hibernateTransactionManager.getTransaction(this.transactionDefinition);
        			
        			jdbcTemplate.execute(sql);
        		}
        		catch (DataAccessException dae) {
        			log.error(dae);
        			throw dae;
        		}
           	}
        
        	public void delete(Entity<T> entity) throws Exception {
        		dao.getHibernateTemplate().delete(entity);
        	}
        
                .....
        
        }
        I also had to enable FLUSH_EAGER:

        Code:
        dao.getHibernateTemplate().setFlushMode(HibernateAccessor.FLUSH_EAGER);
        i know the design is awfull, but i had to stick to the api of the db class.

        as told, everything works now... maybe somebody needs this sometime (but i don't think so...)

        Another question that aroused was that the HibernateTransactionManager should be able to rollback the jdbc stuff as well. i got this exception when mixing HibernateTransactionManager and DataSourceTransactionManager:

        Code:
        org.springframework.transaction.IllegalTransactionStateException: 
        Pre-bound JDBC Connection found! HibernateTransactionManager does not support 
        running within DataSourceTransactionManager if told to manage the DataSource 
        itself. It is recommended to use a single HibernateTransactionManager for all 
        transactions on a single DataSource, no matter whether Hibernate or 
        JDBC access.
        So i switched completeley to HibernateTransactionManager. But on the JDBC stuff i have to still call conn.rollback(). otherwise it won't roll back.

        Has anybody experienced somthing similiar (when combing jdbc and hibernate). Are there any know drawbacks/bugs/caveats. Or is there even a nicer pattern for doing this?

        best regards...

        Comment


        • #5
          i have legacy code the uses an db-access layer that is implemented in plain jdbc. this layer is going to be substituted by aspring manageg mixed hibernate, jdbc layer.
          Which still doesn't prevent you from using declarative transaction management. It might take some thought.

          So i switched completeley to HibernateTransactionManager. But on the JDBC stuff i have to still call conn.rollback(). otherwise it won't roll back.
          Depends on how you started a transaction and how you are getting a Connection to the database. If you use the Spring supplied (or suggested) ways calling conn.rollback() shouldn't be needed.

          Comment


          • #6
            Depends on how you started a transaction and how you are getting a Connection to the database. If you use the Spring supplied (or suggested) ways calling conn.rollback() shouldn't be needed.
            i get the connection with the following code (dataSource is properly defined):
            Code:
            Connection conn = DataSourceUtils.getConnection((DataSource) 
            				Spring.getFactory().getBean("dataSource"));
            Starting a connection:

            Code:
            this.transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            this.transactionStatus = hibernateTransactionManager.getTransaction(this.transactionDefinition);
            where is the fault? they are Spring supplied (or suggested) ways, aren't they?

            thnaks for the replies

            best regards

            Comment


            • #7
              IllegalTransactionStateException

              now i tried to check what happens if two different threads (started from the same class) gain access to the database-layer via the way mentioned above (mixed Hibernate and Jdbc access). i get the already mentioned exception again:

              Code:
              org.springframework.transaction.IllegalTransactionStateException: 
              Pre-bound JDBC Connection found! HibernateTransactionManager does not support 
              running within DataSourceTransactionManager if told to manage the DataSource 
              itself. It is recommended to use a single HibernateTransactionManager for all 
              transactions on a single DataSource, no matter whether Hibernate or 
              JDBC access.
              Neither am I using DataSourceTransactionManager nor do I directly access Jdbc (only via JdbcTemplate and DataSourceUtils.getConnection(dataSource)).

              I'm wondering what this might be...

              Comment


              • #8
                An existing transaction/connection is bound in a ThreadLocal. So it is local to the current thread. If you execute/start another thread and try to start a new transaction you will get this exception.

                Comment

                Working...
                X