Spring_Spring与DAO_Spring的事务管理
一、Spring的事务管理
在Spring中通常可以通过以下三种方式来实现对事务的管理:
- 使用Spring的事务代理工厂管理事务
- 使用Spring的事务注解管理事务
- 使用AspectJ的AOP配置管理事务
二、Spring事务管理API
(1)事务管理接口
1、PlatformTransactionManager接口有2个常用的实现类:
- DataSourceTransactionManager:使用JDBC或者iBatis进行持久化数据时使用;
- HibernateTransactionManager:使用Hibernate进行持久化数据时使用。
2、Spring的回滚方式
Spring事务的默认回滚方式是:发生运行时异常时回滚,发生受查异常时提交。不过对于受查异常,也可以手动设置其回滚方式。
(2)事务定义接口
A、5个事务隔离级别
B、定义了7个事务传播行为常量
- REQUIRE: 指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中,若当前没有事务,则创建一个新事务。这种传播行为也是最常见的选择,也是Spring默认的事务传播方式;
- SUPPORTS:指定的方法支持支持当前事务,但若当前没有事务,也可以以非事务方式执行;
- MANDATORY:指定的方法必须在当前事务内执行,若当前没有事务,则直接抛出异常;
- REQUIRES_NES:总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕;
- NOT_SUPPORTED:指定的方法不能在事务环境中执行,若当前存在事务,就将当前事务挂起‘
- NEVER:指定的方法不能在事务环境下执行,若当前存在事务,就直接抛出异常;
- NESTED:指定的方法必须在事务内执行,若当前存在事务,则在嵌套事务内执行;若当前没有事务,则创建一个新事务;
c、默认事务超时时限
三、程序举例环境搭建
购买股票举例
1、定义实体类及DB表
1 public class Account { 2 private Integer aid; 3 private String aname; 4 private double balance;// 余额 5 6 7 public Account() { 8 super(); 9 } 10 11 12 public Account(Integer aid, String aname, double balance) { 13 super(); 14 this.aid = aid; 15 this.aname = aname; 16 this.balance = balance; 17 } 18 19 20 public Integer getAid() { 21 return aid; 22 } 23 24 25 public void setAid(Integer aid) { 26 this.aid = aid; 27 } 28 29 30 public String getAname() { 31 return aname; 32 } 33 34 35 public void setAname(String aname) { 36 this.aname = aname; 37 } 38 39 40 public double getBalance() { 41 return balance; 42 } 43 44 45 public void setBalance(double balance) { 46 this.balance = balance; 47 } 48 49 50 @Override 51 public String toString() { 52 return "Account [aid=" + aid + ", aname=" + aname + ", balance=" + balance + "]"; 53 } 54 55 56 }
1 public class Stock { 2 private Integer sid; 3 private String sname;// 股票名称 4 private int count;// 股票数量 5 6 public Stock() { 7 super(); 8 } 9 10 public Stock(Integer sid, String sname, int count) { 11 super(); 12 this.sid = sid; 13 this.sname = sname; 14 this.count = count; 15 } 16 17 public Integer getSid() { 18 return sid; 19 } 20 21 public void setSid(Integer sid) { 22 this.sid = sid; 23 } 24 25 public String getSname() { 26 return sname; 27 } 28 29 public void setSname(String sname) { 30 this.sname = sname; 31 } 32 33 public int getCount() { 34 return count; 35 } 36 37 public void setCount(int count) { 38 this.count = count; 39 } 40 41 @Override 42 public String toString() { 43 return "Stock [sid=" + sid + ", sname=" + sname + ", count=" + count + "]"; 44 } 45 46 47 48 }
二、定义Service
1 public interface IBuyStockService { 2 void openAccount(String aname,double money);//开用户 3 void openStock(String sname,int amount); 4 5 void buyStock(String aname,double money,String sname,int amount) throws BuyStockException; 6 }
1 import com.jmu.dao.IAccountDao; 2 import com.jmu.dao.IStockDao; 3 4 public class BuyStockService implements IBuyStockService { 5 private IAccountDao adao; 6 private IStockDao sdao; 7 8 public void setAdao(IAccountDao adao) { 9 this.adao = adao; 10 } 11 12 public void setSdao(IStockDao sdao) { 13 this.sdao = sdao; 14 } 15 16 @Override 17 public void openAccount(String aname, double money) { 18 // TODO Auto-generated method stub 19 adao.insertAccount(aname, money); 20 } 21 22 @Override 23 public void openStock(String sname, int amount) { 24 // TODO Auto-generated method stub 25 sdao.insertStock(sname, amount); 26 } 27 28 @Override 29 public void buyStock(String aname, double money, String sname, int amount) throws BuyStockException { 30 // TODO Auto-generated method stub 31 boolean isBuy = true; 32 adao.updateAccount(aname, money, isBuy); 33 34 if (1 == 1) { 35 throw new BuyStockException("购买股票异常"); 36 } 37 sdao.updateStock(sname, amount, isBuy); 38 39 } 40 41 }
1 public class BuyStockException extends Exception { 2 3 public BuyStockException() { 4 super(); 5 // TODO Auto-generated constructor stub 6 } 7 8 public BuyStockException(String message) { 9 super(message); 10 // TODO Auto-generated constructor stub 11 } 12 13 }
三、定义Dao
1 public interface IAccountDao { 2 3 void insertAccount(String aname, double money); 4 void updateAccount(String aname, double money, boolean isBuy); 5 6 7 }
1 public interface IStockDao { 2 3 void insertStock(String sname, int amount); 4 void updateStock(String sname, int amount, boolean isBuy); 5 6 }
1 import org.springframework.jdbc.core.support.JdbcDaoSupport; 2 3 public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao { 4 5 @Override 6 public void insertAccount(String aname, double money) { 7 // TODO Auto-generated method stub 8 String sql = "insert into account(aname,balance) values(?,?)"; 9 this.getJdbcTemplate().update(sql, aname, money); 10 } 11 12 @Override 13 public void updateAccount(String aname, double money, boolean isBuy) { 14 String sql = "update account set balance=balance+? where aname=?"; 15 if (isBuy) { 16 // TODO Auto-generated method stub 17 sql = "update account set balance=balance-? where aname=?"; 18 } 19 this.getJdbcTemplate().update(sql, money,aname); 20 } 21 22 }
1 import org.springframework.jdbc.core.support.JdbcDaoSupport; 2 3 public class StockDaoImpl extends JdbcDaoSupport implements IStockDao { 4 5 @Override 6 public void insertStock(String sname, int amount) { 7 // TODO Auto-generated method stub 8 String sql="insert into stock(sname,count) values(?,?)"; 9 this.getJdbcTemplate().update(sql,sname,amount); 10 } 11 12 @Override 13 public void updateStock(String sname, int amount, boolean isBuy) { 14 // TODO Auto-generated method stub 15 String sql="update stock set count=count-? where sname=?"; 16 if (isBuy) { 17 sql="update stock set count=count+? where sname=?"; 18 } 19 this.getJdbcTemplate().update(sql,amount,sname); 20 }; 21 22 }
四、导入Jar包
项目结构
五、使用事务代理管理事务(方法一)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" 4 xsi:schemaLocation=" 5 http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/context 8 http://www.springframework.org/schema/context/spring-context.xsd"> 9 <!-- IoC --> 10 11 <!--注册数据源:C3P0 --> 12 <bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 13 <property name="driverClass" value="${jdbc.driver}" /> 14 <property name="jdbcUrl" value="${jdbc.url}" /> 15 <property name="user" value="${jdbc.user}" /> 16 <property name="password" value="${jdbc.password}" /> 17 </bean> 18 19 <!-- 注册属性文件 --> 20 <context:property-placeholder location="classpath:jdbc.properties"/> 21 <!--注册Dao --> 22 <bean id="accountDao" class="com.jmu.dao.AccountDaoImpl"> 23 <property name="dataSource" ref="myDataSource" /> 24 25 </bean> 26 <bean id="stockDao" class="com.jmu.dao.StockDaoImpl"> 27 <property name="dataSource" ref="myDataSource" /> 28 29 </bean> 30 31 <!-- 注册Service --> 32 <bean id="buyStockService" class="com.jmu.service.BuyStockService"> 33 <property name="adao" ref="accountDao" /> 34 <property name="sdao" ref="stockDao"/> 35 </bean> 36 37 38 <!-- AOP --> 39 <!-- 注册事务管理器 --> 40 <bean id="myTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 41 <property name="dataSource" ref="myDataSource"></property> 42 </bean> 43 44 <!-- 生成事务代理对象 --> 45 <bean id="serviceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> 46 <property name="transactionManager" ref="myTransactionManager"></property> 47 <property name="target" ref="buyStockService"></property> 48 <property name="transactionAttributes"> 49 <props> 50 <prop key="open*">ISOLATION_DEFAULT,PROPAGATION_REQUIRED</prop> 51 <prop key="buyStock">ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-BuyStockException </prop> 52 <!-- 53 -异常:表示发生指定异常后 回滚 (通常是受查异常) 54 +异常:表示发生指定异常后提交 (通常是运行时异常) 55 --> 56 </props> 57 </property> 58 </bean> 59 </beans>
-异常:表示发生指定异常后 回滚 (通常是受查异常)
+异常:表示发生指定异常后提交 (通常是运行时异常)
<!-- 生成事务代理对象 --> <bean id="serviceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="myTransactionManager"></property> <property name="target" ref="buyStockService"></property> <property name="transactionAttributes"> <props> <prop key="open*">ISOLATION_DEFAULT,PROPAGATION_REQUIRED</prop> <prop key="buyStock">ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-BuyStockException </prop> <!-- -异常:表示发生指定异常后 回滚 (通常是受查异常) +异常:表示发生指定异常后提交 (通常是运行时异常) --> </props> </property> </bean>
1 import org.junit.Before; 2 import org.junit.Test; 3 import org.springframework.context.ApplicationContext; 4 import org.springframework.context.support.ClassPathXmlApplicationContext; 5 6 import com.jmu.service.BuyStockException; 7 import com.jmu.service.IBuyStockService; 8 9 public class MyTest { 10 11 private IBuyStockService service; 12 13 @Before 14 public void before(){ 15 String resource="applicationContext.xml"; 16 ApplicationContext aContext=new ClassPathXmlApplicationContext(resource); 17 service=(IBuyStockService)aContext.getBean("serviceProxy"); 18 } 19 20 @Test 21 public void test01(){ 22 service.openAccount("张三", 10000); 23 service.openStock("腾讯", 0); 24 25 } 26 @Test 27 public void test02() throws BuyStockException{ 28 service.buyStock("张三", 2000, "腾讯", 5); 29 30 31 } 32 33 34 }
六、使用注解管理事务(方法二)
Spring配置文件约束
<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- AOP -->
<!-- 注册事务管理器 -->
<bean id="myTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="myDataSource"></property>
</bean>
<!-- 注册事务注解驱动 -->
<tx:annotation-driven transaction-manager="myTransactionManager"/>
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED) @Override public void openAccount(String aname, double money) { // TODO Auto-generated method stub adao.insertAccount(aname, money); } @Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED) @Override public void openStock(String sname, int amount) { // TODO Auto-generated method stub sdao.insertStock(sname, amount); } @Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED,rollbackFor=BuyStockException.class) @Override public void buyStock(String aname, double money, String sname, int amount) throws BuyStockException { // TODO Auto-generated method stub boolean isBuy = true; adao.updateAccount(aname, money, isBuy); if (1 == 1) { throw new BuyStockException("购买股票异常"); } sdao.updateStock(sname, amount, isBuy); }
输出结果:
七、使用AspectJ的AOP配置管理事务(重点)
导入Jar包
<!-- AOP --> <!-- 注册事务管理器 --> <bean id="myTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="myDataSource"></property> </bean> <tx:advice id="txAdvice" transaction-manager="myTransactionManager"> <!--事务属性 --> <!--将事务属性织入到方法中 --> <tx:attributes> <!--这里指定的是为每一个连接点指定所要应用的事务属性 --> <tx:method name="open*" isolation="DEFAULT" propagation="REQUIRED" /> <tx:method name="buyStock" isolation="DEFAULT" propagation="REQUIRED" rollback-for="BuyStockException" /> </tx:attributes> </tx:advice> <!-- AOP配置 --> <aop:config> <!--这里指定的是切入点 --> <!-- execution(* *..service.*.open*(..)) --> <aop:pointcut expression="execution(* *..service.*.*(..))" id="myPoint" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="myPoint" /> </aop:config>