引言

数据库事务的概念和基础,总结在《MySQL 基础 ————事务与隔离级别总结》。

本篇博客通过“JDBC + 纯编码”方式实现事务控制,完成一个 A 给 B 转账的小功能,在进一步熟练JDBC的编程流程的同时,重点关注 Java 语言如何操作和控制事务

一、事务自动提交的三种情况

事务默认自动提交的三种情况:

1、DDL操作执行后,会自动提交事务,SET autocommit=false 对该类语句不管用。不过,在DDL语言上一般不考虑事务。

2、DML(增、删、改)默认情况下,执行后会自动提交,不过可以通过 set autocommit=false;取消 DML的自动提交。

3、数据库连接关闭时,默认会自动提交事务。因此,如果两次DML操作在同一事务中,则事务中间不可以关闭连接

一般的事务控制只需要考虑后两种情况,我们需要做的就是在当前连接中取消自动提交,在整体逻辑完成后,提交事务或异常回滚,最后恢复自动提交

二、逻辑介绍、库表及实体类

如下图,两条用户信息,完成 Morty 转账 100 块给 Jack 的转账功能。涉及两步操作:1、Morty 减少 100   2、Jack 增加 100

User 实体类:

public class User {private Integer id;private String name;private Date birthDay;private Integer balance;public User() {}// ... getter setter...
}

三、数据库连接获取与资源关闭工具类

该工具类使用 JDBC API 完成对数据库连接的获取,及关闭操作,代码逻辑与注意事项参考《JDBC——概述与JDBC的使用》

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;public class JDBCUtils {public static Connection getConnection() {Connection connection = null;try {// 默认的识别路径就是 src 目录下InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");Properties props = new Properties();props.load(is);String url = props.getProperty("url");String username = props.getProperty("username");String password = props.getProperty("password");String driverName = props.getProperty("driverName");// 加载驱动类Class.forName(driverName);// 获取连接connection = DriverManager.getConnection(url, username, password);} catch (Exception e) {e.printStackTrace();}return connection;}public static void closeResource(Connection conn, Statement statement, ResultSet rs) {try {if (rs != null) {rs.close();}} catch (SQLException e) {e.printStackTrace();}try {if (statement != null) {statement.close();}} catch (SQLException e) {e.printStackTrace();}try {if (conn != null) {conn.close();}} catch (SQLException e) {e.printStackTrace();}}
}

四、转账操作逻辑实现

在《JDBC——概述与JDBC的使用》中,展示了简单的入库操作,这里可以将其优化为通用的更新操作:

    /*** 数据库更新操作*/private static int update(Connection connection, String sql, Object... args) throws Exception {PreparedStatement ps = null;try {ps = connection.prepareStatement(sql);// 填充属性for (int i = 0; i < args.length; i++) {ps.setObject(i + 1, args[i]);}int rows = ps.executeUpdate();return rows;} finally {JDBCUtils.closeResource(null, ps, null);}}

上述代码中,方法参数接收一个连接Connection 对象,这是因为,在执行完更新操作后,不可以直接关闭连接,否则会自动提交事务,在最后的finally块中,也要注意,不要将 Connection对象传入关闭资源的方法中,以免误关Connection。并且如果发生异常,直接抛出即可,这是因为如果在此过程中发生异常,既可以将情况反映给调用方,也可以在 finally 块中关闭必要的资源。

    /*** 转账(事务控制)*/private static void transferAccountsTx() {Connection connection = null;try {connection = JDBCUtils.getConnection();// 关闭自动提交connection.setAutoCommit(false);// 转账金额Integer amount = 100;// 减少金额String sql1 = "UPDATE user SET balance = balance - ? WHERE name = ?";update(connection, sql1, amount, "Morty");// 模拟异常System.out.println(10 / 0);// 增加金额String sql2 = "UPDATE user SET balance = balance + ? WHERE name = ?";update(connection, sql2, amount, "Jack");// 执行提交connection.commit();System.out.println("转账成功!");} catch (Exception e) {e.printStackTrace();try {// 异常回滚connection.rollback();} catch (SQLException ex) {ex.printStackTrace();}} finally {try {// 恢复事务自动提交,针对连接池的情况需要额外注意该操作connection.setAutoCommit(true);} catch (SQLException e) {e.printStackTrace();}JDBCUtils.closeResource(connection, null, null);}}

上述代码中,通过 .setAutoCommit(false) 关闭自动提交,开启事务, 并通过 10 ÷ 0 的操作,模拟了一个转账过程中的异常,捕获异常时,我们要执行回滚操作。如果逻辑可以正常执行,就使用  commit(); 完成提交操作。最后在整体逻辑后面,使用 finally 块恢复事务自动提交(实际上,如果是关闭Connection,并不需要恢复自动提交,但如果是连接池的情况,重复利用连接就需要这么做),并关闭 Connection 。

五、通过代码设置数据库事务隔离级别

一般情况,不需要通过代码层面更改数据库事务,不过可以了解,事务隔离级别非常简单,具体概念可以参考《MySQL 基础 ————事务与隔离级别总结》。

使用 Connection 对象可以设置和获取隔离级别:

// 获取事务隔离级别
connection.getTransactionIsolation();// 设置事务隔离级别
connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

Connection 中定义了 4 种事务隔离级别的常量,从低到高分别是:

int TRANSACTION_READ_UNCOMMITTED = 1;
int TRANSACTION_READ_COMMITTED   = 2;
int TRANSACTION_REPEATABLE_READ  = 4;
int TRANSACTION_SERIALIZABLE     = 8;

总结

需要考虑事务自动提交的两种情况:

1、DML语句会自动提交事务,需要 set autocommit=false 关闭自动提交,对应 JDBC的 API 就是

connection.setAutoCommit(false);

2、关闭连接会自动提交事务,一定要在完成全部事务操作后才可以关闭 Connection 。代码中一定要注意  connection.close() 的位置!

三个事务控制的新方法:

// 关闭自动提交
connection.setAutoCommit(false);
...
// 手动提交事务
connection.commit();
...
// 异常时回滚事务
connection.rollback();

另外,每条SQL执行时也可能发生异常导致操作失败,一定要将异常抛给包含事务的调用方,以免调用方以为SQL执行成功,从而破坏了事务的一致性。

具体点说,比如上述案例中,Morty 账户减100 时在 update() 执行过程中发生了未知异常,但是如果update(..) 方法内部捕获了异常,方法正常退出,而没有抛给 transferAccountsTx(),那么transferAccountsTx() 就不会进入 catch() 块并回滚事务。这也是一个需要注意的点,即,我们要保证事务中的每一处发生异常时,都可以成功回滚

JDBC——编程式事务的实现逻辑相关推荐

  1. 全面分析 Spring 的编程式事务管理及声明式事务管理(转)

    摘要 Spring 的事务管理是 Spring 框架中一个比较重要的知识点,该知识点本身并不复杂,只是由于其比较灵活,导致初学者很难把握.本教程从基础知识开始,详细分析了 Spring 事务管理的使用 ...

  2. 全面分析 Spring 的编程式事务管理及声明式事务管理--转

    开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本 ...

  3. java编程式事务_Spring编程式和声明式事务实例讲解

    Spring事务管理 Spring支持两种方式的事务管理: 编程式事务管理: 通过Transaction Template手动管理事务,实际应用中很少使用, 使用XML配置声明式事务: 推荐使用(代码 ...

  4. Spring笔记(4) - Spring的编程式事务和声明式事务详解

    一.背景 事务管理对于企业应用而言至关重要.它保证了用户的每一次操作都是可靠的,即便出现了异常的访问情况,也不至于破坏后台数据的完整性.就像银行的自助取款机,通常都能正常为客户服务,但是也难免遇到操作 ...

  5. Spring中两种编程式事务管理

    Spring中两种编程式事务管理 在代码中显示调用beginTransaction,commit,rollback等与事务处理相关的方法,这就是编程式事务管理,当只有少数事务操作时,编程式事务管理才比 ...

  6. 全面分析 Spring 的编程式事务管理及声明式事务管理

    转自:http://www.open-open.com/lib/view/open1414310646012.html 关于本教程 本教程将深切讲授 Spring 庞杂而丁壮夜的事务治理功用,包括编程 ...

  7. 事务声明声明式事务和编程式事务区别

    事务声明声明式事务和编程式事务区别 1.编程式事务: 所谓编程式事务指的是通过编码方式实现事务,即类似于JDBC编程实现事务管理.管理使用TransactionTemplate或者直接使用底层的Pla ...

  8. 【spring】编程式事务控制

    结构: AccountServiceImpl package com.itheima.service.impl;import com.itheima.dao.IAccountDao; import c ...

  9. 编程式事务与声明式事务

    编程式事务 1.加入jar包 com.springsource.net.sf.cglib-2.2.0.jar com.springsource.org.aopalliance-1.0.0.jar co ...

最新文章

  1. php在线读取pdf文件大小_怎么压缩PDF文件?快来试试这些工具!
  2. BugKuCTF WEB 备份是个好习惯
  3. 压力管道流量计算公式_给水管管径及流量计算方法
  4. 格力又有新专利了:“一种铁芯冲片、电机及新能源汽车”
  5. 电脑的发展史_UI设计发展史及未来
  6. Python Window10 环境安装流程
  7. V模型、W模型、测试工具的介绍
  8. 【黑苹果EFI下载】三星笔记本NP500R4K(5200U+HD5500)+Macos10.14版本
  9. net-java-php-python-新华眼镜ERP系统计算机毕业设计程序
  10. 查找算法之二分查找算法
  11. 2012年读书年度小结
  12. alsa buffer原理_ALSA driver--HW Buffer
  13. [RK3399][Android7.1.1]系统强制App横屏显示
  14. 软件测试,2019.2.15中移物联网面试心路历程。
  15. JavaScript 实现网页截屏五种方法
  16. 审阅模式中word保存不了
  17. 【OPENCV】运行opencv时找不到Qt库
  18. 那个牛逼的斯坦福大学
  19. CSDN20181211博客黑板报
  20. #10115. 「一本通 4.1 例 3」校门外的树

热门文章

  1. 集合使用与内部实现原理
  2. Matlab制作朱利表
  3. QT5开发的程序打包发布
  4. Python3之logging输出写入日志
  5. 滑动窗口--单调队列
  6. uni-calendar更改打点颜色实现签到和缺勤不同打点颜色效果
  7. mysql 查询一个月的时间_mysql日期查询sql语句总结(查询一天,查询一周,查询一个月的数据)...
  8. java解析xml中文字符乱码_Eclipse读取xml中文乱码问题解决
  9. python原理与架构_Python:爬虫原理和网页构造
  10. java 树状 子节点_java构建树形列表(带children属性)