• Spring 事务管理


    事务概念:

    原子性:要么都成功,有一个失败都失败

    一致性:总量不变(A有100元,B有100元,总量是200元。A把100元转给B,B就有了200元,A就没钱了,总量还是200元)

    隔离性:两人操作同一条数据,不会相互影响

    持久性:最终提交到数据库后成功

    搭建事务操作环境进行操作():

    银行转账例子:

    1.1、创建数据库,创建表,添加数据

    create table t_account
    (
        id       int auto_increment primary key,
        username varchar(50) null,   
        money    int         null
    );
    

     

    1.2、在项目中创建Service,Dao 完成对象创建和注入

    结构: 先配置bean配置文件,然后创建service创建dao。service里面注入dao,dao里面注入jdbc模板。在dao里面两个方法{多钱方法][少钱方法}。service中转账分别调用两个方法完成最终过程

    配置xml:
    <context:component-scan base-package="com.spring.test"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="jdbc:mysql://localhost:3306/user_db"/>
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    

      

    dao层:
    package com.spring.test.dao;
    
    public interface UserDao {
        //多钱的方法
        public void addMoney();
        //少钱的方法
        public void reduceMoney();
    }
    

      

    实现类:
    package com.spring.test.dao;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class UserDaoImpl implements UserDao {
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        //多钱
        @Override
        public void addMoney() {
            String sql = "update t_account set money=money+? where username=?";
            jdbcTemplate.update(sql,100,"marry");
        }
        //少钱
        @Override
        public void reduceMoney() {
            String sql = "update t_account set money=money-? where username=?";
            jdbcTemplate.update(sql,100,"lucy");
        }
    }
    

      

    service层:
    package com.spring.test.service;
    
    import com.spring.test.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserService {
        //注入dao
        @Autowired
        private UserDao userDao;
    
        //转账操作
        public void accountMoney(){
            //lucy少100
            userDao.reduceMoney();
            //marry多100
            userDao.addMoney();
        }
    }
    

      

    测试文件:
    package com.spring.test.run;
    
    import com.spring.test.service.UserService;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class AppRun {
        @Test
        public void RunTest(){
            ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
            UserService userService = (UserService)context.getBean("userService");
            userService.accountMoney();
        }
    }
    

      

    执行结果:

    数据执行成功~!

    来继续查看数据库变化

     上面的操作,如果正常执行是没有问题的,但是代码执行过程中出现一场,则转账会发生严重问题。

    模拟异常环境:

    service层中添加异常代码来模拟转账时的突然停电操作

    package com.spring.test.service;
    
    import com.spring.test.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserService {
        //注入dao
        @Autowired
        private UserDao userDao;
    
        //转账操作
        public void accountMoney(){
            //lucy少100
            userDao.reduceMoney();
    
            //异常代码
            int a = 12/0;
    
            //marry多100
            userDao.addMoney();
        }
    }
    

      

    通过前面的测试结果lucy的钱是900元,marry的钱是1100元。

    接下来进行异常测试

    运行测试文件后抛出异常:

    八月 03, 2020 9:22:46 下午 com.alibaba.druid.pool.DruidDataSource info
    信息: {dataSource-1} inited
    //下面的是报错--可以认为转账过程中停电了,但是lucy已经转账了100元
    java.lang.ArithmeticException: / by zero
    at com.spring.test.service.UserService.accountMoney(UserService.java:19)
    at com.spring.test.run.AppRun.RunTest(AppRun.java:13)
    

      接着查看数据库内容

     发现lucy的钱已经少了100,而marry并没有接收到转来的100元,这就是一个异常。

    解决这个问题的方法:

    (典型方法)

    操作过程:1、开启事务操作 2、进行业务上的操作 3、没有发生异常,提交数据 4、如果出现异常,进行事务回滚

    //转账操作
    public void accountMoney(){
        try {
            //第一步 开启事务
            //第二步 进行业务操作
            //lucy少100
            userDao.reduceMoney();
    
            //会出异常的代码
            int a = 12/0;
    
            //marry多100
            userDao.addMoney();
    		//第三步 没有发生异常,提交数据
        }catch (Exception e){
            //第四步 如果出现异常,进行事务回滚
           	回滚操作....
        }
    }
    

      

    (Spring事务操作)

    操作步骤:1、在业务逻辑层(service)加入事务管理 2、在Spring里面进行事务管理操作(编程式事务管理|声明式事务管理)

    声明式事务管理:1、注解方式 2、xml配置文件方式

    声明式事务管理底层:AOP(面向切面方式)

    事务管理API:

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

      

    开始:

    1.1、配置事务管理器开启事务注解
    <!--dataSource-->
    	.....见前面配置
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--开启事务注解 tx和配置context一样-->
    <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
    

      

    1.2、在service层的类或方法上添加事务的一个注解
    @Service
    @Transactional//表示类里面所有的方法都添加事务 可以单独的添加到类中需要添加事务的方法上
    public class UserService {
        //注入dao
        @Autowired
        private UserDao userDao;
    
        //转账操作
        public void accountMoney() {
            //lucy少100
            userDao.reduceMoney();
            
            //会出异常的代码
            int a = 12/0;
            
            //marry多100
            userDao.addMoney();
    
        }
    }
    

      

    先将lucy和marry的钱全部修改为1000

    执行测试类:

    八月 03, 2020 10:24:25 下午 com.alibaba.druid.pool.DruidDataSource info
    信息: {dataSource-1} inited
    //下面的是报错--可以认为转账过程中停电了,lucy和marry的钱不会多也不会少 各自还是1000元
    java.lang.ArithmeticException: / by zero
    

      

    事务操作(声明式事务管理配置)

    @Transactional(参数说明)

    @Transactional(propagation = Propagation.REQUIRED)  //事务
    @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ) **,//隔离级别
    @Transactional( timeout = 5,{事务},{隔离级别}) //超时时间
    @Transactional(readOnly = true,{超时时间},{事务},{隔离级别})//是否只读
    

      

    事务方法:对数据库表数据进行变化的操作(增删查改)

    参数说明:
    prepagation(7种行为)   --事务的传播行为,多事务方法之间进行调用,这个过程是如何接管的
    行为1:REQUIED
    如果有事务在运行,当前的方法就在这个事务内运行,否则就启动一个新的事务,并在自己的事务内运行
    [如果A方法本身有事务,调用B方法之后,B方法会使用当前A方法里面的事务]
    [如果A方法本身没有事务,调用B方法之后,创建新事务]
    -----
    行为2:REQUIED_NEW
    当前的方法必须重启新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起
    [使用A方法调用B方法,无论A方法是否有事务,都会创建新的事务]
    -----
    行为3:SUPPORTS
    如果有事务在运行,当前的方法就在这个事务内运行,否则他可以不运行在事务中
    -----
    行为4:NO_RESOURCES
    当前的方法不应该运行在事务中,如果有运行的事务,将它挂起
    -----
    行为5:MANDATORY
    当前的方法必须在事务内部,如果没有正在运行的事务,抛出异常
    -----
    行为6:NEVER
    当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常
    -----
    行为7:NESTED
    如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则就启动一个新的事务,并在它自己的事务内运行
    
    isolation(4个隔离行为)	    --事务的隔离级别
    事务里面有一个特性成为隔离性(不考虑隔离性会出现下面的三个问题),多事务操作之间它们不会产生影响。不考虑隔离性会产生一系列问题
    有三个读的问题:脏读、不可重复读、虚读
    脏读:多个事务之间一个未提交的事务读取到了另一个未提交事务的数据(致命问题)
    不可重复读:一个未提交的事务读取到了另一个提交事务的修改数据(普通现象)
    虚读:一个未提交的事务读取到另一条事务添加的数据
    所以隔离性可以解决这三个问题
    
    (×=可解决,√=不可解决)
    隔离行为1:DEAD UNCOMMITTED(未读已提交)
    脏读:√  不可重复读:√  虚读:√
    -----
    隔离行为2:READ COMMITTED(读已提交)
    脏读:×  不可重复读:√  虚读:√
    -----
    隔离行为3:REPEATABLE READ(可重复读)
    脏读:×  不可重复读:×  虚读:√
    -----
    隔离行为4:SERIALIZABLE(串行化)
    脏读:×  不可重复读:×  虚读:×
    -----
    
    timeout()	    --超时时间
    事务需要在一定的时间内进行提交,如果不提交就会进行回滚操作
    默认值-1(不超时) 按照秒
    
    readOnly()      --是否只读
    读:查询操作。写:添加修改删除操作
    默认值是false(表示可以查询,可以增删查改)
    设置层true后,就只能进行查询操作。
    
    rollbackFor()   --回滚
    设置运行中出现了哪些异常进行事务回滚
    
    noRollbackFor() --不回滚
    设置出现哪些异常它不进行事务回滚
    

      

  • 相关阅读:
    异或运算用途
    js正则表达式子校验
    SMART原则
    边际成本,机会成本,沉默成本
    cxf 例子
    CXF使用JMS作为传输协议的配置
    js验证手机号,身份证,车牌号验证
    redis应用
    list集合去重复元素
    lodop
  • 原文地址:https://www.cnblogs.com/MineLSG/p/13430751.html
Copyright © 2020-2023  润新知