Spring JDBC与事务管理5:Spring编程式事务;

52 篇文章 0 订阅

说明:

(1)有关事务内容可以快速参考【MySQL综合应用二:事务机制一:事务的基本介绍】及附近文章;【JDBC入门九:JDBC实现事务(十分重要!!!)】;

(2) 【Spring JDBC模块】中也有事务控制;其中就包括编程式事务和声明式事务;本篇博客介绍编程式事务;

目录

一:编程式事务简介

二:代码演示:没有使用【编程式事务控制】的情况

首先,创建EmployeeService类: 

然后,在applicationContext.xml中配置EmployeeService对象,并注入EmployeeDao:

然后,在JdbcTemplateTestor测试类中编写测试方法:

三:问题分析

引入logback日志依赖:

通过日志信息,分析【未使用控制事务】时的执行过程:

四:使用【编程式事务】去控制(核心!)

首先,在applicationContext.xml中配置transactionManager事务管理器对象:

然后,在需要事务控制的类(EmployeeService类)中,添加并注入transactionManager:

然后,就是【编程式事务】的实际使用了:

测试1:【执行过程中出现异常】的案例:

测试2:【执行过程中没有出现异常】的案例:

五:【编程式事务】有缺点,引出【声明式事务】


一:编程式事务简介

 

(1)【Spring JDBC模块】通过引入TransactionManager对象(事务管理器),来控制事务;

(2)TransactionManager对象提供了两个核心方法:commit()和rollback()方法;(PS:在【JDBC入门九:JDBC实现事务(十分重要!!!)】中Connection对象也有自己的commit()和rollback()方法来控制事务;都是相通的)

(3)本篇博客的代码沿用自【Spring JDBC与事务管理4:Spring JDBC四:Jdbc Template新增、更新、删除方法;(update();)】;


 

二:代码演示:没有使用【编程式事务控制】的情况

需求:公司入职10名新员工,需要把这10名新员工批量导入Employee表中;要求是,要么一次性全部导入,要么一个也不导入;

首先,创建EmployeeService类: 

EmployeeService:

package com.imooc.spring.jdbc.service;

import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;

import java.util.Date;

public class EmployeeService {
    private EmployeeDao employeeDao;
    public void batchImport() {
        for (int i = 1; i <= 10; i++) {
            Employee employee = new Employee();
            employee.setEno(8000 + i);
            employee.setEname("员工"+i);
            employee.setSalary(4000f);
            employee.setDname("研发部");
            employee.setHiredate(new Date());
            employeeDao.insert(employee);
        }
    }

    public EmployeeDao getEmployeeDao() {
        return employeeDao;
    }

    public void setEmployeeDao(EmployeeDao employeeDao) {
        this.employeeDao = employeeDao;
    }
}

然后,在applicationContext.xml中配置EmployeeService对象,并注入EmployeeDao:

然后,在JdbcTemplateTestor测试类中编写测试方法:

 

 

 


 

三:问题分析

虽然,上面写完了,而且导入成功了;但是上面写入操作并没有进行事务控制;

我们要求是,这10名员工,要么一次性全部导入,要么一个也不导入;而上面10次新增操作并不是在一个事务中完成的;可以打印一下该过程的日志来观察一下;

引入logback日志依赖:

说明:

(1)在【MyBatis进阶一:MyBatis日志管理;(【如何输出日志到日志文件中】待补充……)】中介绍过logback这个日志组件;

(2)当添加logback这个日志依赖后,Spring默认就集成了;即Spring发现当前类路径下引入了logback后,Spring框架就可以默认使用logback将日志打印输出;

通过日志信息,分析【未使用控制事务】时的执行过程:

为了重新测试,将刚才添加的十条数据删除:

然后,重新运行JdbcTemplateTestor测试类中的testBatchImport()方法:观察日志信息

即,可知,在插入10条数据的过程中,一旦在过程中程序出现了中断,肯定会出现【一部分数据插入成功,而另一部分数据没有插入的情况】,而这是和我们的要求相违背的;


 

四:使用【编程式事务】去控制(核心!)

首先,在applicationContext.xml中配置transactionManager事务管理器对象:

然后,在需要事务控制的类(EmployeeService类)中,添加并注入transactionManager:

这儿,是在EmployeeService类中需要事务控制;

 

设置好了之后,如何使用?

然后,就是【编程式事务】的实际使用了:

EmployeeService完整代码如下:

package com.imooc.spring.jdbc.service;

import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.Date;

public class EmployeeService {
    private EmployeeDao employeeDao;
    private DataSourceTransactionManager transactionManager;
    public void batchImport() {
        //定义了事务默认的标准配置
        TransactionDefinition definition = new DefaultTransactionDefinition();
        //开始一个事务,返回值是事务状态,事务状态说明当前事务的执行阶段
        TransactionStatus transactionStatus = transactionManager.getTransaction(definition);
        try {
            for (int i = 1; i <= 10; i++) {
                Employee employee = new Employee();
                employee.setEno(8000 + i);
                employee.setEname("员工" + i);
                employee.setSalary(4000f);
                employee.setDname("研发部");
                employee.setHiredate(new Date());
                employeeDao.insert(employee);
            }
            //如果一切正常,就提交事务
            transactionManager.commit(transactionStatus);
        } catch (RuntimeException e) {
            //如果运行出错,回滚事务
            transactionManager.rollback(transactionStatus);
            e.printStackTrace();//可以将异常进行打印,这也意味着异常不会向上抛出,在EmployeeService内部就被消化了;
//            throw e;//如果,想让异常在外侧(即调用方)处理的话,可以把异常抛出去;
        }
    }

    public EmployeeDao getEmployeeDao() {
        return employeeDao;
    }

    public void setEmployeeDao(EmployeeDao employeeDao) {
        this.employeeDao = employeeDao;
    }

    public DataSourceTransactionManager getTransactionManager() {
        return transactionManager;
    }

    public void setTransactionManager(DataSourceTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }
}

说明:

(1)代码分析

测试1:【执行过程中出现异常】的案例:

 为了重新测试,将刚才添加的十条数据删除:

 

日志分析:

 

可以看到,当使用了编程式事务控制以后,所有的数据操作,都是在一个数据库连接中完成的;然后,已经成功写入的数据,不是直接写到数据库的表中,而是先被放入到了事务区中;

测试2:【执行过程中没有出现异常】的案例:

日志分析:

 

 


五:【编程式事务】有缺点,引出【声明式事务】

编程式事务:有优点,有缺点:

     优点:编程式事务的控制,是直接写在代码中的, 人眼阅读比较友好;

     缺点:编程式事务需要我们在代码中去书写事务控制的代码,因为不同的人对Spring掌握的程度不同,或者有的人可能在写代码的时候忘记写如下的代码:

                

这就可能导致数据的不完整的情况,从而给软件埋下巨大的风险。在一些金融、军事、科学运算等对数据要求很高的系统中,出现不完整的数据是非常严重的错误。所以,编程式事务控制可能存在人为的风险;为此,将介绍【Spring JDBC】提供的一种更加高级的做法:声明式事务。

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 黑客帝国 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值