Announcement Announcement Module
Collapse
No announcement yet.
Spring 2.5 ibatis 2.x transactions Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring 2.5 ibatis 2.x transactions

    I am a rookie to both spring and ibatis and i've been learning both of them for the past week.

    I've been trying to implement transactions with spring but for some reason it doesn't seem to work for me.

    Could someone please help me with this program.

    I am using mysql (that comes with xampp) and generated the ibatis artifcats using abator. The generated artifacts use Spring's SqlMapClientDaoSupport.


    Dao Class


    Code:
    public class UserDAOImpl extends SqlMapClientDaoSupport implements UserDAO {
    
        public UserDAOImpl() {
            super();
        }
    
        public int updateByPrimaryKey(User record) throws DataAccessException{
            int rows = getSqlMapClientTemplate().update("user.abatorgenerated_updateByPrimaryKey", record);
            return rows;
        }
    
        public User selectByPrimaryKey(Double id) throws DataAccessException{
            User key = new User();
            key.setId(id);
            User record = (User) getSqlMapClientTemplate().queryForObject("user.abatorgenerated_selectByPrimaryKey", key);
            return record;
        }
    }


    applicationContext.xml


    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:p="http://www.springframework.org/schema/p"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:jee="http://www.springframework.org/schema/jee"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schem...-beans-2.5.xsd
                               http://www.springframework.org/schema/tx http://www.springframework.org/schem...ing-tx-2.5.xsd
                               http://www.springframework.org/schema/context http://www.springframework.org/schem...ontext-2.5.xsd
                               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
    
          
        <context:property-placeholder location="com/sampleibatisproj/maps/jdbc.properties"/>
        
        <bean id="dataSource"
            class="org.apache.commons.dbcp.BasicDataSource"
            destroy-method="close">
            <property name="driverClassName"
                      value="${jdbc.driverClassName}" />        
    	     <property name="url"
                      value="${jdbc.url}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </bean>
    
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        	<property name="dataSource" ref="dataSource" />
        </bean>
        
        <bean id="sqlMap"
              class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
            <property name="configLocation">
                <value>classpath:com/sampleibatisproj/maps/SqlMapConfig.xml</value>
            </property>
            <property name="dataSource" ref="dataSource" />
        </bean>    
    
         <bean id="userDAOImpl"
            class="com.sampleibatisproj.dao.UserDAOImpl">
            <property name="sqlMapClient" ref="sqlMap" />
            <property name="dataSource"><ref local="dataSource"/></property>        
         </bean>
    
         <bean id="workerBeanProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
         	<property name="target" ref="workerBean" />
            <property name="transactionManager" ref="transactionManager" />
            <property name="transactionAttributes">
               <props>
                   <prop key="purchase">PROPAGATION_REQUIRED</prop>
               </props>
            </property>
            <property name="proxyTargetClass" value="true" />
         </bean>
         
      <bean id="workerBean" class="WorkerThree">     
           </bean>  
    
    </beans>
    Class on which the transaction needs to apply.


    Code:
    public class WorkerThree {
    	
    	public WorkerThree(){super();}
    	
    	public void doTransaction(){
    	    ApplicationContext ctx = new ClassPathXmlApplicationContext("com/sampleibatisproj/maps/applicationContext.xml");
    	    UserDAO userDAO = (UserDAO) ctx.getBean("userDAOImpl");            
    			
    	    User user = (User)userDAO.selectByPrimaryKey(new Double(2));
    	    System.out.println(user.getEmail() + " " + user.getFirstName() + " " + user.getLastName());
    			
            user.setId(new Double(2));
            user.setEmail("[email protected]");
            user.setFirstName("harish_kumarisk");
            userDAO.updateByPrimaryKey(user);
    	    
            user = (User)userDAO.selectByPrimaryKey(new Double(2));
    		System.out.println(user.getEmail() + " " + user.getFirstName() + " " + user.getLastName());
            
    		throw new DataAccessException("test") {};
        }
    }
    Main class


    Code:
    public class Main {
    
    	public static void main(String[] args) {
    		ApplicationContext ctx = new ClassPathXmlApplicationContext("com/sampleibatisproj/maps/applicationContext.xml");;
    		WorkerThree worker = (WorkerThree)ctx.getBean("workerBeanProxy");
    		worker.doTransaction();
    	}
    
    	public Main() {
    		super();
    	}
    }


    Here in WorkerThree's doTransaction method I am throwing a DataAccessException("abce"). This should rollback the transaction and undo the updates
    made to the db. In this case the db updates done by these lines need to be undone.

    Code:
    	
            user.setId(new Double(2));
            user.setEmail("[email protected]");
            user.setFirstName("harish");
            userDAO.updateByPrimaryKey(user);
    But its not.

    Could someone figure out what is the problem here?
    Last edited by harishchoragudi; Dec 27th, 2010, 08:49 AM. Reason: Formatted the code in [CODE] tags

  • #2
    I suggest a basic spring tutorial.... Your code is flawed...

    1) You load the context in your main/test class, retrieve the bean
    2) You load the context AGAIN in your do transaciton method which basically renders everything pretty much useless.
    3) Don't use TransactionalProxyFactoryBean use aop:config and tx:advice (or tx:annotation-driven).
    4) Use dependency injection (which is something else as number 2 as described here).

    Comment


    • #3
      Hi Marten,

      Thank you so much for the response. I think I understood what I needed to do.

      However I am not sure.

      I wrote two versions of this same program. One works and one doesn't work.
      The difference being @Transactional is for updateByPrimaryKey() method in UserDAOImpl in the version that works. In the version that doesn't work, I put the @Transactional in doTransaction() method and removed the @Transactional from UserDAOImpl. doTransaction() in worker class calls the UserDAOImpl.


      Could you please explain why the other one is not working.

      The version that doesn't work:

      applicationContext.xml:
      -------------------------

      Code:
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:p="http://www.springframework.org/schema/p"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:jee="http://www.springframework.org/schema/jee"
             xmlns:tx="http://www.springframework.org/schema/tx"
             xmlns:aop="http://www.springframework.org/schema/aop"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                                 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
                                 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
                                 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
      
          <tx:annotation-driven/>
          
          <context:property-placeholder location="com/sampleibatisproj/maps/jdbc.properties"/>
          
          <bean id="dataSource"
              class="org.apache.commons.dbcp.BasicDataSource"
              destroy-method="close">
              <property name="driverClassName"
                        value="${jdbc.driverClassName}" />        
      	     <property name="url"
                        value="${jdbc.url}" />
              <property name="username" value="${jdbc.username}" />
              <property name="password" value="${jdbc.password}" />
          </bean>
      
          <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
          	<property name="dataSource" ref="dataSource" />
          </bean>
          
          <bean id="sqlMap"
                class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
              <property name="configLocation">
                  <value>classpath:com/sampleibatisproj/maps/SqlMapConfig.xml</value>
              </property>
              <property name="dataSource" ref="dataSource" />
              <property name="useTransactionAwareDataSource" value="true"/>
          </bean>    
      
          <bean id="userDAOImpl"
              class="com.sampleibatisproj.dao.UserDAOImpl">
              <property name="sqlMapClient" ref="sqlMap" />
              <property name="dataSource"><ref local="dataSource"/></property>        
           </bean>
      
          <bean id="workerBean" class="Worker">
             <property name="userDAO" ref="userDAOImpl" />       
         </bean>  
           
      </beans>
      Worker Class:
      ----------------

      Code:
      public class Worker {
      	private UserDAO userDAO;
      	
      	public Worker(){
      		
      	}
      	
      	@Transactional
      	public void doTransaction(){
      		try
      		{
              		User user = (User)userDAO.selectByPrimaryKey(2);
      			System.out.println(user.getEmail() + " " + user.getFirstName() + " " + user.getLastName());
      		    user.setId(2);
      	        user.setEmail("[email protected]");
      	        user.setFirstName("nihu");
      	        userDAO.updateByPrimaryKey(user);
      		    
      	        throw new DataAccessException("abce") {};
      		}
      		catch(Exception e){
      			System.out.println(e.getMessage());
      			e.printStackTrace();
      		}
      	}
      
      	public UserDAO getUserDAO() {
      		return userDAO;
      	}
      
      	public void setUserDAO(UserDAO userDAO) {
      		this.userDAO = userDAO;
      	}
      }
      Main class:
      ---------------

      Code:
      public class Main {
      	public static void main(String[] args) {
      		ApplicationContext ctx = new ClassPathXmlApplicationContext("com/sampleibatisproj/maps/applicationContext.xml");
      		try
      		{
      		    System.out.println("Classpath loaded");
      		    System.out.println(" ");
      		    Worker worker = (Worker) ctx.getBean("workerBean");            
      			worker.doTransaction();
      		 }
      		catch(Exception e){
      			System.out.println(e.getMessage());
      			e.printStackTrace();
      		}
      	}
      	
      	public Main() {
      		super();
      	}
      }
      UserDAOImpl Class:
      ----------------------

      Code:
      public class UserDAOImpl extends SqlMapClientDaoSupport implements UserDAO {
      
      	public UserDAOImpl() {
      		super();
      	}
      
      
      	public void updateByPrimaryKey(User record) {
      		int rows = getSqlMapClientTemplate().update(
      				"user.abatorgenerated_updateByPrimaryKey", record);
      	}
      
      
      }
      The above version in which I use worker class doesn't work.

      How ever the version below is working fine.

      The version that works :

      applicationContext.xml:
      -------------------------

      Code:
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:p="http://www.springframework.org/schema/p"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:jee="http://www.springframework.org/schema/jee"
             xmlns:tx="http://www.springframework.org/schema/tx"
             xmlns:aop="http://www.springframework.org/schema/aop"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                                 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
                                 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
                                 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
      <beans>
      	
      	<tx:annotation-driven/>
          
          <context:property-placeholder location="com/sampleibatisproj/maps/jdbc.properties"/>
          
          <bean id="dataSource"
              class="org.apache.commons.dbcp.BasicDataSource"
              destroy-method="close">
              <property name="driverClassName"
                        value="${jdbc.driverClassName}" />        
      	     <property name="url"
                        value="${jdbc.url}" />
              <property name="username" value="${jdbc.username}" />
              <property name="password" value="${jdbc.password}" />
          </bean>
      
          <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
          	<property name="dataSource" ref="dataSource" />
          </bean>
          
          <bean id="sqlMap"
                class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
              <property name="configLocation">
                  <value>classpath:com/sampleibatisproj/maps/SqlMapConfig.xml</value>
              </property>
              <property name="dataSource" ref="dataSource" />
              <property name="useTransactionAwareDataSource" value="true"/>
          </bean>    
      
          <bean id="userDAOImpl"
              class="com.sampleibatisproj.dao.UserDAOImpl">
              <property name="sqlMapClient" ref="sqlMap" />
              <property name="dataSource"><ref local="dataSource"/></property>        
           </bean>
      
            
      </beans>

      Main Class:
      ------------------

      Code:
      public class Main {
      	public static void main(String[] args) {
      		ApplicationContext ctx = new ClassPathXmlApplicationContext("com/sampleibatisproj/maps/applicationContext.xml");
      		try
      		{
      		    System.out.println("Classpath loaded");
      		    System.out.println(" ");
      		    UserDAO userDAO = (UserDAO) ctx.getBean("userDAOImpl");            
      				
      		    User user = (User)userDAO.selectByPrimaryKey(2);
      			System.out.println(user.getEmail() + " " + user.getFirstName() + " " + user.getLastName());
      				
      	        user.setId(2);
      	        user.setEmail("[email protected]");
      	        user.setFirstName("nihuvtasak");
      	        userDAO.updateByPrimaryKey(user);
      		}
      		catch(Exception e){
      			System.out.println(e.getMessage());
      			e.printStackTrace();
      		}
      	}
      	
      	public Main() {
      		super();
      	}
      }
      Code:
      UserDAOImpl Class:
      --------------------
      
      public class UserDAOImpl extends SqlMapClientDaoSupport implements UserDAO {
      
      	public UserDAOImpl() {
      		super();
      	}
      
      	@Transactional
      	public void updateByPrimaryKey(User record) {
      		int rows = getSqlMapClientTemplate().update(
      				"user.abatorgenerated_updateByPrimaryKey", record);
      		throw new DataAccessException("abce") {};
      	}
      }


      I have some other queries as well.

      If I understand your statements correctly - you are telling that we need to load the application context only once in the project.

      1. How do we usually do that in real time scenario.
      2. Do we have to get the application context as a singleton object?
      3. What is the best way to load application Context.


      Could you please answer my queries?

      Thanks,
      Harish
      Last edited by harishchoragudi; Dec 30th, 2010, 02:26 PM. Reason: Did some code alignments

      Comment


      • #4
        You are comparing apples and oranges. My guess is it isn't working becaus eyou should be using classproxied (I suggest a read of the aop chapter especially the part explaining proxies!) and you don't have cglib in your classpath, you should also tell the tx:annotation-driven to create class based proxies.

        You really want 1 instance of your applicationcontext, you generally bootstrap your application by loading the context. You shouldn't get/need the application context further on, the application is wired together and that it is.

        Comment


        • #5
          Marten,

          I followed your advice and I included cglib-nodep-2.1_3.jar and made tx:annotation-driven use cglib.

          <tx:annotation-driven proxy-target-class="true" />

          but still no luck.

          The data is written to db even though i throw a DataAccessException. i have absolutely no clue how to proceed.

          It would be great if you help me. I am attaching the source code.


          Thanks,
          Harish

          Comment


          • #6
            If you use MySQL make sure you have transactional tables (InnoDB) and not MyISAM tables (which aren't transactional!).

            Also your code is flawed you are catching the exception, the tx management code NEVER sees the exception and thinks everything is fine and thus commits. In general NEVER catch an exception unlees you want to handle it and do something with it, else simple don't catch (or catch, log and rethrow).
            Last edited by Marten Deinum; Jan 2nd, 2011, 05:41 AM.

            Comment


            • #7
              Thank you so much for the reply.

              I was using innodb and as i said in the question - transactions were working in one case and in other cases it doesn't.

              Anyway i guess the problem is with PersistenceExceptionTranslationPostProcessor

              It has been posted as a questions in this thread
              http://forum.springsource.org/showthread.php?t=100430


              Thanks,
              Harish

              Comment


              • #8
                Anyway i guess the problem is with PersistenceExceptionTranslationPostProcessor
                No it isn't...

                Your problem is related to the fact that your code is flawed.
                Originally posted by mdeinum
                Also your code is flawed you are catching the exception, the tx management code NEVER sees the exception and thinks everything is fine and thus commits. In general NEVER catch an exception unlees you want to handle it and do something with it, else simple don't catch (or catch, log and rethrow).

                Comment


                • #9
                  Marten - Yes, you were absolutely right. The problem was in handling the exception.


                  Code:
                  code - 1:
                  ---------------
                  
                  @Repository
                  public class HibWorker implements Worker{
                  ...........//setters, getters and constructors 
                  
                  	@Transactional
                  public void doTrans(){
                  		try{
                  		Employee employee =  new Employee();
                  	    employee.setFirstName("abcde");
                  	    employee.setLastName("fghi");
                  	    employee.setStartDate(new GregorianCalendar(2007,8,1).getTime());
                  	    employee.setEndDate(new GregorianCalendar(2007,9,3).getTime());
                  	    employeeDaoImpl.Store(employee);
                  	    throw new RuntimeException();
                  	    }
                  		catch(Exception e){}
                  	}
                  }
                  
                  
                  code-2:
                  -----------
                  
                  @Repository
                  public class HibWorker implements Worker{
                  ...........//setters, getters and constructors 
                  
                  	@Transactional
                  public void doTrans(){
                  		Employee employee =  new Employee();
                  	    employee.setFirstName("abcde");
                  	    employee.setLastName("fghi");
                  	    employee.setStartDate(new GregorianCalendar(2007,8,1).getTime());
                  	    employee.setEndDate(new GregorianCalendar(2007,9,3).getTime());
                  	    employeeDaoImpl.Store(employee);
                  	    throw new RuntimeException();
                  }

                  Code - 1 commits the transaction while code - 2 rolls it back coz the exception is not handled.

                  But we sure can handle the exception where the doTrans() method is called.
                  It has no effect on transaction committing or rollling back.


                  Code:
                   try{
                      hibWorker.doTrans();
                     }
                     catch (Exception e) {
                      System.out.println(e.getMessage());
                     }
                  Please correct me if I am incorrect.

                  -harish

                  Comment


                  • #10
                    There is no issue with handling the exception in the code calling the transactional method, that is how it should be done in the first place.

                    Comment

                    Working...
                    X