开始怀疑Spring框架的jdbc事务处理方式

05-09-28 lemon_zc1949
目前我做了个小小的试验,证明了Spring的jdbc事务的问题。我的环境:

使用jakarta commons dbcp 的 org.apache.commons.dbcp.BasicDataSource,作为DataSource.

xml中的配置如下:

    <bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost/TransactionTest" />
        <property name="username" value="zc" />
        <property name="password" value="zc" />
        <property name="defaultAutoCommit" value="false" />
    </bean>
<p>

my sql 数据库TransactionTest中有2个表,一个account, 一个accountprofile,分别弄了2个dao

    <bean id="accountdao"
          class="test.AccountDaoImp" >
          <property name="dataSource" ref="datasource" />
    </bean>
    	 
    <bean id="accountprofiledao"
          class="test.AccountProfileDaoImp" >
          <property name="dataSource" ref="datasource" />
    </bean>

<p>

事务管理器配置:

    <bean id="dbTransactionManager"
    	  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    	  <property name="dataSource" ref="datasource" />
    </bean>
<p>

业务门面:

    <bean id="accountservice_target"
    	  class="test.AccountService" >
    	  <property name="accountDao" ref="accountdao" />
    	  <property name="accountProfileDao" ref="accountprofiledao" />    	  
    </bean>

<p>

最后配置Spring的事务代理(其实就是利用了aop):

    <bean id="accountservice" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" >
    	<property name="transactionManager" ref="dbTransactionManager" />
    	<property name="target" ref="accountservice_target" />
    	<property name="transactionAttributes" >
    		<props>
    			<prop key="register*">PROPAGATION_REQUIRED</prop>
    		</props>
    	</property>
    </bean>

<p>

我的业务门面实现有个方法:

	public void registerAccount(Account a, AccountProfile ap){
		accountDao.insert(a);
		accountProfileDao.insert(ap);		
	}
<p>

分别调用2个dao的insert()方法,Account, AccountProfile是值对象。

测试代码:

	public static void main(String[] args) {
		FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("beans.xml");
		
		AccountService service = (AccountService) context.getBean("accountservice");
		
		Account a = new Account();
		a.setId("U001");
		a.setName("zc");
		a.setPhone("123");
		
		AccountProfile ap = new AccountProfile();
		ap.setName(a.getName()); // "zc"
		ap.setPassword("abcdef");
		
		service.registerAccount(a,ap);

		a = new Account();
		a.setId("U002");
		a.setName("zc");
		a.setPhone("123");
		
		ap = new AccountProfile();
		ap.setName(a.getName()); // "zc"
		ap.setPassword("12345");
		
		service.registerAccount(a,ap);
		
		context.close();

	}

<p>

由于表accountprofile 的name 字段是唯一的,所以第2次registerAccount()要出现异常。

也就是在第2次执行registerAccount()时

{
		accountDao.insert(a);
		accountProfileDao.insert(ap);  // 这要抛出异常		
}

<p>

但前面的 accountDao.insert(a) 无法回滚呢?

数据库结果:account有2条记录,id不同,但name相同, accountprofile有1条记录.

数据库不一致, 事务没起到作用。

lemon_zc1949
2005-09-28 15:18
Spring的事务有2种方式:

1。编程的方式,使用TransactionTemplate的execute方法。

2。AOP方式,通过配置来启动事务。

我上面的方法是采用AOP方式。

还有,我试验做过,如果使用相同的DataSource,调用它的getConnection(),那么获取的Connection(连接池封装了的)的hashCode都不一样,也就是说Connection是不一样。注射给 2个dao以及TransactionManager的DataSource是同意个DataSource(单列),单他们在使用DataSource的时候可能使用的Connection是不同的,所以无法实现事务混滚, 不知道是不是这样?

有点郁闷。

banq
2005-09-28 21:29
如果你不是MySQL 5.0,你需要指定Mysql的事务机制是INNO DB。

你在JBoss下试验看看。

banq
2005-09-29 10:45
Spring只提供使用JTA JMS等方便使用方式,但是不提供这些服务,必须由容器提供,你需要使用JBoss.

浆糊
2005-09-29 13:25
拜托先确保数据库的事务能力好伐?

0000ps
2008-01-29 18:12
我也做了个类似的例子, 只用jdbc 和 spring , 没有用hibernate和任何应用服务器....

结果也是控制不了transaction, 不过从log4j的输出看,

的确该rollback的exception是有rollback, 该commit的exception也是有commit. 貌似spring rollback和commit的connection 和我用来做DB操作的connection是两个实例 .... ()

[org.springframework.transaction.interceptor.TransactionInterceptor] - Completing transaction for <b>[dream.service.JdbcService.insertTTRange] after exception: dream.exception.ToRollbackException

[org.springframework.transaction.interceptor.RuleBasedTransactionAttribute] - Applying rules to determine whether transaction should rollback on dream.exception.ToRollbackException

[org.springframework.transaction.interceptor.RuleBasedTransactionAttribute] - Winning rollback rule is: RollbackRuleAttribute with pattern [ToRollbackException]

[org.springframework.jdbc.datasource.DataSourceTransactionManager] - Triggering beforeCompletion synchronization

[org.springframework.jdbc.datasource.DataSourceTransactionManager] - Initiating transaction rollback

[org.springframework.jdbc.datasource.DataSourceTransactionManager] - Rolling back JDBC transaction on Connection </b>[jdbc:microsoft:sqlserver://AspenServer:1433;NETADDRESS=000000000000;HOSTPROCESS=0;SENDSTRINGPARAMETERSASUNICODE=true;LOGINTIMEOUT=0;DATABASENAME=Test;PROGRAMNAME=;SELECTMETHOD=direct;WSID=, UserName=sa, SQLServer]

[org.springframework.jdbc.datasource.DataSourceTransactionManager] - Triggering afterCompletion synchronization

[org.springframework.transaction.support.TransactionSynchronizationManager] - Clearing transaction synchronization

[org.springframework.transaction.support.TransactionSynchronizationManager] - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@1dacccc] for key [org.apache.commons.dbcp.BasicDataSource@e41bc3] from thread [main]

[org.springframework.jdbc.datasource.DataSourceTransactionManager] - Releasing JDBC Connection [jdbc:microsoft:sqlserver://AspenServer:1433;NETADDRESS=000000000000;HOSTPROCESS=0;SENDSTRINGPARAMETERSASUNICODE=true;LOGINTIMEOUT=0;DATABASENAME=Test;PROGRAMNAME=;SELECTMETHOD=direct;WSID=, UserName=sa, SQLServer] after transaction

[org.springframework.jdbc.datasource.DataSourceUtils] - Returning JDBC Connection to DataSource

[该贴被0000ps于2008-01-29 18:13修改过]

banq
2008-01-30 16:57
Spring引入显式事务表面上给使用者带来可配置方便,太实际上引入了复杂性,Spring+Hibernate必须强制配置事务,否则无法运行,这些从一个侧面看事务和线程一样不是那么好驾驭的,这也是EJB将事务封装到容器一个原因之一,Spring打破这个盒子,曾经被一些盲目跟从者叫好,同时他们必须面对潘多拉盒子打开的后果。

现在Grails又试图将Spring+Hibernate及其它打开的潘多拉盒子冒出的魔鬼一起再封装到一个新的盒子里..

唉,,分久必和 合久必分。螺旋式上升?

[该贴被banq于2008-01-30 17:14修改过]

0000ps
2008-01-31 15:34
sorry, 我收回我说的..

spring在纯jdbc也可以控制事务,

不过dataSource要以注入的方式获取..

不然spring最后commit 或rollback的connection, 和数据操作的connection是不同的object, 这样就会造成spring没有在控制transaction的假象

water1735
2010-02-26 22:44
public static void main(String[] args) { FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("beans.xml"); AccountService service = (AccountService) context.getBean("accountservice"); Account a = new Account(); a.setId("U001"); a.setName("zc"); a.setPhone("123"); AccountProfile ap = new AccountProfile(); ap.setName(a.getName()); // "zc" ap.setPassword("abcdef"); service.registerAccount(a,ap); a = new Account(); a.setId("U002"); a.setName("zc"); a.setPhone("123"); ap = new AccountProfile(); ap.setName(a.getName()); // "zc" ap.setPassword("12345"); service.registerAccount(a,ap); context.close(); }

water1735
2010-02-26 22:52
楼主说的是两个事务,service.registerAccount(a,ap)执行了两次.这里是两个分开的事务.第一次执行当然没问题,事务成功.第二次会因为因为主键冲突出错,当然你不能期望第一次的数据能回滚.

如果你这样测试就能满足你的要求了:

public void registerAccount(Account a,AccountProfile ap){

accountDao.insert(a);

accountProfileDao.insert(ap);

accountDao.insert(a);

accountProfileDao.insert(ap);

}

那么一条数据都不会插入成功.

猜你喜欢