Spring二刷笔记-事务(Transaction)概念理解与实现

Posted 滑稽404#

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring二刷笔记-事务(Transaction)概念理解与实现相关的知识,希望对你有一定的参考价值。

一、事务概念

1.什么是事务

事务是数据库操作的基本单元,要么都成功,要么都失败

2.事务的四个特性(ACID)

原子性(Atomicity)

原子性是指事务是不可再分,事务中的操作要么都发生,要么都不发生。

一致性(Consistency)

事务前后数据的完整性必须保持一致。

隔离性(Isolation)

多个事务同时进行,互相没有影响

持久性(Durability)

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

二、事务场景

银行转账,jack要转给amy100元,jack减少100元,amy增加100元

  • dao层有减少钱和增加钱的具体数据操作方法
  • service层要根据具体业务使用dao层的方法,如:转账业务,该业务不仅需要减少钱方法,也需要增加钱方法,而且还要通过事务特性保证数据一致性

dao层

@Repository
public class AccountMapper implements AccountDao {
    @Autowired
    JdbcTemplate jdbcTemplate;
    public void addMoney(String id,int money) {
        String sql="update account set money=money+? where id=?";
        int update=jdbcTemplate.update(sql,money,id);
        //System.out.println(update);
    }

    public void reduceMoney(String id,int money) {
        String sql="update account set money=money-? where id=?";
        int update=jdbcTemplate.update(sql,money,id);
        //System.out.println(update);
    }
}

service层

@Service("accountService")
public class AccountServiceImpl implements AccountService {

    @Autowired
    AccountDao accountDao;
    //转账
    public void accountMoney(String reduceId,String addId,int money) {
        accountDao.reduceMoney(reduceId,money);
        System.out.println(reduceId+"转出"+money+"元");
        accountDao.addMoney(addId,money);
        System.out.println(addId+"收到"+money+"元");

        System.out.println(reduceId+"转给"+addId+money+"元");
    }
}

转账

 ApplicationContext context=new ClassPathXmlApplicationContext("bean3.xml");
 AccountService accountService = context.getBean("accountService", AccountService.class);
     accountService.accountMoney("1","2",100);//1向2转账100

你会发现执行后数据正常,好像并没有什么错误
看下面这个场景,jack转钱时,金额已经扣了,但是突然停电了(异常中断),钱并没有到amy账上

 //转账
    public void accountMoney(String reduceId,String addId,int money) {
        accountDao.reduceMoney(reduceId,money);
        System.out.println(reduceId+"转出"+money+"元");

        //模拟异常
        int a=2/0;

        accountDao.addMoney(addId,money);
        System.out.println(addId+"收到"+money+"元");

        System.out.println(reduceId+"转给"+addId+money+"元");
    }

然后这种情况,就会导致数据不一致,产生错误,所以需要用到事务操作

事务流程

try{
	//1、开启事务
	//2、具体业务
	//3、没有异常,提交事务
}catch(Exception e){
	//4、捕获到异常,事务回滚(回到事务执行前的状态)
}

三、事务操作(事务管理)

将事务添加到service层(业务逻辑层),一般事务与具体业务结合起来使用

1.Spring进行事务管理操作

  1. 编程式事务管理,就是上述的事务流程,这种方式要在每个业务里写大量事务代码,过于繁琐,不推荐使用
  2. 声明式事务管理

2.声明式事务

Spring实现声明式事务,底层是AOP实现

  1. 基于注解实现
  2. xml配置文件实现
<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: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">

        <context:component-scan base-package="com.chime"/>
        <context:property-placeholder location="classpath:jdbc.properties"/>

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

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

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

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

3.注解实现

(1)添加tx命名空间

(2)开启事务管理器

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

(3)开启事务注解

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

将注解添加到service类上或者具体方法上
添加到类上表示,类中所有方法开启事务操作

@Service("accountService")
@Transactional//开启事务
public class AccountServiceImpl implements AccountService

3.注解参数

在这里插入图片描述

(1)propagation:事务传播行为

多事务互相调用,事务怎样管理,如在add中调用update

在这里插入图片描述

  • REQUIRED:add中调用update,如果add本身有事务,则update使用已有事务,add没有事务,则update开启一个新的事务
  • REQUIRED_NEW:无论add有没有事务,update都会开启一个新的事务
 @Transactional(propagation = Propagation.REQUIRES_NEW)

默认REQUIRED

(2)isolation(隔离级别)

事务隔离性:多事务操作不会互相影响,不考虑隔离会产生很多影响
三个读问题:脏读、不可重复读、虚读(幻读)

三个读问题

在这里插入图片描述

  • 脏读:B事务进行操作后数据发生改变 money 500->2000 A事务开启后读到了2000,但是B事务发生事务回滚,A读到了不该读的东西
  • 不可重复读:多个事务间不产生影响,B事务提交后500->2000,A事务读到了500,然后读到了2000,B事务对A事务产生了影响
  • 虚读:一个未提交事务读取到了已提交事务数据(跟不可重复读过程差不多)
隔离级别

在这里插入图片描述

参数默认重复读

(3)timeout

事务超过一定时间没有提交就会回滚
默认是-1,表示不限时

(4)readOnly(是否只读)

(5)rollbackFor (回滚)

(6)noRollbackFor(不回滚)

4.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:tx="http://www.springframework.org/schema/tx"
       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/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:component-scan base-package="com.chime"/>
    <context:property-placeholder location="classpath:jdbc.properties"/>

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

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--1、开启事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--2、配置通知(事务)-->
    <tx:advice id="accountAdvice">
        <tx:attributes>
            <tx:method name="accountMoney" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!--3、配置切面-->
    <aop:config>
        <!--切入点-->
        <aop:pointcut id="accountPoint" expression="execution(* com.chime.service.AccountService.*(..))"/>

        <aop:advisor advice-ref="accountAdvice" pointcut-ref="accountPoint"/>
    </aop:config>
    
</beans>

不需要开启事务注解了

以上是关于Spring二刷笔记-事务(Transaction)概念理解与实现的主要内容,如果未能解决你的问题,请参考以下文章

Spring二刷笔记-AOP概念理解与实现

Spring二刷笔记-IOC概念理解以及具体实现(xml和Annotation)

Spring 事务Transaction源码深度解析

spring---transaction---事务的配置

Spring 事务Transaction源码深度解析

本人遇到的spring事务之UnexpectedRollbackException异常解决笔记