Spring事务

一、事务认识

  事务Transaction就是一件事情,要做就要做完,如果中途出现问题就要恢复成最初状态。事务具备ACID四种特性,ACID是Atomic(原子性)、Consistency(一致性)、Isolation(隔离性)和Durability(持久性)的英文缩写:

  • 原子性:事务最基本的操作单元,要么全部成功,要么全部失败,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样
  • 一致性:事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态
  • 隔离性:对同一条数据的操作,不同的事务之间应该是隔离的
  • 持久性:只要事务成功结束,它对数据库所做的更新就必须永久保存下来

事务的传播特性

  事务传播行为就是多个事务方法调用时,如何定义方法间事务的传播,spring定义了七个传播级别:

  • propagation_required:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是Spring默认的选择。
  • propagation_support:支持当前事务,如果没有事务就以非事务的方式运行
  • propagation_mandatory:使用当前事务,如果当前没有事务就排除异常
  • propagation_required_new:新建一个事务,如果当前存在事务,就把当前事务挂起
  • propagation_not_supported:支持非事务,如果当前存在事务,就把当前事务挂起
  • propagation_never:以非事务的方式运行,如果当前存在事务,就抛出异常
  • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作。

事务的隔离级别

  • 读未提交:是最低的事务隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。
  • 读已提交:保证一个事物提交后才能被另外一个事务读取。另外一个事务不能读取该事物未提交的数据。
  • 可重复读:这种事务隔离级别可以防止脏读,不可重复读。但是可能会出现幻象读。它除了保证一个事务不能被另外一个事务读取未提交的数据之外还避免了以下情况产生(不可重复读)。
  • 序列化:这是花费最高代价但最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读之外,还避免了幻象读。

事务导致结果

  • 脏读:指当一个事务正字访问数据,并且对数据进行了修改,而这种数据还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
  • 幻读:一个事务先后读取一个范围的记录,但两次读取的纪录数不同,我们称之为幻象读。侧重于修改
  • 不可重复读:指在一个事务内,多次读同一数据。在这个事务还没有执行结束,另外一个事务也访问该同一数据,那么在第一个事务中的两次读取数据之间,由于第二个事务的修改第一个事务两次读到的数据可能是不一样的,这样就发生了在一个事物内两次连续读到的数据是不一样的,这种情况被称为是不可重复读。侧重于新增或删除

事务的实现方式

  • 编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。
  • 基于 TransactionProxyFactoryBean的声明式事务管理
  • 基于 @Transactional 的声明式事务管理
  • 基于Aspectj AOP配置事务

二、事务实现

基于 TransactionProxyFactoryBean实现

package Transaction.Entity;
/*
    账户对象
 */
public class Account {
    //账号ID
    private int accountid;
    //账号名称
    private String name;
    //金额
    private String 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 String getBalance() {
        return balance;
    }

    public void setBalance(String balance) {
        this.balance = balance;
    }
}
package Transaction.Entity;

/*
    股票对象
 */
public class Stock {

    //股票ID
    private int stockid;
    //股票名称
    private String name;
    //股数
    private Integer 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;
    }
}
package Transaction.Entity;
/*
    自定义异常
 */
public class BuyStockException extends Exception{

    public BuyStockException() {
        super();
    }

    public BuyStockException(String message) {
        super(message);
    }
}
package Transaction.Dao;

public interface AccountDao {
    void addAccount(String name,double money);

    void updateAccount(String name,double money,boolean isbuy);
}
package Transaction.Dao;

import Transaction.Dao.AccountDao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

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);
    }
}
package Transaction.Dao;

public interface StockDao {
    void addStock(String sname,int count);

    void updateStock(String sname,int count,boolean isbuy);

}
package Transaction.Dao;

import Transaction.Dao.StockDao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

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);
    }
}
package Transaction.Service;

import Transaction.Entity.BuyStockException;

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;
}
package Transaction.Service;

import Transaction.Entity.BuyStockException;
import Transaction.Dao.AccountDao;
import Transaction.Dao.StockDao;
import org.springframework.transaction.annotation.Transactional;

public class BuyStockServiceImpl implements BuyStockService {

    private AccountDao accountDao;

    private StockDao stockDao;

    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;
    }

    @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);
    }
}
<?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: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/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="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl"  value="jdbc:mysql://localhost:3306"></property>
        <property name="user"  value="root"></property>
        <property name="password" value="123456"></property>
    </bean>

    <bean id="accountDao" class="Transaction.Dao.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="stockDao" class="Transaction.Dao.StockDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="buyStockService" class="Transaction.Service.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>   <!-- 植入service -->
        <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>
package Transaction;

import Transaction.Service.BuyStockService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        String resouce = "TransactionProxyFactoryBean.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(resouce);
        BuyStockService buyStockService =  (BuyStockService) applicationContext.getBean("serviceProxy");
        buyStockService.addAccount("小郑", 5000);
        buyStockService.addStock("知晓科技", 0);
        /*try{
            buyStockService.buyStock("小郑", 1000, "知晓科技", 100);
        }catch (Exception e){
            e.printStackTrace();
        }*/
    }
}

基于 @Transactional实现

package Transaction.Service;

import Transaction.Entity.BuyStockException;
import Transaction.Dao.AccountDao;
import Transaction.Dao.StockDao;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

public class BuyStockServiceImpl implements BuyStockService {

    private AccountDao accountDao;

    private StockDao stockDao;

    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;
    }

    @Override
    @Transactional(isolation= Isolation.DEFAULT,propagation= Propagation.REQUIRED)
    public void addAccount(String accountname, double money) {
        accountDao.addAccount(accountname,money);
    }

    @Override
    @Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED)
    public void addStock(String stockname, int amount) {
        stockDao.addStock(stockname,amount);
    }

    @Override
    @Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED)
    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);
    }
}
<!-- 事务管理器 -->
    <bean id="myTracnsactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 启用事务注解 -->
    <tx:annotation-driven transaction-manager="myTracnsactionManager"/>

基于Aspectj AOP实现

<!-- 事务管理器 -->
    <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(* *..service.*.*(..))" id="point"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="point"/>
    </aop:config>
原文地址:https://www.cnblogs.com/yfstudy/p/13622072.html