你们所了解的事务Transaction,它是一些列严密操做动做,要么都操做完成,要么都回滚撤销。Spring事务管理基于底层数据库自己的事务处理机制。数据库事务的基础,是掌握Spring事务管理的基础。这篇总结下Spring事务。 事务具有ACID四种特性,ACID是Atomic(原子性)、Consistency(一致性)、Isolation(隔离性)和Durability(持久性)的英文缩写。spring
事务最基本的操做单元,要么所有成功,要么所有失败,不会结束在中间某个环节。事务在执行过程当中发生错误,会被回滚到事务开始前的状态,就像这个事务历来没有执行过同样。sql
事务的一致性指的是在一个事务执行以前和执行以后数据库都必须处于一致性状态。若是事务成功地完成,那么系统中全部变化将正确地应用,系统处于有效状态。若是在事务中出现错误,那么系统中的全部变化将自动地回滚,系统返回到原始状态。数据库
指的是在并发环境中,当不一样的事务同时操纵相同的数据时,每一个事务都有各自的完整数据空间。由并发事务所作的修改必须与任何其余并发事务所作的修改隔离。事务查看数据更新时,数据所处的状态要么是另外一事务修改它以前的状态,要么是另外一事务修改它以后的状态,事务不会查看到中间状态的数据。express
指的是只要事务成功结束,它对数据库所作的更新就必须永久保存下来。即便发生系统崩溃,从新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。编程
事务传播行为就是多个事务方法调用时,如何定义方法间事务的传播。Spring定义了7中传播行为:spring-mvc
编程式事务基本已经OUT了,全部就省略了,主要回顾 下声明式事务。 以用户购买股票为例 新建用户对象、股票对象、以及dao、service层bash
/**
* 帐户对象
*
*/
public class Account {
private int accountid;
private String name;
private int balance;
public int getAccountid() {
return accountid;
}
public void setAccountid(int accountid) {
this.accountid = accountid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
}
复制代码
/**
* 股票对象
*
*/
public class Stock {
private int stockid;
private String name;
private Integer count;
public Stock() {
super();
}
public Stock(int stockid, String name, Integer count) {
super();
this.stockid = stockid;
this.name = name;
this.count = count;
}
public int getStockid() {
return stockid;
}
public void setStockid(int stockid) {
this.stockid = stockid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
}
复制代码
DAO层并发
public interface AccountDao {
void addAccount(String name,double money);
void updateAccount(String name,double money,boolean isbuy);
}
复制代码
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Override
public void addAccount(String name, double money) {
String sql = "insert account(name,balance) values(?,?);";
this.getJdbcTemplate().update(sql,name,money);
}
@Override
public void updateAccount(String name, double money, boolean isbuy) {
String sql = "update account set balance=balance+? where name=?";
if(isbuy)
sql = "update account set balance=balance-? where name=?";
this.getJdbcTemplate().update(sql, money,name);
}
}
复制代码
public interface StockDao {
void addStock(String sname,int count);
void updateStock(String sname,int count,boolean isbuy);
}
public class StockDaoImpl extends JdbcDaoSupport implements StockDao {
@Override
public void addStock(String sname, int count) {
String sql = "insert into stock(name,count) values(?,?)";
this.getJdbcTemplate().update(sql,sname,count);
}
@Override
public void updateStock(String sname, int count, boolean isbuy) {
String sql = "update stock set count = count-? where name = ?";
if(isbuy)
sql = "update stock set count = count+? where name = ?";
this.getJdbcTemplate().update(sql, count,sname);
}
}
复制代码
Servicemvc
public interface BuyStockService {
public void addAccount(String accountname, double money);
public void addStock(String stockname, int amount);
public void buyStock(String accountname, double money, String stockname, int amount) throws BuyStockException;
}
复制代码
public class BuyStockServiceImpl implements BuyStockService{
private AccountDao accountDao;
private StockDao stockDao;
@Override
public void addAccount(String accountname, double money) {
accountDao.addAccount(accountname,money);
}
@Override
public void addStock(String stockname, int amount) {
stockDao.addStock(stockname,amount);
}
@Override
public void buyStock(String accountname, double money, String stockname, int amount) throws BuyStockException {
boolean isBuy = true;
accountDao.updateAccount(accountname, money, isBuy);
if(isBuy==true){
throw new BuyStockException("购买股票发生异常");
}
stockDao.updateStock(stockname, amount, isBuy);
}
public AccountDao getAccountDao() {
return accountDao;
}
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public StockDao getStockDao() {
return stockDao;
}
public void setStockDao(StockDao stockDao) {
this.stockDao = stockDao;
}
}
复制代码
自定义异常类ide
public class BuyStockException extends Exception {
public BuyStockException() {
super();
}
public BuyStockException(String message) {
super(message);
}
}
复制代码
(1)基于 TransactionProxyFactoryBean的声明式事务管理
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-aop-4.2.xsd ">
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 注册数据源 C3P0 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="accountDao" class="com.zwd.spring.transaction.daoImpl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="stockDao" class="com.zwd.spring.transaction.daoImpl.StockDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="buyStockService" class="com.zwd.spring.transaction.serviceImpl.BuyStockServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<property name="stockDao" ref="stockDao"></property>
</bean>
<!-- 事务管理器 -->
<bean id="myTracnsactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 事务代理工厂 -->
<!-- 生成事务代理对象 -->
<bean id="serviceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="myTracnsactionManager"></property>
<property name="target" ref="buyStockService"></property>
<property name="transactionAttributes">
<props>
<!-- 主要 key 是方法
ISOLATION_DEFAULT 事务的隔离级别
PROPAGATION_REQUIRED 传播行为
-->
<prop key="add*">ISOLATION_DEFAULT,PROPAGATION_REQUIRED</prop>
<!-- -Exception 表示发生指定异常回滚,+Exception 表示发生指定异常提交 -->
<prop key="buyStock">ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-BuyStockException</prop>
</props>
</property>
</bean>
</beans>
复制代码
经过结果和观察数据库数据变化,能够看出咱们声明的异常回滚发生了效果。
(2)基于 @Transactional 的声明式事务管理
其余类不作改变,只改变购买股票接口实现类和配置文件
public class BuyStockServiceImpl implements BuyStockService{
private AccountDao accountDao;
private StockDao stockDao;
@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED)
@Override
public void addAccount(String accountname, double money) {
accountDao.addAccount(accountname,money);
}
@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED)
@Override
public void addStock(String stockname, int amount) {
stockDao.addStock(stockname,amount);
}
public BuyStockServiceImpl() {
// TODO Auto-generated constructor stub
}
@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,rollbackFor=BuyStockException.class)
@Override
public void buyStock(String accountname, double money, String stockname, int amount) throws BuyStockException {
boolean isBuy = true;
accountDao.updateAccount(accountname, money, isBuy);
if(isBuy==true){
throw new BuyStockException("购买股票发生异常");
}
stockDao.updateStock(stockname, amount, isBuy);
}
public AccountDao getAccountDao() {
return accountDao;
}
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public StockDao getStockDao() {
return stockDao;
}
public void setStockDao(StockDao stockDao) {
this.stockDao = stockDao;
}
}
复制代码
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 注册数据源 C3P0 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="accountDao" class="com.zwd.spring.transaction.daoImpl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="stockDao" class="com.zwd.spring.transaction.daoImpl.StockDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="buyStockService" class="tcom.zwd.spring.transaction.serviceImpl.BuyStockServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<property name="stockDao" ref="stockDao"></property>
</bean>
<!-- 事务管理器 -->
<bean id="myTracnsactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 启用事务注解 -->
<tx:annotation-driven transaction-manager="myTracnsactionManager"/>
复制代码
能够看出,使用@Transactional注解的方式配置文件要简单的多,将事务交给事务注解驱动。它有个缺陷是他会把全部的链接点都做为切点将事务织入进去, 显然只须要在buyStock()方法织入事务便可。下面看看最后一种实现,它就能够精准的织入到指定的链接点
(3)基于Aspectj AOP配置事务
public class BuyStockServiceImpl implements BuyStockService{
private AccountDao accountDao;
private StockDao stockDao;
@Override
public void addAccount(String accountname, double money) {
accountDao.addAccount(accountname,money);
}
@Override
public void addStock(String stockname, int amount) {
stockDao.addStock(stockname,amount);
}
public BuyStockServiceImpl() {
// TODO Auto-generated constructor stub
}
@Override
public void buyStock(String accountname, double money, String stockname, int amount) throws BuyStockException {
boolean isBuy = true;
accountDao.updateAccount(accountname, money, isBuy);
if(isBuy==true){
throw new BuyStockException("购买股票发生异常");
}
stockDao.updateStock(stockname, amount, isBuy);
}
public AccountDao getAccountDao() {
return accountDao;
}
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public StockDao getStockDao() {
return stockDao;
}
public void setStockDao(StockDao stockDao) {
this.stockDao = stockDao;
}
}
复制代码
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 注册数据源 C3P0 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="accountDao" class="com.zwd.spring.transaction.daoImpl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="stockDao" class="com.zwd.spring.transaction.daoImpl.StockDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="buyStockService" class="com.zwd.spring.transaction.serviceImpl.BuyStockServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<property name="stockDao" ref="stockDao"></property>
</bean>
<!-- 事务管理器 -->
<bean id="myTracnsactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="txAdvice" transaction-manager="myTracnsactionManager">
<tx:attributes>
<!-- 为链接点指定事务属性 -->
<tx:method name="add*" isolation="DEFAULT" propagation="REQUIRED"/>
<tx:method name="buyStock" isolation="DEFAULT" propagation="REQUIRED" rollback-for="BuyStockException"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 切入点配置 -->
<aop:pointcut expression="execution(* *..serviceImpl.*.*(..))" id="point"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="point"/>
</aop:config>
复制代码