spring 事务

事务基础

事务的概念

  • 事务指数据库中多个操作合并在一起形成的操作序列
  • 由多个操作组成的一个逻辑单元,组成这个逻辑单元的多个操作要么都成功要么都失败

事务的特性

  • 原子性(Atomicity)

    • 指事务是一个不可分割的整体,其中的操作要么全执行或全不执行
  • 一致性(Consistency)

    • 事务前后数据的完整性必须保持一致
  • 隔离性(Isolation)

    • 事务的隔离性是多个用户并发访问数据库时,
    • 数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,
    • 多个并发事务之间要相互隔离
  • 持久性(Durability)

    • 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,
    • 接下来即使数据库发生故障也不应该对其有任何影响

名词解释

  • 脏读
    • 允许读取未提交的信息
    • 解决方案:表级读锁
  • 不可重复读
    • 读取过程中单个数据发生了变化
    • 解决方案:行级写锁
  • 幻读
    • 读取过程中数据条目发生了变化
    • 解决方案:表级写锁

隔离级别

  • Read uncommitted
    • 存在:脏读、不可重复读、幻读
  • Read committed
    • 存在:不可重复读、幻读
    • 解决:脏读
  • Repeatable read
    • 存在:幻读
    • 解决:脏读、不可重复读
  • Serializable
    • 解决:脏读、不可重复读、幻读

编程式事务控制

applicationContext.xml 配置

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:property-placeholder location="classpath:*.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="accountService" class="com.asaawan.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.asaawan.domain"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.asaawan.dao"/>
    </bean>

    <!-- aop 控制事务 -->

    <!-- 为 TxAdvice 注入数据源 -->
    <bean id="txAdvice" class="com.asaawan.aop.TxAdvice">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 为 transfer 方法添加事务控制 -->
    <aop:config>
        <aop:pointcut id="pt" expression="execution(* *..transfer(..))"/>
        <aop:aspect ref="txAdvice">
            <aop:around method="transactionManager" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>
</beans>

TxAdvice 配置

public class TxAdvice {

    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public Object transactionManager(ProceedingJoinPoint pjp) throws Throwable {
        // 开启事务 
        // 创建事务管理器并为事务管理器设置与数据层相同的数据源
        PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource);
        // 创建事务定义对象
        TransactionDefinition td = new DefaultTransactionDefinition();
        // 创建事务状态对象,用于控制事务执行
        TransactionStatus ts = ptm.getTransaction(td);
		// 执行切入点方法
        Object ret = pjp.proceed(pjp.getArgs());
        // 提交事务
        ptm.commit(ts);
        // 返回数据
        return ret;
    }
}

声明式事务控制

applicationContext.xml 配置

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:property-placeholder location="classpath:*.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.asaawan.domain"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.asaawan.dao"/>
    </bean>

    <bean id="accountService" class="com.asaawan.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!-- tx 控制事务 -->

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--定义事务管理的通知类-->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <!--定义控制的事务-->
        <tx:attributes>
            <tx:method
                name="transfer"
                read-only="false"
                timeout="-1"
                isolation="DEFAULT"
                no-rollback-for=""
                rollback-for=""
                propagation="REQUIRED"
                />
            <!--<tx:method name="transfer" read-only="false"/>-->
            <!--name="transfer"		待添加事务的方法名表达式(支持*号通配符),例如get*
			read-only="false"		设置事务的读写属性,true为只读,false为只写
			timeout="-1"			设置事务超时时长,单位秒
			isolation="DEFAULT" 	设置事务的隔离级别,该隔离级设定是基于Spring的设定,非数据库端
			no-rollback-for=""  	设置事务中不回滚的异常,多个异常间使用 ',' 分割
			rollback-for=""			设置事务中必回滚的异常,多个异常间使用 ',' 分割
			propagation="REQUIRED" 	设置事务的传播行为-->
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:pointcut id="pt" expression="execution(* com.asaawan.service.*Service.*(..))"/>
        <aop:pointcut id="pt2" expression="execution(* com.asaawan.dao.*.b(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt2"/>
    </aop:config>
</beans>

applicationContext.xml 注解

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:property-placeholder location="classpath:*.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.asaawan.domain"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.asaawan.dao"/>
    </bean>

    <bean id="accountService" class="com.asaawan.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

	<!-- tx 事务控制注解版 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
	<!-- 开启事务注解驱动 -->
    <tx:annotation-driven transaction-manager="txManager"/>
</beans>

AccountService 配置

//对当前接口的所有方法添加事务
//添加事务一般在 *Service 添加
@Transactional(isolation = Isolation.DEFAULT)
public interface AccountService {

    /**
     * 转账操作
     * @param outName     出账用户名
     * @param inName      入账用户名
     * @param money       转账金额
     */
    
    //对当前方法添加事务,该配置将替换接口的配置
    @Transactional(
        readOnly = false,
        timeout = -1,
        isolation = Isolation.DEFAULT,
        rollbackFor = {},   //java.lang.ArithmeticException.class, IOException.class
        noRollbackFor = {},
        propagation = Propagation.REQUIRED
        )
    public void transfer(String outName, String inName, Double money);
}

声明式事务控制纯注解

TxManagerConfig 注解

public class TxManagerConfig {
    @Bean
    public PlatformTransactionManager getTransactionManager(@Autowired DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
}

SpringConfiguration 注解

@Configuration
@ComponentScan("com.asaawan")
@PropertySource(value = {"classpath:jdbc.properties"},ignoreResourceNotFound = true)
@Import({JdbcConfiguration.class, MyBatisConfiguration.class})
@EnableTransactionManagement
public class SpringConfiguration {
}

aop:advice 与 aop:advisor

aop:advice

  • 配置的通知类可以是普通 java 对象,不实现接口,也不使用继承关系

aop:advisor

  • 配置的通知类必须实现通知接口
  • MethodBeforeAdvice
  • AfterReturningAdvice
  • ThrowsAdvice
  • ...

事务的传播行为

概念

  • 指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行
spring 事务

常见取值

spring 事务
上一篇:Springboot mysql链接设置


下一篇:Spring常用的注解(绝对童叟无欺)