JDBC03 利用JDBC实现事务提交与回滚【调用Connection中的方法实现事务管理】
目录
1 Connection中的重用方法
2 JDBC事务管理经典案例
1 Connection类中常用的方法回顾
1.1 Statement createStatement() throws SQLException;
创建一个Statement实例(即:创建一个SQL执行对象)
1.2 PreparedStatement prepareStatement(String sql) throws SQLException;
创建一个PreparedStatement对象(即:创建一个预编译SQL执行对象)
1.3 void setAutoCommit(boolean autoCommit) throws SQLException;
设置事务的自动提交(false为关闭自动提交,true为启动自动提交)
1.4 void commit() throws SQLException;
手动提交事务
1.5 void rollback() throws SQLException;
手动回滚事务
2 需要用到事务回滚的经典案例:银行转账案例
转出和转入是一个事务,如果转出成功但是转入失败的会就需要进行事务回滚,否则就出出现转出者余额减少但是转入者余额没有增加
注意:事务的提交与回滚是通过Connection提供的方法来调用的;本质上事务还是依赖数据库的实现;Connection的方法实质上也是调用了数据库事务机制.
2.1 不使用事务控制的转账业务
缺点:如果转入成功,但是转入失败的话,会造成转出者余额减少,但是转入者余额不变
项目结构图
- package cn.xiangxu.entity;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.util.Scanner;
- import cn.xiangxu.tools.DBUtil;
- public class Test {
- public static void main(String[] args) {
- Scanner scanner = new Scanner(System.in);
- System.out.println("请输入转出用户名:");
- String outName = scanner.nextLine();
- System.out.println("请输入需要转出的资金额度:");
- Double money = Double.parseDouble(scanner.nextLine());
- System.out.println("请输入转入用户名:");
- String inName = scanner.nextLine();
- System.out.println("转出账户为:" + outName + "转出金额为:" + money + "转入账户为:" + inName);
- Connection conn = null;
- try {
- conn = DBUtil.getConnection(); // 实例化连接对象
- // conn.setAutoCommit(false); // 关闭自动提交事务功能
- String sql = "UPDATE client "
- + "SET account = account - ? "
- + "WHERE name = ? ";
- PreparedStatement ps = conn.prepareStatement(sql);
- ps.setDouble(1, money);
- ps.setString(2, outName);
- Integer rs = ps.executeUpdate();
- if(rs > 0) {
- System.out.println("转出成功");
- } else {
- System.out.println("转出失败");
- return; // 转出失败跳出函数,不再执行下面的语句;但是finally中的语句还是会执行的,因为就算天塌下来finally中的语句都会执行
- }
- System.out.println("======分割线=======");
- String sql_in = "UPDATE client "
- + "SET account = account + ? "
- + "WHERE name = ? ";
- PreparedStatement ps_in = conn.prepareStatement(sql_in);
- ps_in.setDouble(1, money);
- ps_in.setString(2, inName);
- Integer judge_in = ps_in.executeUpdate();
- if(judge_in > 0) {
- System.out.println("转入成功");
- // conn.commit(); // 转出、转入都成功就提交事务
- } else {
- System.out.println("转入失败");
- // conn.rollback(); // 转出成功、转入失败就回滚事务
- }
- // conn.setAutoCommit(true); // 打开自动提交事务
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } finally {
- System.out.println("我是finally中的语句哟");
- try {
- DBUtil.closeConnection();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
转账业务java源代码
- CREATE TABLE client (
- id INT (10) PRIMARY KEY,
- name VARCHAR (10),
- pwd VARCHAR (10),
- account INT (20)
- );
SQL语句
- package cn.xiangxu.tools;
- import java.io.IOException;
- import java.io.InputStream;
- import java.sql.Connection;
- import java.sql.SQLException;
- import java.util.Properties;
- import org.apache.commons.dbcp.BasicDataSource;
- public class DBUtil {
- /*
- * ThreadLocal用于线程跨方法共享数据使用
- * ThreadLocal内部有一个Map, key为需要共享数据的线程本身,value就是其需要共享的数据
- */
- private static ThreadLocal<Connection> tl; // 声明一个类似于仓库的东西
- private static BasicDataSource dataSource; // 声明一个数据库连接池对象
- // 静态代码块,在类加载的时候执行,而且只执行一次
- static {
- tl = new ThreadLocal<Connection>(); // 实例化仓库对象
- dataSource = new BasicDataSource(); // 实例数据库连接池对象
- Properties prop = new Properties(); // 创建一个Properties对象用(该对象可以用来加载配置文件中的属性列表)
- InputStream is = DBUtil.class.getClassLoader().getResourceAsStream("config/mysql.properties"); // 读取配置文件信息
- try {
- prop.load(is); // 加载配置文件中的属性列表
- String driverClassName = prop.getProperty("driverClassName"); // 获取属性信息
- String url = prop.getProperty("url");
- String username = prop.getProperty("username");
- String password = prop.getProperty("password");
- Integer maxActive = Integer.parseInt(prop.getProperty("maxActive"));
- Integer maxWait = Integer.parseInt(prop.getProperty("maxWait"));
- dataSource.setDriverClassName(driverClassName); // 初始化数据库连接池(即:配置数据库连接池的先关参数)
- dataSource.setUrl(url);
- dataSource.setUsername(username);
- dataSource.setPassword(password);
- dataSource.setMaxActive(maxActive);
- dataSource.setMaxWait(maxWait);
- is.close(); // 关闭输入流,释放资源
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- /**
- * 创建连接对象(注意:静态方法可以直接通过类名来调用)
- * @return 连接对象
- * @throws Exception
- */
- public static Connection getConnection() throws Exception {
- try {
- Connection conn = dataSource.getConnection(); // 创建连接对象(利用数据库连接池进行创建)
- tl.set(conn); // 将连接对象放到仓库中
- return conn;
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- throw e;
- }
- }
- /**
- * 关闭连接对象(注意:静态方法可以通过类名直接调用)
- * @throws Exception
- */
- public static void closeConnection() throws Exception {
- Connection conn = tl.get(); // 从仓库中取出连接对象
- tl.remove(); // 清空仓库
- if(conn != null) { // 判断连接对象是否释放资源
- try {
- conn.close();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- throw e;
- }
- }
- }
- }
数据库连接池的java源代码
- # zhe shi zhu shi , yi ban bu yong zhong wen
- # deng hao liang bian mei you kong ge, mo wei mei you fen hao
- # hou mian bu neng you kong ge
- driverClassName=com.mysql.jdbc.Driver
- url=jdbc:mysql://localhost:3306/test
- username=root
- password=182838
- maxActive=100
- maxWait=3000
数据库信息文件
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>cn.xiangxu</groupId>
- <artifactId>testJDBC</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <dependencies>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>5.1.37</version>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.12</version>
- </dependency>
- <dependency>
- <groupId>commons-dbcp</groupId>
- <artifactId>commons-dbcp</artifactId>
- <version>1.4</version>
- </dependency>
- </dependencies>
- </project>
maven依赖文件
2.2 利用事务控制的转账业务
- package cn.xiangxu.entity;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.SQLException;
- import java.util.Scanner;
- import cn.xiangxu.tools.DBUtil;
- public class Test {
- public static void main(String[] args) {
- Scanner scanner = new Scanner(System.in);
- System.out.println("请输入转出用户名:");
- String outName = scanner.nextLine();
- System.out.println("请输入需要转出的资金额度:");
- Double money = Double.parseDouble(scanner.nextLine());
- System.out.println("请输入转入用户名:");
- String inName = scanner.nextLine();
- System.out.println("转出账户为:" + outName + "转出金额为:" + money + "转入账户为:" + inName);
- Connection conn = null;
- try {
- conn = DBUtil.getConnection(); // 实例化连接对象
- conn.setAutoCommit(false); // 关闭自动提交事务功能
- String sql = "UPDATE client "
- + "SET account = account - ? "
- + "WHERE name = ? ";
- PreparedStatement ps = conn.prepareStatement(sql);
- ps.setDouble(1, money);
- ps.setString(2, outName);
- Integer rs = ps.executeUpdate();
- if(rs > 0) {
- System.out.println("转出成功");
- } else {
- System.out.println("转出失败");
- return; // 转出失败跳出函数,不再执行下面的语句;但是finally中的语句还是会执行的,因为就算天塌下来finally中的语句都会执行
- }
- System.out.println("======分割线=======");
- String sql_in = "UPDATE client "
- + "SET account = account + ? "
- + "WHERE name = ? ";
- PreparedStatement ps_in = conn.prepareStatement(sql_in);
- ps_in.setDouble(1, money);
- ps_in.setString(2, inName);
- Integer judge_in = ps_in.executeUpdate();
- if(judge_in > 0) {
- System.out.println("转入成功");
- conn.commit(); // 转出、转入都成功就提交事务
- } else {
- System.out.println("转入失败");
- conn.rollback(); // 转出成功、转入失败就回滚事务
- }
- conn.setAutoCommit(true); // 打开自动提交事务
- } catch (Exception e) {
- // TODO Auto-generated catch block
- try {
- conn.rollback(); // 捕获到异常后也需要进行事务回滚
- } catch (SQLException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
- e.printStackTrace();
- } finally {
- System.out.println("我是finally中的语句哟");
- try {
- DBUtil.closeConnection();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
转账业务的java源代码
2.3 将关闭自动提交功能、手动提交功能、手动回滚功能封装到一个类中
- package cn.xiangxu.tools;
- import java.io.IOException;
- import java.io.InputStream;
- import java.sql.Connection;
- import java.sql.SQLException;
- import java.util.Properties;
- import org.apache.commons.dbcp.BasicDataSource;
- public class DBUtil {
- /*
- * ThreadLocal用于线程跨方法共享数据使用
- * ThreadLocal内部有一个Map, key为需要共享数据的线程本身,value就是其需要共享的数据
- */
- private static ThreadLocal<Connection> tl; // 声明一个类似于仓库的东西
- private static BasicDataSource dataSource; // 声明一个数据库连接池对象
- // 静态代码块,在类加载的时候执行,而且只执行一次
- static {
- tl = new ThreadLocal<Connection>(); // 实例化仓库对象
- dataSource = new BasicDataSource(); // 实例数据库连接池对象
- Properties prop = new Properties(); // 创建一个Properties对象用(该对象可以用来加载配置文件中的属性列表)
- InputStream is = DBUtil.class.getClassLoader().getResourceAsStream("config/mysql.properties"); // 读取配置文件信息
- try {
- prop.load(is); // 加载配置文件中的属性列表
- String driverClassName = prop.getProperty("driverClassName"); // 获取属性信息
- String url = prop.getProperty("url");
- String username = prop.getProperty("username");
- String password = prop.getProperty("password");
- Integer maxActive = Integer.parseInt(prop.getProperty("maxActive"));
- Integer maxWait = Integer.parseInt(prop.getProperty("maxWait"));
- dataSource.setDriverClassName(driverClassName); // 初始化数据库连接池(即:配置数据库连接池的先关参数)
- dataSource.setUrl(url);
- dataSource.setUsername(username);
- dataSource.setPassword(password);
- dataSource.setMaxActive(maxActive);
- dataSource.setMaxWait(maxWait);
- is.close(); // 关闭输入流,释放资源
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- /**
- * 创建连接对象(注意:静态方法可以直接通过类名来调用)
- * @return 连接对象
- * @throws Exception
- */
- public static Connection getConnection() throws Exception {
- try {
- Connection conn = dataSource.getConnection(); // 创建连接对象(利用数据库连接池进行创建)
- tl.set(conn); // 将连接对象放到仓库中
- return conn;
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- throw e;
- }
- }
- /**
- * 关闭连接对象(注意:静态方法可以通过类名直接调用)
- * @throws Exception
- */
- public static void closeConnection() throws Exception {
- Connection conn = tl.get(); // 从仓库中取出连接对象
- tl.remove(); // 清空仓库
- if(conn != null) { // 判断连接对象是否释放资源
- try {
- conn.close();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- throw e;
- }
- }
- }
- /**
- * 在执行SQL语句前关闭JDBC的自动提交事务功能
- * @throws SQLException
- */
- public static void tansBegin() throws SQLException {
- try {
- tl.get().setAutoCommit(false); // 从仓库中获取连接对象并调用setAutoCommit来关闭自动提交事务功能
- } catch(SQLException e) {
- e.printStackTrace();
- throw e;
- }
- }
- /**
- * 手动回滚功能
- * @throws SQLException
- */
- public static void transBack() throws SQLException {
- tl.get().rollback(); // 从仓库中获取连接对象并调用rollback来实现事务回滚操作
- tl.get().setAutoCommit(true); // 回滚启动事务自动提交功能
- }
- /**
- * 手动提交功能
- * @throws SQLException
- */
- public static void transCommit() throws SQLException {
- tl.get().commit(); // 从仓库中获取连接对象并调用commit来实现事务提交操作
- tl.get().setAutoCommit(true); // 提交后启动事务自动提交功能
- }
- }
DBUtil
- package cn.xiangxu.entity;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.SQLException;
- import java.util.Scanner;
- import cn.xiangxu.tools.DBUtil;
- public class Test {
- public static void main(String[] args) {
- Scanner scanner = new Scanner(System.in);
- System.out.println("请输入转出用户名:");
- String outName = scanner.nextLine();
- System.out.println("请输入需要转出的资金额度:");
- Double money = Double.parseDouble(scanner.nextLine());
- System.out.println("请输入转入用户名:");
- String inName = scanner.nextLine();
- System.out.println("转出账户为:" + outName + "转出金额为:" + money + "转入账户为:" + inName);
- Connection conn = null;
- try {
- conn = DBUtil.getConnection(); // 实例化连接对象
- DBUtil.tansBegin(); // 关闭自动提交事务功能
- String sql = "UPDATE client "
- + "SET account = account - ? "
- + "WHERE name = ? ";
- PreparedStatement ps = conn.prepareStatement(sql);
- ps.setDouble(1, money);
- ps.setString(2, outName);
- Integer rs = ps.executeUpdate();
- if(rs > 0) {
- System.out.println("转出成功");
- } else {
- System.out.println("转出失败");
- return; // 转出失败跳出函数,不再执行下面的语句;但是finally中的语句还是会执行的,因为就算天塌下来finally中的语句都会执行
- }
- System.out.println("======分割线=======");
- String sql_in = "UPDATE client "
- + "SET account = account + ? "
- + "WHERE name = ? ";
- PreparedStatement ps_in = conn.prepareStatement(sql_in);
- ps_in.setDouble(1, money);
- ps_in.setString(2, inName);
- Integer judge_in = ps_in.executeUpdate();
- if(judge_in > 0) {
- System.out.println("转入成功");
- DBUtil.transCommit(); // 转出、转入都成功就提交事务
- } else {
- System.out.println("转入失败");
- DBUtil.transBack(); // 转出成功、转入失败就回滚事务
- }
- } catch (Exception e) {
- // TODO Auto-generated catch block
- try {
- DBUtil.transBack();// 捕获到异常后也需要进行事务回滚
- } catch (SQLException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
- e.printStackTrace();
- } finally {
- System.out.println("我是finally中的语句哟");
- try {
- DBUtil.closeConnection();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
转账业务java源代码
JDBC03 利用JDBC实现事务提交与回滚【调用Connection中的方法实现事务管理】的更多相关文章
- 【转】批量复制操作(SqlBulkCopy)的出错处理:事务提交、回滚
原文地址:http://blog.csdn.net/westsource/article/details/6658109 默认情况下,批量复制操作作为独立的操作执行. 批量复制操作以非事务性方式发生, ...
- RocketMQ源码分析之RocketMQ事务消息实现原下篇(事务提交或回滚)
摘要: 事务消息提交或回滚的实现原理就是根据commitlogOffset找到消息,如果是提交动作,就恢复原消息的主题与队列,再次存入commitlog文件进而转到消息消费队列,供消费者消费,然后将原 ...
- mysql事务提交和回滚机制
应用场景: 银行取钱,从ATM机取钱,分为以下几个步骤 1 登陆ATM机,输入密码: 2 连接数据库,验证密码: 3 验证成功,获得用户信息,比如存款余额等: 4 用 ...
- 对mysql事务提交、回滚的错误理解
一.起因 begin或者START TRANSACTION开始一个事务 rollback事务回滚 commit 事务确认 人们对事务的解释如下:事务由作为一个单独单元的一个或多个SQL语句组成,如果其 ...
- 关于SAP的事务提交和回滚(LUW)
1 Sap的更新的类型 在sap中,可以使用CALL FUNCTION ... IN UPDATE TASK将多个数据更新绑定到一个database LUW中.程序使用COMMIT WORK提交修改请 ...
- 关于jave在oracle驱动下事务提交与回滚问题
一直以来,都觉得Connection假设设置了setAutoCommit(false)后.启动手工事务提交.必须手工进行commit或者rollback才行.今天正好遇到一个问题.结果大跌眼镜. 于是 ...
- MySQL事务提交与回滚
提交 为了演示效果,需要打开两个终端窗口,使用同一个数据库,操作同一张表 step1:连接 终端1:查询商品分类信息 select * from goods_cates; step2:增加数据 终端2 ...
- J2EE分布式事务中的提交、回滚方法调用异常。
这个是昨天上班的时候,写一个后台程序的调试程序时碰到的问题,和项目经理纠结了一天,最后搞定了.于是今天上班正好闲着,花了几乎一天的时间去网上找各种相关的资料.目前了解的内容如此: 根据使用的weblo ...
- nestd事务如果报错了 则回滚到外部事物保存点 且外部事物如果没异常的话 会正常提交 nested事务并不会提交;如果外部事物报错了 内部事务会一同回滚
nestd事务如果报错了 则回滚到外部事物保存点 且外部事物如果没异常的话 会正常提交 nested事务并不会提交:如果外部事物报错了 内部事务会一同回滚
随机推荐
- AI探索(四)NumPy库的使用
NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库. umPy 是一个运行速度非常快的数学库, ...
- CMC 实例管理
有人问我,用户用的BW-QUERY看报表挺快的,用了BO发现很慢. 我心想,不会是什么高级优化吧,我可不会. 发现用WEBI时看报表很慢.那这个还是好解决的. 前面说那种情况,解决方法我只知道一种上H ...
- ES6 类(Class)基本用法和静态属性+方法详解
原文地址:http://blog.csdn.net/pcaxb/article/details/53759637 ES6 类(Class)基本用法和静态属性+方法详解 JavaScript语言的传统方 ...
- New Concept English three (50)
31 39 The New Year is a time for resolutions. Mentally, at least, most of us could compile formidabl ...
- Gym - 100623J Just Too Lucky (数位dp)
给定n∈[1,1e12],求1到n的所有整数中,各位数字之和能整除它本身的数的个数. 这道题与UVA-11361类似,假如设dp[u][lim][m1][m2]为枚举到第u位(从低到高数),是否受限, ...
- UVA - 11107 Life Forms (广义后缀自动机)
题意:给你n个字符串,求出在超过一半的字符串中出现的所有子串中最长的子串,按字典序输出. 对这n个字符串建广义后缀自动机,建完后每个字符串在自动机上跑一遍,沿fail树向上更新所有子串结点的出现次数( ...
- LeetCode Maximum Length of Pair Chain
原题链接在这里:https://leetcode.com/problems/maximum-length-of-pair-chain/description/ 题目: You are given n ...
- LeetCode Perfect Number
原题链接在这里:https://leetcode.com/problems/perfect-number/#/description 题目: We define the Perfect Number ...
- bzoj 3887: Grass Cownoisseur Tarjan+Topusort
题目: 给一个有向图,然后选一条路径起点终点都为1的路径出来,有一次机会可以沿某条边逆方向走,问最多有多少个点可以被经过?(一个点在路径中无论出现多少正整数次对答案的贡献均为1) 题解: 首先考虑简单 ...
- 3143 codevs 二叉树的序遍历
题目描述 Description 求一棵二叉树的前序遍历,中序遍历和后序遍历 输入描述 Input Description 第一行一个整数n,表示这棵树的节点个数. 接下来n行每行2个整数L和R.第i ...