JDBC——编程式事务的实现逻辑

引言

数据库事务的概念和基础,总结在《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() 块并回滚事务。这也是一个需要注意的点,即,我们要保证事务中的每一处发生异常时,都可以成功回滚

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/558677.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

排序算法——插入排序

一、算法思想 给定一个无序数列&#xff0c;模拟一个指针从第0位开始向后&#xff0c;始终保持当前位置左边的数列是有序的。 指针位置上的元素依次与前面的元素比较&#xff0c;当遇到小于自己的数或右边已经没有元素时&#xff0c;都停止比较&#xff0c;开始下一轮。 生活…

Linux 底层原理 —— epoll 与多路复用

引言 epoll 是 Linux 系统下高性能网络服务的必备技术&#xff0c;很多面试中高频出现的 Nginx、Redis 都使用了这一技术&#xff0c;本文总结 linux 多路复用模型的演变过程&#xff0c;看一看epoll 是如何实现高性能的。 一、相关基础知识 1.1 文件描述符 文件描述符&…

异或运算的应用

一、基础知识 异或运算&#xff0c;相异为1。 异或运算是一种常用的位运算&#xff0c;在算法题中&#xff0c;对于避免额外的空间复杂度有独特的用处。 异或运算也被称为“无进位相加”&#xff0c;它具有以下特性&#xff1a; 特性1&#xff1a;0 ^ N N 特性2&#xff1a…

单向队列、双端队列、栈的模型实现

引言 自己实现简单的队列、栈的逻辑结构。 队列都包含头和尾两个指针&#xff0c;简单的单向队列只能在一端&#xff08;如&#xff1a;head端&#xff09;入列&#xff0c;在另一端&#xff08;如&#xff1a;tail 端&#xff09;出列&#xff1b;双端队列可以在 head 进出&…

递归算法及其时间复杂度分析

引言 “递归” 一词是比较专业的计算机术语&#xff0c;在现实生活中&#xff0c;有一个更可爱的词——“套娃”。如果把“递归算法”叫做“套娃算法”&#xff0c;或许可以减少一些恐惧程度。 套娃是有限的&#xff0c;同样&#xff0c;递归也是有限的&#xff0c;这和我们经…

算法设计中的基础常用代码

引言 本篇博客旨在记录一些基础算法知识的常见组合用法&#xff0c;以及何时使用&#xff0c;需要注意的问题等&#xff0c;长期更新。 为什么要这样总结呢&#xff1f;难道掌握了位运算、常用算法工具API的定义还不够吗&#xff1f; 这是因为某些知识比如 &、 |、 ~、 …

Redis —— 常用命令一览

引言 参考《菜鸟教程 Redis 常用命令》&#xff0c;其中红色为极其重要&#xff0c;蓝色为重要。 一、总览 二、key相关命令 三、String 相关命令 四、Hash 相关命令 五、List 相关命令 六、Set 相关命令 七、ZSet 相关命令

Redis 实用技术——消息发布和订阅

引言 发布订阅模型是redis的重要功能&#xff0c;它可以像网站动态一样&#xff0c;将消息发送到多个订阅者的主页里。 一、常用命令 二、消息格式 消息是一个有三个元素的多块响应&#xff1a; 如上图&#xff0c;发布者向 mysub 频道发送了一条消息&#xff0c;redis会返回…

Redis 实用技术——事务

引言 redis的事务不像关系型数据库的事务那样完整。 “快”是redis的特征&#xff0c;在事务管理的过程中&#xff0c;使用muti命令开启事务块&#xff0c;当输入多条命令后&#xff0c;再使用exec命令执行事务块中的全部命令。 Redis事务可以保证两件事&#xff1a; 1、隔…

排序算法——归并排序的相关问题

一、小和问题 问题描述&#xff0c;给定一个数组&#xff0c;如[1, 3, 2, 6, 5]&#xff0c;计算每个数左边小于自己的所有数的和&#xff0c;并累加。例如&#xff1a; 1左边没有数 3左边有一个小于自己的数 1 2左边有一个小于自己的数 1 6左边有三个小于自己的数 1 3 2 6…

经典数据结构——堆的实现

一、完全二叉树 堆是一种完全二叉树&#xff0c;什么是完全二叉树&#xff1f; 简单的说&#xff0c;一棵满二叉树表示的是所有节点全部饱和&#xff0c;最后一层全部占满&#xff1a; 而完全二叉树指的是满二叉树的最后一层&#xff0c;所有叶子节点都从左往顺序排满&#x…

排序算法 —— 堆排序

引言 此文基于《经典数据结构——堆的实现》中堆结构&#xff0c;实现一个以堆处理排序的算法。 一、算法思想 基于堆结构的堆排序的算法思想非常简单&#xff0c;循环获取大根堆中的最大值&#xff08;0位置的根节点&#xff09;放到堆的末尾&#xff0c;直到将堆拿空。 由…

经典数据结构——前缀树

引言 前缀树——trie /ˈtraɪ//树&#xff0c;也叫作“单词查找树”、“字典树”。 它属于多叉树结构&#xff0c;典型应用场景是统计、保存大量的字符串&#xff0c;经常被搜索引擎系统用于文本词频统计。它的优点是利用字符串的公共前缀来减少查找时间&#xff0c;最大限度…

排序算法 —— 计数排序

引言 计数排序是桶排序思想的一种具体实现&#xff0c;针对一些具有特殊限制的样本数据&#xff0c;如公司员工年龄&#xff0c;那么样本数据本身就一定在0~200之间&#xff0c;针对这样的数据&#xff0c;使用从0到200 的桶数组&#xff0c;桶的位置已经是有序的&#xff0c;…

Java多线程 —— 线程状态迁移

引言 线程状态迁移&#xff0c;又常被称作线程的生命周期&#xff0c;指的是线程从创建到终结需要经历哪些状态&#xff0c;什么情况下会出现哪些状态。 线程的状态直接关系着并发编程的各种问题&#xff0c;本文就线程的状态迁移做一初步探讨&#xff0c;并总结在何种情况下…

Java中的Unsafe

Java和C语言的一个重要区别就是Java中我们无法直接操作一块内存区域&#xff0c;不能像C中那样可以自己申请内存和释放内存。Java中的Unsafe类为我们提供了类似C手动管理内存的能力。 Unsafe类&#xff0c;全限定名是sun.misc.Unsafe&#xff0c;从名字中我们可以看出来这个类对…

arm中断保护和恢复_浅谈ARM处理器的七种异常处理

昨天的文章&#xff0c;我们谈了ARM处理器的七种运行模式&#xff0c;分别是&#xff1a;用户模式User(usr)&#xff0c;系统模式System(sys)&#xff0c;快速中断模式(fiq)&#xff0c;管理模式Supervisor(svc)&#xff0c;外部中断模式(irq)&#xff0c;数据访问中止模式Abor…

Queue —— JUC 的豪华队列组件

目录引言一、Queue 的继承关系1.1 Queue 定义基础操作1.2 AbstractQueue 为子类减负1.3 BlockingQueue 阻塞式Queue1.4 Deque 两头进出二、Queue 的重要实现三、BlockingQueue 的实现原理四、Queue 在生产者消费者模式中的应用五、Queue 在线程池中的应用六、ConcurrentLinkedQ…

daad转换器实验数据_箔芯片电阻在高温应用A/D转换器中的应用

工业/应用领域高温&#xff1a;地震数据采集系统、石油勘探监测、高精度检测仪产品采用&#xff1a;V5X5 Bulk Metal (R) Foil芯片电阻案例介绍TX424是一个完整的4通道24位模数转换器&#xff0c;采用40脚封装。该设计采用最先进设计方案&#xff0c;两个双通道24位调节器和一个…

excel分段排序_学会这个神操作,报表填报不再五花八门,效率远超Excel

在报表工作人员的的日常工作中&#xff0c;常常要面临统计混乱的终端用户输入的问题。由于无法准确限制用户的输入内容&#xff0c;所以在最终进行数据统计时&#xff0c;常常会出现数据不合法的情况。为此需要花费大量的人力和时间核对校验数据。举个简单的例子&#xff0c;某…