spring事务入门级理解

一、事务概念

1、什么是事务

事务是数据库操作最基本的单元,逻辑上一组操作,要么都成功,如果有一个失败,则所有操作都失败

经典场景:银行转账

2、事物的四个特性(ACID)

原子性、一致性、隔离性、持久性

3、引出事务操作

(1)数据库建表account,并插入数据

在这里插入图片描述

pom依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>4.3.0.RELEASE</version>
        </dependency>
    </dependencies>

核心配置文件application.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
        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">

    <!-- 开启组件扫描-->
    <!-- 2、扫描上层-->
    <context:component-scan base-package="com.lu"></context:component-scan>

    <!-- 数据库连接池。
         destroy-method="close"作用是当数据库连接不使用的时候,就把该连接重新放到数据池中,方便下次使用调用.
         xml中:&要用 &amp; 转义-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/spring-crud?userSSL=true&amp;
                                    useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC" />
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>

    <!--JdbcTemplate对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入dataSourceDefault-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

</beans>

(2)创建service,dao完成对象创建和注入关系

UserDao类

public interface UserDao {
    
    
    void addMoney();
    void reduceMoney();
}

UserDaoImpl类

@Repository
public class UserDaoImpl implements UserDao{
    
    

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void addMoney() {
    
    
        String sql = "update account set money = money-? where username=?";
        jdbcTemplate.batchUpdate(sql,"100","lucy");
    }

    public void reduceMoney() {
    
    
        String sql = "update account set money = money+? where username=?";
        jdbcTemplate.batchUpdate(sql,"100","tom");
    }
}

UserService类

@Service
public class UserService {
    
    
    //注入dao
    @Autowired
    private UserDao userDao;

    //转账方法
    public void accountMoney(){
    
    
        //lucy少100
        userDao.reduceMoney();
        //tom多100
        userDao.addMoney();
    }
}

测试类TestAccount

public class TestAccount {
    
    
    public static void main(String[] args) {
    
    
        ApplicationContext context =
                new ClassPathXmlApplicationContext("application.xml");
        UserService userService = context.getBean("userService",UserService.class);
        userService.accountMoney();
    }
}

3、正常执行上边程序,是没问题的。但是,如果在步骤中间,有异常,就可能导致一个成功、一个失败的问题,进而导致钱数错误。

    //转账方法
    public void accountMoney(){
    
    
        //lucy少100
        userDao.reduceMoney();

        //模拟异常
        int i= 10/0;

        //tom多100
        userDao.addMoney();
    }

4、如何解决上边的问题?

使用事务进行解决

事务操作的过程

在这里插入图片描述

二、spring事务

1、事务添加到JavaEE三层架构里面的Service层(业务逻辑层)

2、spring中有两种事务管理方式

  • 编程式事务管理(如上图,4步)

  • 声明式事务管理(常用)

3、声明式事务管理

  • 基于注解方式(常用)
  • 基于xml配置文件方式

4、在spring进行声明式事务管理,底层使用AOP

5、spring事务管理API

(1)提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类

在这里插入图片描述

三、事务操作(注解实现声明式事务管理)

3.1、在spring配置文件中配置事务管理器

    <!--创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源。ref后跟的式数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

3.2、在spring配置文件中开启事务注解

  • 在spring配置文件中引入名称空间tx

在这里插入图片描述

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

3.3、在service类上面(或者service类里面方法上)添加事务注解

  • @Transactional,这个注解可以添加类上面,也可以添加方法上面。

  • 添加到类上面,这个类里面所有方法都添加事务。

  • 添加到方法上面,只是为这个方法添加事务。

四、声明式事务管理参数配置

在service类上面添加注解@Transactional,在这个注解里面可以配置事务相关参数。

在这里插入图片描述

4.1、propagation:事务传播行为

使用方法:就是在Transactional注解后边的括号内添加propagation = xxx
例如: @Transactional(propagation = Propagation.REQUIRED)

事务传播行为一般是:多事务方法之间的调用,这个过程中事务时如何进行管理的

在这里插入图片描述

图片来自尚硅谷王泽老师B站视频

事务的传播行为指方法之间事务的传播,比如方法A调用了方法B:
有7种,required 、required_new常用:

  • REQUIRED
    Spring默认的传播机制,如果A有事务就使用当前事务, 如果A没有事务, 就创建一个新事务。
  • REQUIRES_NEW
    不管A有没有事务都创建一个新事务。
  • NEVER
    如果A有事务就抛异常, 如果A没有事务, 就以非事务执行。

4.2、ioslation:事务隔离级别

使用方法:就是在Transactional注解后边的括号内添加,如下。如果有多种,逗号隔开。
@Transactional(isolation = Isolation.REPEATABLE_READ)

并发下事务会产生的问题

举个例子,事务A和事务B操纵的是同一个资源,事务A有若干个子事务,事务B也有若干个子事务,事务A和事务B在高并发的情况下,会出现各种各样的问题。

脏读、不可重复读、幻读

(1)脏读(Dirty read)
脏读发生在一个事务读取了被另一个事务改写但尚未提交的数据时。

(2)不可重复读(Nonrepeatable read)
不可重复读发生在一个事务执行相同的查询两次或两次以上,但每次查询结果都不相同时。这通常是由于另一个并发事务在两次查询之间更新了数据。

(不可重复读重点在修改)

(3)幻读(Phantom reads)
幻读和不可重复读相似。当一个事务(T1)读取几行记录后,另一个并发事务(T2)插入或者删除了一些记录时,幻读就发生了。在后来的查询中,第一个事务(T1)就会发现一些原来没有或少了的额外记录。
(幻读重点在新增或删除)
参考的blog:https://www.cnblogs.com/mseddl/p/11577846.html

解决:通过设置事务隔离级别,解决读的问题

隔离级别 含义
Read Uncommitted 读未提交:可能导致脏读、幻读或不可重复读。
Read Committed 读已提交:可防止脏读,但幻读和不可重复读仍可能发生。
Repeated Read 可重复读:可防止脏读和不可重复读,但幻读仍可能发生。
Serializable 串行化:完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。

4.3、timeout:超时时间

  • 事务需要在一定时间内进行提交,如果不提交,进行回滚
  • 默认值是-1,设置时间以秒单位进行计算
  • 设置格式:@Transactional(timeout = -1)

4.4、readOnly:是否只读

  • readOnly默认值是false,表示可以查询,可以添加、修改和删除操作
  • 可以设置readOnly值是true,设置之后,只能查询
  • 设置格式:@Transactional(readOnly = false)

4.5、rollbackFor:回滚

  • @Transactional的rollbackFor用于指定能够触发事务回滚的异常类型,可以指定多个,用逗号分隔。
  • rollbackFor默认值为UncheckedException,包括了RuntimeException和Error.
  • 当我们直接使用@Transactional不指定rollbackFor时,Exception及其子类都不会触发回滚。
  • 设置格式:@Transactional(rollbackOn = Exception.class)

4.6、noRollbackFor:不回滚

  • 设置出现哪些异常不进行事务回滚

练习代码:
链接:https://pan.baidu.com/s/1jOYuR-sfeT6DFhLWXqJOpg
提取码:vx8x

猜你喜欢

转载自blog.csdn.net/qq_42524288/article/details/108953441
今日推荐