Spring事务管理

@(Spring)[spring, 事务管理, 事务, Spring]

事务的基本概念

什么是事务

是逻辑上一组操作,组成这组操作各个单元要么一起成功要么一起失败。

事务的特性

  • 原子性 :不可分割性
  • 一致性 :事务执行前后,数据完整性保持一致。
  • 隔离性 :一个事务执行的时候,不应该受到其他事务的干扰。
  • 持久性 :事务一旦结束,数据就持久化到数据库。

事务的隔离性不完善引发的问题

  • 读问题
    • 脏读 :一个事务读到另一个事务未提交的数据。
    • 不可重复读 :一个事务读到另一个事务已经提交的update的数据,导致多次查询结果内容不一致。
    • 幻影读 :一个事务读到另一个事务已经提交的insert、delete的数据,导致多次查询结果集合不一致。
  • 写问题
    • 丢失更新

设置隔离级别解决读问题

  • read uncommitted :读未提交,三个读问题都有。
  • read committed :读已提交,解决脏读,但是不可重复读和幻影读有可能发生。
  • repeatable read :可重复读,解决脏读和不可重复读,但是幻影读有可能发生。
  • serializable :可串行化,解决所有读问题。

Spring的事务管理

在Spring中,由事务定义类TransactionDefinition完成事务信息的定义,由事务管理器PlatformTransactionManager根据事务定义信息进行事务的管理,在管理过程中生成事务的各种状态,将状态记录到事务状态对象TransactionStatus中。

Spring事务管理的API

这里写图片描述

  • PlatformTransactionManager :平台事务管理器,真正进行事务管理的对象。
    • DataSourceTransactionManager :底层使用JDBC的模板
    • HibernateTransactionManager :底层使用Hibernate
    • JpaTransactionManager

平台事务管理器接口有三个方法:

方法名方法定义注释
commitvoid commit(TransactionStatus status)根据事务状态提交事务
rollbackvoid rollback(TransactionStatus status)根据事务状态回滚事务
getTransactionTransactionStatus getTransaction(TransactionDefinition definition)通过事务定义获取事务状态
  • TransactionDefinition :事务的定义信息

    • 定义事务隔离级别
      • ISOLATION_DEFAULT :根据数据库默认隔离级别设置数据库隔离级别。(默认)
      • ISOLATION_READ_UNCOMMITTED :读未提交
      • ISOLATION_READ_COMMITTED :读已提交
      • ISOLATION_REPEATABLE_READ :可重复读
      • ISOLATION_SERIALIZABLE :可串行化
    • 定义事务传播行为
      • PROPAGATION_REQUIRED
      • PROPAGATION_SUPPORTS
      • PROPAGATION_MANDATORY
      • PROPAGATION_REQUIRES_NEW
      • PROPAGATION_NOT_SUPPORTED
      • PROPAGATION_NEVER
      • PROPAGATION_NESTED
    • 定义事务超时信息

      • TIMEOUT_DEFAULT:使用事务默认超时时间。
    • 通过以下方法可以获取事务定义信息

      • getIsolationLevel():获取事务隔离级别
      • getName():获取事务名称
      • getPropagationBehavior():获取事务传播行为
      • getTimeout():获取事务超时时间
      • isReadOnly():事务是否只读
  • TransactionStatus :事务的状态

    • isNewTransaction():是否是新事务
    • hasSavepoint():事务是否有保存点
    • isCompleted():事务是否已经完成
    • isRollbackOnly():事务是否是只能回滚
    • setRollbackOnly():设置事务为只能回滚

Spring的事务传播行为

事务的传播行为主要解决在复杂业务下的事务嵌套问题,比如说业务层之乐方法的相互调用。

spring事务的传播行为

  • 事务传播行为取值

    • 使用当前事务

      • PROPAGATION_REQUIRED默认值。如果A中有事务,使用A中的事务。如果A中没有事务,创建一个新的事务将A的内容包含进来。
      • PROPAGATION_SUPPORTS :如果A中有事务,使用A中的事务,如果A中没有事务,不使用事务。
      • PROPAGATION_MANDATORY :如果A中有事务,使用A中的事务,如果A中没有事务,抛异常。
    • 不使用当前事务

      • PROPAGATION_REQUIRES_NEW如果A中有事务,将A中的挂起,创建一个新的事务执行自身部分。如果A中没有事务,创建一个新事务,执行自身部分。
      • PROPAGATION_NOT_SUPPORTED :如果A中有事务,将A中事务挂起。以非事务的方式运行。
      • PROPAGATION_NEVER :如果A中有事务,就抛异常。
    • 使用嵌套事务

      • PROPAGATION_NESTED当A执行结束后,会设置一个保存点。如果B的部分没有错误,执行通过,如果B的部分出错,允许回滚到最初始也可以回滚到保存点。

Spring的事务管理案例环境搭建

Spring的事务管理的分类

  • 编程式事务管理:手动编写代码完成事务管理
  • 声明式事务管理:通过配置方式完成事务管理
    • XML方式
    • 注解方式

创建相关接口和类并配置注解

package com.pc.service;
/**
 * 账号服务接口
 * 
 * @author Switch
 * @data 2016年11月25日
 * @version V1.0
 */
public interface AccountService {
    /**
     * 转账
     * @param from 转账者
     * @param to 收账者
     * @param money 钱数
     */
    public void transfer(String from, String to, Double money);
}
package com.pc.service.impl;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.pc.dao.AccountDao;
import com.pc.service.AccountService;
/**
 * 账号服务接口实现类
 * @author Switch
 * @data 2016年11月25日
 * @version V1.0
 */
// 配置服务层注解
@Service("accountService")
public class AccountServiceImpl implements AccountService {
    /**
     * 注入账号持久层对象依赖
     */
    @Resource(name = "accountDao")
    private AccountDao accountDao;

    @Override
    public void transfer(String from, String to, Double money) {
        accountDao.transferFrom(from, money);
        // int i = 1 / 0;
        accountDao.transferTo(to, money);
    }
}
package com.pc.dao;
/**
 * 账号持久层接口
 * 
 * @author Switch
 * @data 2016年11月25日
 * @version V1.0
 */
public interface AccountDao {
    /**
     * 从哪个账户转账
     * @param from 转账者
     * @param money 钱数
     */
    void transferFrom(String from, Double money);

    /**
     * 转账到哪个账户
     * @param to 收账者
     * @param money 钱数
     */
    void transferTo(String to, Double money);
}
package com.pc.dao.impl;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import com.pc.dao.AccountDao;
/**
 * 账号持久层接口实行类
 * 
 * @author Switch
 * @data 2016年11月25日
 * @version V1.0
 */
// 配置持久层组件注解
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
    @Override
    public void transferFrom(String from, Double money) {
    }

    @Override
    public void transferTo(String to, Double money) {
    }
}

配置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: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.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 配置组件扫描 -->
    <context:component-scan base-package="com.pc.service.impl"/>

    <!-- 配置账号dao,并注入数据源 -->
    <bean id="accountDao" class="com.pc.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- spring配置c3p0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <!-- 引入外部属性文件 -->
    <context:property-placeholder location="classpath:db.properties"/>
</beans>

在DAO中使用JDBC的模板

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
    @Override
    public void transferFrom(String from, Double money) {
        super.getJdbcTemplate().update("update account set money = money - ? where name = ?", money, from);
    }

    @Override
    public void transferTo(String to, Double money) {
        super.getJdbcTemplate().update("update account set money = money + ? where name = ?", money, to);
    }
}

编写测试类

package com.pc.test;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.pc.service.AccountService;
/**
 * 测试事务
 * 
 * @author Switch
 * @data 2016年11月25日
 * @version V1.0
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTX {
    @Resource(name = "accountService")
    AccountService accountService;

    @Test
    public void test1() {
        accountService.transfer("Switch", "Kity", 1000d);
    }
}

Spring的声明式事务管理

声明式的事务底层都是基于AOP的。

一般开发种常用的是XML方式的声明式事务,但有时候为了简单也会使用注解方式的事务。

引入jar包
这里写图片描述

XML方式的声明式事务

配置事务管理器
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>
配置一个事务通知
<!-- 基于XML的事务配置 -->
<!-- 配置事务通知 -->
<tx:advice id="txAdvice">
    <tx:attributes>
        <!-- 配置事务定义 -->
        <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>
配置AOP
<!-- 事务aop配置 -->
<aop:config>
    <aop:pointcut expression="execution(* com.pc.service.impl.AccountServiceImpl.transfer(..))" id="pointcut1"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
</aop:config>
tx:method的配置
  • name:配置的方法名称,支持*通配符匹配
  • propagation:事务传播行为
  • isolation:事务隔离级别
  • timeout:超时时间
  • read-only:是否只读事务
  • rollback-for:触发回滚的异常,逗号分隔
  • no-rollback-for:不触发回滚的异常,逗号分隔

注解方式的声明式事务

配置事务管理器
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
</bean>
开启注解事务
<!-- 开启注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
在业务层添加一个事务的注解
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED)
public class AccountServiceImpl implements AccountService {
    ......
}

PS:事务的注解加在类上表示该类所有方法都被事务管理器管理,加在方法上,则只有该方法被事务管理器管理。

@Transactional的配置
  • value:使用的TransactionManager
  • propagation:事务传播行为
  • isolation:事务隔离级别
  • timeout:超时
  • readOnly:是否只读事务
  • rollbackFor:触发回滚的异常类对象数组
  • rollbackForClassName:出发回滚的异常类名称数组
  • noRollbackFor:不触发回滚的异常类对象数组
  • noRollbackForClassName:不触发回滚的异常类名称数组

Spring的编程式事务管理

编程式事务基本不用,这里只是基本介绍

配置事务管理器

<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
</bean>

配置事务管理的模板(简化事务开发)

<!-- 配置事务管理模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="transactionManager"/>
</bean>

在业务层注入事务管理的模板

@Resource(name = "transactionTemplate")
private TransactionTemplate transactionTemplate;

进行事务管理

package com.pc.service.impl;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.pc.dao.AccountDao;
import com.pc.service.AccountService;
/**
 * 账号服务接口实现类
 * @author Switch
 * @data 2016年11月25日
 * @version V1.0
 */
// 配置服务层注解
@Service("accountService")
// @Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED)
public class AccountServiceImpl implements AccountService {
    /**
     * 注入账号持久层对象依赖
     */
    @Resource(name = "accountDao")
    private AccountDao accountDao;
    @Resource(name = "transactionTemplate")
    private TransactionTemplate transactionTemplate;

    @Override
    public void transfer(final String from, final String to, final Double money) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus arg0) {
                accountDao.transferFrom(from, money);
                int i = 1 / 0;
                accountDao.transferTo(to, money);
            }
        });
    }
}

TransactionTemplate的使用

标准用法
public Object transfer(final String from, final String to, final Double money) {
    return transactionTemplate.execute(new TransactionCallback() {
        @Override
        public Object doInTransaction(TransactionStatus arg0) {
            accountDao.transferFrom(from, money);
            int i = 1 / 0;
            accountDao.transferTo(to, money);
            return accountDao.toString();
        }
    });
}
没返回值用法
@Override
public void transfer(final String from, final String to, final Double money) {
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus arg0) {
            accountDao.transferFrom(from, money);
            // int i = 1 / 0;
            accountDao.transferTo(to, money);
        }
    });
}
异常处理
public void transfer(final String from, final String to, final Double money) {
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus arg0) {
            try {
                accountDao.transferFrom(from, money);
                // int i = 1 / 0;
                accountDao.transferTo(to, money);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

案例

GitHub:SpringDataTest
GitHub:MyStore-netease

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐