【JDBC】转账案例(二)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 【JDBC】转账案例

  1. 处理思路
  1. 核心思路:

让转账的两个动作:减钱,加钱  必须同时成功或者是同时失败

  1. 可选技术:

数据库的事务。

  1. 事务的概述
  1. 事务指的是逻辑上的一组操作,组成这组操作的各个单元要么全成功,要么全失败.
  2. 事务作用:保证一组操作要么全都成功,对数据库进行完整更新要么在某一个动作失败的时候让数据恢复原状,不会引起不完整的修改。
  1. MySQL事务的操作

sql语句

描述

start transaction;

开启事务

commit;

提交事务(完整更新)

rollback;

回滚事务(恢复原状)


MYSQL中可以有两种方式进行事务的管理:

自动提交:MySql默认自动提交。即执行一条sql语句提交一次事务。

手动提交:先开启,再提交

方式1:手动提交(当执行手动提交的时候自动提交会暂停)


start transaction;
update account set money=money-1000 where name='守义';
update account set money=money+1000 where name='凤儿';
commit;
#或者
rollback;


  1. 方式2:自动提交,通过修改mysql全局变量“autocommit”进行控制

show variables like '%commit%';


04fa7304c32a48c9832a9e480ee79cab.png

* 设置自动提交的参数为OFF:

set autocommit = 0;  -- 0:OFF  1:ON

  1. &bsp;&bsp;测试利用事物实现转账1块钱 顺利完成情况
  2. START TRANSACTION;  -- 开启事物


-- 执行一组操作


UPDATE account SET money=money-1 WHERE NAME='张三';


UPDATE account SET money=money+1 WHERE NAME='李四';


COMMIT; -- 提交事物


执行前:

27b5dfcf653a4cf99cc7779d8bd33be9.png

执行后:

cd568d8256ec4d449d4acfe62ee2906d.png

  1. &bsp;&bsp;测试利用事物实现转账1块钱 出现问题并回滚(恢复原状)


# 测试利用事物实现转账1块钱


START TRANSACTION;  -- 开启事物


-- 执行一组操作


UPDATE account SET money=money-1 WHERE NAME='张三';


-- 下一句发生错误


UPDATE account SET money=money+1 WHERE NAME  &……&%&……¥(*&* ='李四';


ROLLBACK; -- 回滚(恢复原状)


执行前:

f8f39c2048f5466f9aab1aed043ade70.png

执行如下两句:


6fae93a9e6fe4c3e8265b50c0c2b4750.png

执行效果:

ec35e4fb663546cfaa474c1494fb6c8d.png

执行下面一句

c3180b75988144118a102bd75c78e695.png


这一组操作的第二个给李四加钱执行失败了 ,李四的钱并不会改变


db58686b043f4348867a3836f691945e.png


此时一组操作没有全部成功,需要回滚来让数据恢复原状


f3399e1feab342fbb7e567c5ed31b9e5.png


  1. &bsp;JDBC事务操作


Connection对象的方法名

描述

conn.setAutoCommit(false)

设置关闭自动提交,(开启事务)

conn.commit()

提交事务

conn.rollback()

回滚事务


利用如下模板解决问题


//事务模板代码
public void demo01() throws SQLException{
// 获得连接
Connection conn = ...;
try {
//#1关闭自动提交事物(开始事务)
conn.setAutoCommit(false);
//.... 加钱 ,减钱
//#2 手动提交事务
conn.commit();
} catch (Exception e) {
//#3 手动回滚事务
conn.rollback();
} finally{
// 释放资源
conn.close();
}
}
  1. 解决问题
  1. Dao层

把原来的两个方法进行修改,使用Service层传递过来的conn对象,并且执行完毕不要关闭链接

/*
 * 从指定的账户中取钱(减钱)
 * */
public void outMoney(String name,int money,Connection conn) throws Exception{
//2.发射器
String sql = "UPDATE account SET money=money-? WHERE NAME=?";
PreparedStatement pst = conn.prepareStatement(sql);
pst.setInt(1, money);
pst.setString(2, name);
//3.发射
int num = pst.executeUpdate();
//4.处理结果
System.out.println("本次执行的影响的行数是: num="+num);
//5.关闭资源 只关闭结果集不关闭链接
DruidUtil.closeAll(null, pst, null);
}
/*
 * 从指定的账户中存钱(加钱)
 * */
public void inMoney(String name,int money,Connection conn) throws Exception{
//2.发射器
String sql = "UPDATE account SET money=money+? WHERE NAME=?";
PreparedStatement pst = conn.prepareStatement(sql);
pst.setInt(1, money);
pst.setString(2, name);
//3.发射
int num = pst.executeUpdate();
//4.处理结果
System.out.println("本次执行的影响的行数是: num="+num);
//5.关闭资源 只关闭结果集不关闭链接
DruidUtil.closeAll(null, pst, null);
}
  1. Service层

 获取连接,关闭自动提交变成手动提交,一组动作成功则手动提交事务,一旦有异常则回滚,最后无论异常与否都要关闭连接

public void transfer(String srcName,String descName,int money){
AccountDao ad = new AccountDao();
Connection conn =null;
try {
//获取链接
conn = DruidUtil.getConn();
//把自动提交关闭,变成手动提交
conn.setAutoCommit(false);
// -钱  传递连接对象
ad.outMoney(srcName, money,conn);
//制造一个bug , 模拟转账出现问题
int a=1/0;
// +钱  传递连接对象
ad.inMoney(descName, money,conn);
//转账成功则手动提交事物
conn.commit();
System.out.println("转账完毕 ");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
if(conn!=null){
try {
//操作失败 回滚
conn.rollback();
System.out.println("执行了回滚 ,把数据恢复原状 ");
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}finally {
if(conn!=null){
try {
//关闭链接
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
  1. 测试
  2. 正常情况:

把 如下代码注释上


b350460c94e54b58b41680c60956f135.png


执行前:

aaa80ccc7849454aa3192ab3ace9abdd.png

转账2块钱执行完毕


0500886dd1c04e29bf64caf7e90d9467.png

执行结果

09f7e765d889404c8425d0018d5ce1c0.png

  1. 异常情况:

保留如下代码


ad924023449a431bbab658bb3e721a62.png

执行前:

e2159ba912f4462f8cf4146b889a5390.png


执行效果


f1aa43c5879d47138819c21a442cd3ed.png


执行后:数据恢复原状,问题解决


2492bdec10054f7db11526956ba912c4.png


  1. 理论补充

事务特性:ACID


原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

一致性(Consistency)事务前后数据的完整性必须保持一致。

隔离性(Isolation)事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。

持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。


并发访问问题


如果不考虑隔离性,事务存在3种并发访问问题。

  1. 脏读:一个事务读到了另一个事务未提交的数据.


5eb0face82584316ba1eff3f676ae7fa.png


  1. 不可重复读:一个事务读到了另一个事务已经提交(update)的数据。引发另一个事务,在事务中的多次查询结果不一致。


aaec66376c0e4724b837b7bb65cf6d4b.png

虚读 /幻读:一个事务读到了另一个事务已经提交(insert)的数据。导致另一个事务,在事务中多次查询的结果不一致。(数据量不同)


b075b8f7c2b24af792a439142362f8bf.png

严重性: 脏读 > 不可重复读 >虚读(幻读)

设置隔离级别:解决问题

数据库规范规定了4种隔离级别,分别用于描述两个事务并发的所有情况。

read uncommitted 读未提交,一个事务读到另一个事务没有提交的数据。

存在:3个问题(脏读、不可重复读、虚读)。

解决:0个问题

效率最高,引发所有读问题


基本不设置


read committed 读已提交,一个事务读到另一个事务已经提交的数据。

存放:2个问题(不可重复读、虚读)。

解决:1个问题(脏读)

如果要 效率,那么选择这个read committed


repeatable read :可重复读,在一个事务中读到的数据信息始终保持一致,无论另一个事务是否提交。

存放:1个问题(虚读)。

解决:2个问题(脏读、不可重复读)

如果 要求安全,选择这个repeatable read


虚读的问题可以通过程序来规避:


事务刚开启时,可以count(*)

事务要关闭时,可以count(*)

比对,如果两次数据一致,说明没有虚读

serializable 串行化,同时只能执行一个事务,相当于事务中的单线程。

存放:0个问题。

解决:1个问题(脏读、不可重复读、虚读)

没有效率,安全性最高,基本不设置


安全和性能对比

安全性:serializable > repeatable read > read committed > read uncommitted

性能 : serializable < repeatable read < read committed < read uncommitted

常见数据库的默认隔离级别:

MySql:repeatable read  安全,本身做的优化比较好

Oracle:read committed  效率


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
7月前
|
Java 数据库连接
登录页面JDBC案例
登录页面JDBC案例
87 0
|
7月前
|
Java 关系型数据库 MySQL
【JVM】JDBC案例打破双亲委派机制
【JVM】JDBC案例打破双亲委派机制
170 4
|
6月前
|
SQL 前端开发 Java
2024考古之还在用原始JDBC开发 手搓 案例 实现一个模块的增删改
2024考古之还在用原始JDBC开发 手搓 案例 实现一个模块的增删改
40 0
|
7月前
|
SQL Java 关系型数据库
JDBC PreparedStatement 字段值为null导致TBase带宽飙升的案例分析
JDBC PreparedStatement 字段值为null导致TBase带宽飙升的案例分析
115 0
|
7月前
|
SQL Java 数据库连接
JDBC增删改查案例讲解
JDBC增删改查案例讲解
36 0
|
Oracle 关系型数据库 Java
分享一个 Oracle RAC 模式下客户端建立JDBC初始连接时因ONS造成应用启动时卡顿30秒问题的排查分析案例
分享一个 Oracle RAC 模式下客户端建立JDBC初始连接时因ONS造成应用启动时卡顿30秒问题的排查分析案例
|
Java 关系型数据库 MySQL
Java Web实战 | JDBC案例:实现图书管理
在项目开发中,应用程序需要的数据基本都是存放在数据库中的。对数据的管理过程离不开数据库。本文将运用JDBC API编写一个实现基本数据库操作(添加、修改、删除、查询)的应用程序,实现对图书信息的管理。 完成此项目的具体步骤如下。
325 0
Java Web实战 | JDBC案例:实现图书管理
|
监控 Java Spring
Spring JDBC-实施Spring AOP事务注意事项及案例分析
Spring JDBC-实施Spring AOP事务注意事项及案例分析
143 0
|
Java 数据库连接 数据库
【JDBC】转账案例(一)
【JDBC】转账案例
117 0
【JDBC】转账案例(一)
|
2月前
|
Java 关系型数据库 MySQL
mysql5.7 jdbc驱动
遵循上述步骤,即可在Java项目中高效地集成MySQL 5.7 JDBC驱动,实现数据库的访问与管理。
207 1