1.导包
2.目标类
首先是dao
package com.kye.dao; public interface AccountDao { void add(double money); int get(); } package com.kye.daoImpl; import org.springframework.jdbc.core.support.JdbcDaoSupport; import com.kye.dao.AccountDao; public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { @Override public void add(double money) { this.getJdbcTemplate().update("update tb_account set money=money+?", money); } @Override public int get() { int result = this.getJdbcTemplate().queryForInt("select money from tb_account"); return result; } }
然后是service
package com.kye.service; public interface AccountService { void transfer(int monehy); int get(); } package com.kye.serviceImpl; import com.kye.dao.AccountDao; import com.kye.service.AccountService; public class AccountServiceImpl implements AccountService { AccountDao dao; public void setDao(AccountDao dao) { this.dao = dao; } @Override public void transfer(int monehy) { dao.add(monehy); // int a = 1 / 0; if (1 == 1) throw new RuntimeException(); dao.add(monehy); } @Override public int get() { return dao.get(); } }
在transfer中,手动抛出运行时异常。
配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- <bean id="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1/db"></property> <property name="user" value="root"></property> <property name="password" value="1234"></property> </bean> --> <bean id="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"></property> <property name="jdbcUrl" value="jdbc:sqlserver://127.0.0.1:1433;DatabaseName=db"></property> <property name="user" value="sa"></property> <property name="password" value="sa123456"></property> </bean> <bean id="accountDao" class="com.kye.daoImpl.AccountDaoImpl"> <property name="dataSource" ref="c3p0"></property> </bean> <bean id="service" class="com.kye.serviceImpl.AccountServiceImpl"> <property name="dao" ref="accountDao"></property> </bean> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="c3p0"></property> </bean> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT" rollback-for="Exception"/> </tx:attributes> </tx:advice> <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.kye.serviceImpl.AccountServiceImpl.*(..))" /> </aop:config> </beans>
最后junit测试
package com.kye.test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.kye.service.AccountService; public class MyTest { @Test public void Test() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); AccountService service = context.getBean("service", AccountService.class); service.transfer(1); int result = service.get(); System.out.println("result:" + result); } }
总结
在demo过程中,遇到了一个问题。就是在transfer中,最开始我是手动抛出Exception,结果导致事物不会滚。后来查看spring的运行机制才发现, 默认spring事务只在发生未被捕获的 runtimeexcetpion时才回滚。
Spring aop异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeexception的异常,但可以通过配置来捕获特定的异常并回滚。换句话说在service的方法中不使用try catch 或者在catch中最后加上抛出运行时异常(RuntimeException),这样程序异常时才能被aop捕获进而回滚。
解决方案:
1)例如service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加throw new RuntimeException()语句,以便让aop捕获异常再去回滚,并且在service上层(webservice客户端,view层action)要继续捕获这个异常并处理
2)在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常
3)在事物管理器中,指定某些异常也回滚事物。例如:<tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT" rollback-for="MyException,ServletException,Exception"/>
遗留问题
使用mysql数据库无法回滚,同时也保证数据库是可以使用事物的,如下图
换成sqlserver就没问题了,找了几个做Java多年的朋友,也没解决此问题-_-||