0
点赞
收藏
分享

微信扫一扫

Spring 事务管理

后来的六六 2022-03-11 阅读 156

一,事务的基本概念
1,什么是事务
(1)事务是数据库操作最基本单元,逻辑上一组操作,这一组操作要不都成功,要么若是有一个失败,则都失败
(2)典型场景:银行转账
2,事务有四个特性(ACID)
(1)原子性:要成功都成功,要失败都失败
(2)一致性:操作之前和操作之后总量不变
(3)隔离性:多事务之间不会产生影响
(4)持久性:事务提交成功以后数据将发生永久变化

二,搭建事务操作环境
环境:WEB 层Service 层(业务操作);Dao 层(数据库操作,不写业务)。
在Dao层创建实现方法,在Service层也创建方法但是去调用Dao层的方法。
举例:zhangsan 的 money 加 1,lisi 的 money 减 1:
1,创建数据库表,添加记录
2,创建 Service,搭建 Dao,完成对象创建和注入关系
(1)Service 注入 Dao,在 Dao 注入 JdbcTemplate,在 JdbcTemplate 注入 DataSource

  • 配置文件中连接池
    <!--组件扫描-->
    <context:component-scan base-package="com.at"></context:component-scan>


    <!--数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="name" value="jdbc:mysql://localhost:3306/demo1?useUnicode=true&characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    </bean>
  • dao中创建 UserDao 接口,和实现 UserDao 的 UserDaoImpl
public interface UserDao {
}

@Repository
public class UserDaoImpl implements UserDao{
    @Autowired
    private JdbcTemplate jdbcTemplate;
}
@Service
public class UserService {
    //注入dao
    @Autowired
    private UserDao userDao;
}

配置文件中

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

3,在 dao 中创建两个方法:add 和 sub 的方法,在 service 中创建方法(同时add和sub)

public interface UserDao {
    public void add();
    public void sub();
}
@Repository
public class UserDaoImpl implements UserDao{
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void add() {
        String sql = "update t_account set money=money+? where username=?";
        jdbcTemplate.update(sql,1,"zhangsan");
    }

    @Override
    public void sub() {
        String sql = "update t_account set money=money-? where username=?";
        jdbcTemplate.update(sql,1,"lisi");
    }
}
@Service
public class UserService {
    //注入dao
    @Autowired
    private UserDao userDao;
    public void addSub() {
        userDao.add();
        userDao.sub();
    }
}

4,上方代码正常执行没有问题,但是如果代码在执行工程中出现异常,将产生问题

如:

    public void addSub() {
        userDao.add();
        
        //假设此处出现故障故障
        。。。。。。
        
        userDao.sub();
    }

在 userDao.add() 后出现故障,那么 userDao.sub() 将不会执行,如此就不能实现 add 与 sub 同时执行
(1)解决方案

  • 使用事务进行解决

(2)事务操作过程

    public void addSub() {
        try {
            //1,开启事务

            //2,进行业务操作

            userDao.add();

            //假设此处出现故障故障
            。。。。。。

            userDao.sub();

            //3,没有发生异常,提交事务
        } catch (Exception e) {
            //4,出现异常,则事务回滚
        }
    }

三,Spring 事务管理介绍
1,事务一般添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)

2,在 Spring 进行事务管理操作
(1)有两种:编程式事务管理,声明式事务管理。(多数用声明式)

编程式:

    public void addSub() {
        try {
            //1,开启事务

            //2,进行业务操作

            userDao.add();

            //假设此处出现故障故障
            。。。。。。

            userDao.sub();

            //3,没有发生异常,提交事务
        } catch (Exception e) {
            //4,出现异常,则事务回滚
        }
    }

3,声明式事务管理
(1)基于注解方式 (大多时候用基于注解方式)
(2)基于 xml 配置文件方式

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

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

接口:PlatformTransactionManager
其中 JDBC 的实现类为:DataSourceTransactionManager

四,基于注解方式的声明式事务管理
1,在 Spring:配置文件配置事务管理器

    <!--数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="name" value="jdbc:mysql://localhost:3306/demo1?useUnicode=true&characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    </bean>

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

2,在 Spring 配置文件中,开启事务注解
(1)在 Spring 配置文件中引入名称空间tx

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

(2)开启事务注解

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

3,在 Service 类上面(获取 Service 类里面方法上面)添加事务注解
(1)@Transactional,这个注解添加到类上面,也可以添加方法上面
(2)如果把这个注解添加到类上面,这个类里面的所有的方法都添加事务
(3)如果把这个注解添加到方法上面,为这个方法添加事务

@Service
@Transactional
public class UserService {

这样当出现异常,则可以自动处理,回滚

五,声明式事务管理参数配置
1,在 service 类上面添加注解 @Transactional,在这个注解里面可以配置事务相关参数

  • propagation:事务传播行为
    (1)多事务方法直接进行调用,这个过程中事务是如何进行管理的
    (事务方法:对数据库表数据进行变化的操作,像查询就不是事务方法)
    研究如:有事务行为的方法调用了无事务行为的方法时,有事务行为的方法调用了也是有事务行为的方法时,无事务行为的方法调用了有事务行为的方法时等等改如何处理。
@Service
@Transactional(propagation = Propagation.REQUIRED)
public class UserService {
  • isolation:事务隔离级别
    (1)事务有个特性 隔离性:多事务操作之间不会产生影响。不考虑隔离性产生很多问题。
    (2)有三个读问题:脏读,不可重复读,虚(幻)读
    (3)脏读:多事务之间,一个未提交事务读取到另一个未提交事务的数据
    (4)不可重复读:一个未提交事务读取到另一个提交事务的修改数据
    (5)虚(幻)读:一个未提交的事务读取到另一个提交事务添加数据
    (6)通过设置事务隔离级别,解决上述问题
脏读不可重复读幻读
READ UNCOMMITTED (读未提交)
READ COMMITTED (读已提交)
REPEATABLE READ(可重复读)
SERIALIZABLE(串行化)
@Service
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)
public class UserService {

(mysql 默认是 REPEATABLE READ(可重复读))

  • timeout:超时时间
  • readOnly:是否只读
  • rollbackFor:回滚
  • noRollbackFor:不回滚
举报

相关推荐

0 条评论