11、Spring5-事务操作

1、事务的概念

1、什么是事务

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

(2)典型场景:银行转账

    lucy转账 100 给mary,lucy少100,mary多100,若出现异常,lucy不会少,mary不会多

2、事务的特性(ACID):

(1)原子性:过程不可分割

(2)一致性:事务执行之前或执行之后数据库都必须,处于一致性状态,不管是错误还是断电,总量不变

(3)隔离性:多事务操作不会相互影响

(4)持久性:事务提交后就会更改表中的数据

 

2、搭建事务操作的基本环境

 

 

1、创建数据库和表,添加记录

 

2、创建service,搭建 dao 和实现类,完成对象创建和注入关系,配置连接池

(1)service 注入 dao ,在 dao 中注入 jdbcTemplate ,在jdbcTemplate 中注入 DataSource

  service中注入dao对象

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

  dao中注入jdbcTemplate对象

@Repository
public class USerDaoImpl implements UserDao{
    @Autowired
    private JdbcTemplate jdbcTemplate;

}

 

   配置文件中jdbcTemplate注入DataSource

<?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">

    <!--开启组件扫描-->
    <context:component-scan base-package="com.spring5"></context:component-scan>

    <!--引入外部属性文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--配置数据库连接池-->
    <!-- DruidDataSource dataSource = new DruidDataSource(); -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!-- dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            set方法注入
        -->
        <!-- 获取properties文件内容,根据key获取,使用spring表达式获取 -->
        <property name="driverClassName" value="${prop.driverClass}"></property>
        <property name="url" value="${prop.url}"></property>
        <property name="username" value="${prop.userName}"></property>
        <property name="password" value="${prop.password}"></property>
    </bean>

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

</beans>

 

3、在dao创建两个方法,一个是多钱的方法,一个是少钱的方法,在Service中创建转账方法

下面是转账方法的基本实现:

dao中的实现如下:

@Repository
public class USerDaoImpl implements UserDao{
    @Autowired
    private JdbcTemplate jdbcTemplate;

    //lucy转账100给mary
    @Override
    public void addMoney() {
        String sql = "update t_account set money=money-? where username=?";
        jdbcTemplate.update(sql,100,"lucy");
    }

    //mary收款100
    @Override
    public void reduceMoney() {
        String sql = "update t_account set money=money+? where username=?";
        jdbcTemplate.update(sql,100,"mary");
    }
}

 

 service的实现方法如下:

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

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

 

4、上面代码实现转账功能,正常执行时是没有问题的,但是如果代码执行过程中出现了异常,会产生一些问题

 

(1)上面的问题如何解决?

  使用事务进行解决:逻辑上一组事务要么都成功,一个失败所有都失败

(2)基本步骤如下:

try{
  //第一步 开启事务操作

  //第二步 进行业务操作(转账)
            
  //第三步 没有异常,提交事务
            
}catch (Exception e){
  //第四步 出现异常,事务回滚
}
上述为编程式事务管理,但是在开发中一般使用声明式事务管理

 

3、Spring事务管理介绍

1、事务一般添加到 JavaEE 三层结构的 Service 层(业务逻辑层)

2、在Spring进行进行事务管理操作,

(1)有两种方式:编程式事务管理和声明式事务管理(一般使用声明式)

3、声明式事务管理

(1)基于注解方式(常用)

(2)基于xml配置文件方式

 

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

 

5、Spring事务管理相关API

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

PlatformTransactionManager  代表事务管理器,Spring 做事务管理都是使用这个接口做到的

DataSourceTransactionManager  针对数据库的事务管理

 

PlatformTransactionManager 是一个顶层接口,在接口中针对不同操作数据库的框架,有不同的实现类。

 

4、实现声明式事务管理(注解的方式)

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

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

 

2、在Spring配置文件中,开启事务的注解

(1)在 Spring 的配置文件中引入名称空间 tx

<?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">

 

(2)开启事务的注解

    <!--开启事务的注解
            transaction-manager 指定事务管理器    
    -->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

 

3、在Service 类上面(或在 Service 类中的方法上面)添加事务的注解 @Transactional

(1)@Transactional  可以添加到类上面,也可以添加到方法上面

(2)如果把这个注解添加到类上面,则表示这个类里面的所有方法都添加上了事务

(3)如果式添加到类的方法上面,则只是为这个方法添加了事务

@Service
@Transactional  //@Transactional可以加到类上,也可以加到方法上面
public class UserService {
    //注入dao
    @Autowired
    private UserDao userDao;

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

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

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

 

 

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

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

 

2、propagation : 表示事务的传播行为

(1)多事务方法之间进行调用(有事务方法调无事务方法或有事务方法调有事务方法等情况),这个事务是如何进行管理的。如:

 

 默认时 REQUIRED

@Transactional(propagation = Propagation.REQUIRED)

 

3、isolation : 表示事务的隔离级别

(1)事务中有一个特性称为隔离性,多事务操作之间不会产生影响。不考虑隔离性会产生很多问题
(2)有三个读的问题:脏读、不可重复读、虚读(幻读)

  脏读:多个事务之间,一个未提交的事务读取到了另外一个未提交的数据

  

 

   不可重复读:一个未提交的事务,读取到了另一个提交事务修改的事务

  

 

   幻读:一个未提交的事务,读取到了另外一个提交事务添加的数据

(3)通过设置事务的隔离性,解决读的问题

事务的隔离级别:

 

 隔离级别设置:

@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ) 

 

在 mysql 中默认使用的时 REAPEATABLE READ 隔离级别

4、timeout : 超时时间

(1)事务在一定的时间内进行提交,否则进行事务的回滚

(2)在 Spring 中,默认值时-1(不超时),可以对时间进行设置,以秒为单位

@Transactional(timeout = -1,propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)

 

5、readonly : 是否只读

(1)读:查询操作,写:添加修改删除操作

(2)readOnly 的默认值时 false ,表示可以进行增加修改删除操作

(3)可以设置 readOnly 的值为 true ,设置成 true 后,只能查询

@Transactional(readOnly = false,timeout = -1,propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)

 

6、rollbackFor : 回滚

(1)设置事务中出现的哪些异常,进行回滚

7、noRollbackFor : 不回滚

 (2)设置事务出现那些异常不进行回滚

 

 

5、使用xml配置文件的方式进行事务的操作

1、在spring配置文件中进行配置

第一步 配置事务管理器

第二步 配置通知(增强)

第三步 配置切入点和切面

    <!--配置通知-->
    <tx:advice id="txAdvice">
        <!--配置事务的相关参数-->
        <tx:attributes>
            <!--指定哪种规则的方法上面添加事务-->
            <tx:method name="accountMoney" propagation="REQUIRED"/><!--可以在后面添加属性-->
<!--            <tx:method name="account*"/>&lt;!&ndash;表示以account开头的方法都进行事务操作&ndash;&gt;-->
        </tx:attributes>
    </tx:advice>
    
    <!--配置切入点和切面-->
    <aop:config>
        <!--配置切入点-->
        <aop:pointcut id="pt" expression="execution(* com.spring5.service.UserService.*(..))"/>
        <!--配置切面-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
    </aop:config>

 

 

6、完全注解方式进行声明式事务管理

1、创建配置类,使用配置类替代xml配置文件

@Configuration  //代表它是一个配置类
@ComponentScan(basePackages = "com.spring5")    //组件扫描
@EnableTransactionManagement    //开启事务
public class txConfig {
    //创建数据库连接池
    @Bean
    public DruidDataSource getDruidDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/user_db");
        dataSource.setUsername("root");
        dataSource.setPassword("124869");
        return dataSource;
    }
    
    //创建JdbcTemplate对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
        //到ioc容器中,根据类型找到dataSource完成注入
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        //注入dataSource
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }
    
    //创建事务管理器对象
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

 

2、在Service中添加注解@Transactional 

@Service
@Transactional(readOnly = false,timeout = -1,propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)  //@Transactional可以加到类上,也可以加到方法上面
public class UserService {
    //注入dao
    @Autowired
    private UserDao userDao;

    //转账的方法
    public void accountMoney(){

        //lucy少100
        userDao.reduceMoney();

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

        //mary多100
        userDao.addMoney();

    }
}    

 测试代码如下:

    @Test
    public void testAccount3(){
        ApplicationContext context = new AnnotationConfigApplicationContext(txConfig.class);
        UserService userService = context.getBean("userService", UserService.class);
        userService.accountMoney();
    }

 

posted @ 2022-06-06 02:41  卓汶  阅读(47)  评论(0编辑  收藏  举报