Announcement Announcement Module
Collapse
No announcement yet.
JdbcDaoSupport : transactions does not roll back! Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • JdbcDaoSupport : transactions does not roll back!

    Hi all

    I'm newbie concerning sping configuration.

    I'm writing Web Service to access a database by using a DAO.
    I'm using jdbTemplate to do my sql updates.

    For one call of my web service, I will have to do severall sql unitaries updates (I do not want ot use hibernate). Between each unitary update, I have to do some Java treatement.
    And if one exception raises during one of my update, I want all previous update will rollback!

    For, the moment, an exception is thrown, the updates are not roll back while a transation is created and the date are committed.

    Here is my spring configuration:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    <!-- =========================== -->
    <!-- Define Web Service conf -->
    <!-- =========================== -->
    <bean id="MyAppServiceProxy" class="org.springframework.transaction.interceptor .TransactionProxyFactoryBean">
    <property name="transactionManager" ref="txManager" />
    <property name="target" ref="MyAppService" />
    <property name="transactionAttributes">
    <props>
    <prop key="*">PROPAGATION_REQUIRED</prop>
    </props>
    </property>
    </bean>

    <bean id="MyAppService" class="com.MyAppServiceImpl">
    <constructor-arg>
    <ref local="MyAppDAO" />
    </constructor-arg>
    </bean>

    <!-- ===================== -->
    <!-- Session definition -->
    <!-- ===================== -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSou rceTransactionManager">
    <property name="dataSource" ref="myDatasource" />
    </bean>

    <!-- ================ -->
    <!-- DAO definition -->
    <!-- ================ -->
    <bean id="myDatasource" class="org.springframework.jndi.JndiObjectFactoryB ean">
    <property name="jndiName" value="java:/comp/env/jdbc/myDatasource" />
    </bean>

    <bean id="MyAppDAO" class="com.dao.MyAppDAOSql">
    <property name="dataSource" ref="seoLinkDatasource"/>
    </bean>
    </beans>

    and my web context (where datasource is located for some deployment reason):
    <?xml version="1.0" encoding="ISO-8859-15"?>

    <Context path="/MyAppServing" docBase="..." reloadable="true">


    <!-- The contracts datasource -->
    <Resource name="jdbc/myDatasource" auth="Container" type="javax.sql.DataSource"
    username="root" password=""
    driverClassName="org.gjt.mm.mysql.Driver"
    url="jdbc:mysql://localhost/myDb?characterEncoding=UTF8" validationQuery="SELECT 1" testOnBorrow="true">

    <parameter>
    <name>removeAbandoned</name>
    <value>true</value>
    </parameter>

    <parameter>
    <name>removeAbandonedTimeout</name>
    <value>60</value>
    </parameter>

    <parameter>
    <name>logAbandoned</name>
    <value>true</value>
    </parameter>

    </Resource>
    </Context>

    Does somebody tell me what goes wrong ?
    Why data are not roll-back while exception is thrown?

  • #2
    Use [ code][/code ] tags when posting code.

    1) Use the service in your web service not your dao directly
    2) Make sure you use the proxy not the real instance.

    Next to that isn't the database queried each time and after that the entities are fetched from the 2nd level cache instead of recreating them....
    Last edited by Marten Deinum; Sep 3rd, 2009, 12:30 PM.

    Comment


    • #3
      Hi Morten

      Thanks for your quick answer.

      I've tried to use a proxy on the DAO and just performed the test few minutes ago another time... But it does not solve my issue.

      Here is my "new" piece of context (it runs thanks to JUNIT framework):
      Code:
      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
      <beans>
      	<!-- ===================== -->
      	<!--  Session definition   -->
      	<!-- ===================== -->
      	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      		<property name="dataSource" ref="myAppDatasource" />
      	</bean>
      
      	<!-- ================ -->
      	<!--  DAO definition  -->
      	<!-- ================ -->
       	<bean id="myAppsDAO"	class="com.dao.myAppsDAOSql">
      		<property name="dataSource" ref="myAppDatasource" />
      	</bean>
      
      	<bean id="MyAppsDAOProxy"	class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
      		<property name="transactionManager" ref="txManager" />
      		<property name="target" ref="myAppsDAO" />
      		<property name="transactionAttributes">
      			<props>
      				<prop key="*">PROPAGATION_REQUIRED</prop>
      			</props>
      		</property>
      	</bean>
      	
      	<!-- bean datasource -->
      	<bean id="myAppDatasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        			<property name="driverClassName">
          			<value>org.gjt.mm.mysql.Driver</value>
        			</property>
        			<property name="url">
          			<value>jdbc:mysql://localhost/myApps?characterEncoding=UTF8</value>
        			</property>
        			<property name="username">
          			<value>XXX</value>
        			</property>
        			<property name="password">
          			<value>XXX</value>
        			</property>
      	</bean>
      </beans>
      Here is the piece of code :
      Code:
      public class TestMyAppDAOSql extends TestCase {
      
      	MyAppDAO myAppDAOProxy =  null;
      
      	public void setUp() {
      
      		// get the DAO
      		ApplicationContext ctx = new ClassPathXmlApplicationContext("wsrestContext.xml");
      		myAppDAOProxy =  (myAppDAO)ctx.getBean("myAppsDAOProxy");
      	}
      
      	public void testAdd() {
      
      		String page = "toto";
      		String url = "http://domain.fr/tata";
      
      		try {
      			seoLinksDAOProxy.add(page, url);
      		} catch (InvalidDomainException e) {
      			e.printStackTrace();
      		}
      	}
      }
      The method "add" of DAO calls severall sql insert like:
      public class MyAppsDAOSql extends JdbcDaoSupport implements MyAppsDAO {
      @Override
      public void addBack(String page, String url) throws InvalidDomainException {
      // 1 - add page informations
      insertPage(page);

      // 2 - add url domain
      insertUrl(url);
      }
      ...
      }
      and I want the first updates performed by method "insertPage(page)" will be rollback when method "insertUrl(url)" throw an exception.

      Could you tell me what I'm doing wrong ?

      When I debug, I could see I'm using a proxy.

      Thanks

      Comment


      • #4
        You are using MySQL make sure you are using InnoDB tables and not MyISAM tables. Only InnoDB supports transactions.

        Comment


        • #5
          He is my log

          Comment


          • #6
            I'm using InnoDB engine.
            Here is one table creation sample:
            Code:
              CREATE TABLE  url (
                d_id BIGINT NOT NULL,
                url VARCHAR(64) DEFAULT NULL,
            ...
                PRIMARY KEY (d_id)
              ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

            Comment


            • #7
              Please post your dao. Your configuration looks ok, you are using the proxy and InnoDB tables. So it must be something in your dao.

              Your InvalidDomainException is it a RuntimeException or a normal Exception?

              Comment


              • #8
                The InvalidDomainException is an exception I throw when I can not extract domain from an URL. And When I throw that, I want my previous sql updates will rollback.

                Here is my DAO:
                Code:
                package com.dao;
                
                import java.sql.Connection;
                import java.sql.PreparedStatement;
                import java.sql.SQLException;
                
                import org.apache.commons.logging.Log;
                import org.apache.commons.logging.LogFactory;
                import org.springframework.dao.DataIntegrityViolationException;
                import org.springframework.jdbc.core.PreparedStatementCreator;
                import org.springframework.jdbc.core.support.JdbcDaoSupport;
                
                import com.bean.DomainTable;
                import com.bean.pageTable;
                import com.exception.InvalidDomainException;
                
                public class MyAppsDAOSql extends JdbcDaoSupport  implements MyAppsDAO {
                	
                	private static Log logger = LogFactory.getLog(MyAppsDAOSql.class);
                	
                	@Override
                	public void addBack(String page, String url) throws InvalidDomainException {
                		
                		// 1 - add page informations 
                		long rId = insertPage(page);
                
                		// 2 - add url domain 
                		long kId = insertUrl(url); 
                
                               // ....
                	}
                	
                	/**
                	 * This method inserts the url
                	 * 
                	 * @param url the url to parse
                	 * @return the id of the url inserted in the Database
                	 * @throws InvalidDomainException 
                	 * @throws InvalidDomainException
                	 */
                	public long insertUrl(String url) throws InvalidDomainException {
                		
                		// 1 - add page domain 
                		long dId = insertDomain(url);
                
                		// 2 insert page
                		//TODO:
                		logger.error("DEV: todo + insertUrlTable("+url+","+ dId+")");
                		long rId = 0L;
                //		long rId = insertUrlTable(url, dId);
                		
                		return rId;
                	}
                
                	/**
                	 * This method inserts a domain based on information retrieved in the URL
                	 * 
                	 * @param url the url to parse
                	 * @return the id of the domain inserted in the Database
                	 * @throws InvalidDomainException 
                	 * @throws InvalidDomainException
                	 */
                	public long insertPage(String page) throws InvalidDomainException {
                		
                		// 1 - add page domain 
                		long dId = insertDomain(page);
                
                		// 2 insert page
                		long rId = insertPageTable(page, dId);
                		
                		return rId;
                	}
                
                	public long insertPageTable(String page, long dId) {
                		if (logger.isDebugEnabled() ) {
                			logger.debug("trying to insert page \'"+page+"\' (lastCrawlDate,dId) = ("+dId+")");
                		}
                		
                		pageTable pageTable = MyAppsDAOUtils.buildpageTable(page,dId);
                
                		return insertPageSQL(pageTable);
                	}
                
                	/**
                	 * 
                	 * @param rId
                	 * @param dId
                	 * @param uri
                	 * @param lastCrawlDate
                	 */
                	public long insertPageSQL(final pageTable pageTable) {
                		
                		final String INSERT_SQL = "insert into "+MyAppsDAOConstants.TABLE_page+" (r_id,d_id,uri,last_crawl_date) values(?,?,?,?)";
                		
                		if (logger.isDebugEnabled() ) {
                			logger.debug("preparing inserting page \'"+pageTable.getdId()+"\' for domain \'"+pageTable.getUri()+"\'");
                		}
                		
                		getJdbcTemplate().update(
                				new PreparedStatementCreator() {
                					public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                						PreparedStatement ps =	connection.prepareStatement(INSERT_SQL, new String[] {MyAppsDAOConstants.page_KEY});
                						ps.setLong(1, pageTable.getrId());
                						ps.setLong(2, pageTable.getdId());
                						ps.setString(3, pageTable.getUri());
                						return ps;
                					}
                				});
                		return pageTable.getdId();
                	}
                	
                	/**
                	 * This method inserts a domain based on information retrieved in the URL.
                	 * If the domain exists, then the domainId of the corresponding domain is returned
                	 * 
                	 * @param url the url to parse
                	 * @return the id of the domain inserted in the Database
                	 * @throws InvalidDomainException
                	 */
                	public long insertDomain(String url) throws InvalidDomainException {
                
                		if (logger.isDebugEnabled() ) {
                			logger.debug("trying to insert domain from page \'"+url+"\'");
                		}
                		
                		DomainTable domainTable = MyAppsDAOUtils.buildDomainTable(url);
                
                		return insertDomainSQL(domainTable);
                	}
                
                	/**
                	 * This method inserts a domainTable in the database 
                	 * 
                	 * @param domainTable the domainTable to insert
                	 * @return the id of the domainTable inserted in the Database
                	 * @throws InvalidDomainException
                	 */
                	public long insertDomainSQL(final DomainTable domainTable) {
                		final String INSERT_SQL = "insert into "+MyAppsDAOConstants.TABLE_DOMAIN+" (d_id,domain,sub_domain) values(?,?,?)";
                
                		if (logger.isDebugEnabled() ) {
                			logger.debug("preparing insertion domain \'"+domainTable.getdId()+"\' for domain \'"+domainTable.getSubDomain()+"\'");
                		}
                			
                			getJdbcTemplate().update(
                					new PreparedStatementCreator() {
                						public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                							PreparedStatement ps =	connection.prepareStatement(INSERT_SQL, new String[] {MyAppsDAOConstants.DOMAIN_KEY});
                							ps.setLong(1, domainTable.getdId());
                							ps.setString(2, domainTable.getDomain());
                							ps.setString(3, domainTable.getSubDomain());
                							return ps;
                						}
                
                					});
                		
                		return domainTable.getdId();
                	}
                }
                Here is the place where I throw the exception you've mentioned:
                Code:
                public class MyAppsDAOUtils {
                
                	private static Log logger = LogFactory.getLog(MyAppsDAOUtils.class);
                	
                	/**
                	 * This method builds a DomainTable based on the domain
                	 * 
                	 * @param domain used to build a DomainTable
                	 * @return the DomainTable
                	 * @throws InvalidDomainException 
                	 */
                	public static DomainTable buildDomainTable(String referrer) throws InvalidDomainException {
                		DomainTable domainTable = null;
                		
                		if (referrer!=null) {
                			
                			// create the domainTable
                			domainTable = new DomainTable();
                			
                			// set the subdomain
                			String subDomain = extractSubDomain(referrer);
                			if (subDomain == null) {
                				throw new InvalidDomainException("No sub-domain could be extracted from referrer \'"+referrer+"\'");
                			}
                			domainTable.setSubDomain(subDomain);
                
                			// set the bdomain
                			String domain = extractDomain(subDomain);
                			if (domain == null) {
                				throw new InvalidDomainException("No domain could be extracted from referrer \'"+referrer+"\'");
                			}
                			domainTable.setDomain(domain);
                			
                			// set id 
                			long dId = cksum(subDomain);
                			domainTable.setdId(dId);
                		}
                		
                		return domainTable;
                	}
                
                ...
                }
                Thank you for loosing your time to help me!

                Comment


                • #9
                  Again what kind of exception is your InvalidDomainException is it a RuntimeException or a Normal exception? If it is a normal exception you need to tell the transaction stuff to rollback for that exception.

                  Comment


                  • #10
                    You're magic!
                    It is a normal exception.

                    And I've just add this replace this property o my proxy:
                    Code:
                    				<prop key="*">PROPAGATION_REQUIRED</prop>
                    by this:
                    Code:
                    				<prop key="*">PROPAGATION_REQUIRED,-Exception</prop>
                    and the roll back runs well!

                    Thanks a lot Mortem!!!!!

                    Comment

                    Working...
                    X