廖雪峰Java15JDBC编程-3JDBC接口-4JDBC事务
1 数据库事务:Transaction
1.1 定义
- 若干SQL语句构成的一个操作序列
- 要么全部执行成功
- 要么全部执行不成功
1.2 数据库事务具有ACID特性:
Atomicity:原子性
一个事务虽有若干SQL语句构成,但它本身是一个原子操作,要么全部成功,要么全部失败
Consistency:一致性
一个事务在开始前或结束之后,数据库的数据是完整的,不存在冲突和数据不一致的情况
Isolation:隔离型
多个事务并发执行的时候,事务之间是隔离的,一个事务不应该影响其他事务运行的结果
Durability:持久性
一个事务一旦成功完成,这个事务对数据库的更改会持久的保留在数据库中,并且不会被回滚。
1.3 为什么需要数据库事务?
例子:新建accounts表。
CREATE table accounts (id int not null auto_increment, name varchar(10), balance DECIMAL(5,2) ,PRIMARY KEY(id)) ENGINE=INNODB DEFAULT charset=utf8;
INSERT INTO accounts(name,balance) VALUES ("小明",550.00),("小红",120.00),("小军",870.60),("小白",120.00),("小兵",199.01);
转账操作,把100元从小明的账户转给小红。
update accounts set balance = balance - 100 where id = 1;
update accounts set balance = balance + 100 where id = 2;
情况一:如果这两条语句执行成功,小明的账户减少100,变为450元,小红的账号增加100,变为220元。

情况二:但如果在执行万第一条update语句后,由于各种原因,第二条update没有执行,会出现什么结果呢?
小明的钱已经扣了,小红的钱却未加上,这个时候应用逻辑出现了不一致的情况。

事务的作用:使用数据库事务可以保证多个SQL语句全部执行或全部取消。转账结果只有2种:
1.小明的钱成功转给了小红
2.小明的钱没有成功转给小红,退回小明账户。小明和小红的钱保持不变。
1.4 事务隔离级别
事务隔离是由数据库允许多个连接同时执行事务引起的。在Java程序中,当一个线程操作一个数据库连接的时候,另一个线程可能也在通过一个数据库连接更新数据。他们之间就会遇到隔离级别的问题。
事务隔离会遇到3个问题:
- 1.脏读 Dirty Read
- 2.非重复复读 Non Repeatable Read
- 3.幻读 Phantom
1.4.1 Dirty Read 脏读:B读到了A未提交成功的数据Bob
-- 事务A:
update students set name = 'Bob' where id = 1;
rollback;
-- 事务B:
select * from students where id = 1;
commit;
事务A修改了修改了一个数据,但还没有提交。这个时候,事务B读取了A没有提交的更新结果,如果事务A回滚,事务B读到的就是脏数据。
1.4.2 Non Repeatable Read 非重复读:在事务B中,2次select查询得到的结果不一致,这是因为事务A在2次select之间修改了数据,并且进行了提交。
而我们在同一个事务中,对于同样的select语句,希望返回同样的结果。这里出现的结果不一致的情况,就是Non Repeatable Read。
-- 事务A
update students set name='Bob' where id = 1;
commit;
-- 事务2
select * from students where id = 1; -- 小明
select * from students where id = 1; -- Bob
1.4.3 Phantom Read:幻读
在事务B中两次select条件为id=99的记录都是空,仿佛id=99的记录不存在。但实际上,事务A已经insert了一条id为99的记录。
当事务B第三次操作id=99的记录,就会多了一条记录。
-- 事务A
insert into students values(99, 1,'Bob', 'M');
commit;
-- 事务B
select * from students where id = 99; --empty
select * from students where id = 99; --empty
update students set name 'Alice' where id = 99;
commit;
select * from students where id = 99; --Alice
1.4.4 数据库隔离级别:为了避免这三种问题,数据库定义了数据库隔离级别
| isolation level | Dirty Read | Non Repeatable Read | Phantom Read |
| Read Uncommitted | 会出现 | 会出现 | 会出现 |
| Read Committed | 会出现 | 会出现 | |
| Repeatable Read | 会出现 | ||
| Serializable |
但是隔离级别越高,数据运行的时候,要加的锁就越多,能够同时并发执行的事务就越少,所以要选择一个合适的隔离级别。
2 JDBC事务代码
conn = openConnection();
try{
conn.setAutoCommit(false); //开始一个事务
//执行多条SQL语句
insert(); update(); delete();
conn.commit(); //提交一个事务
}catch(Exception e){
conn.rollback(); //出现异常,就回滚这个事务
}finally{
conn.setAutoCommit(true); //恢复数据库原来的事务模式,即结束事务
conn.close(); //关闭连接
}
package com.feiyangedu.sample.pop3;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class JdbcTx {
static final String JDBC_URL = "jdbc:mysql://localhost:13306/test0828?characterEncoding=utf8&useSSL=false&serverTimeZone=UTC";
static final String JDBC_USER = "root";
static final String JDBC_PASSWORD = "123456";
public static void main(String[] args) throws SQLException{
List<Student> students = getAllStudents();
for(Student student:students){
System.out.println(student);
}
Connection conn = null;
try{
conn = getConnection();
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
conn.setAutoCommit(false);
updateName(conn, students.get(0).id, "Bob");
updateName(conn, students.get(1).id, "Alice");
conn.commit();
System.out.println("commit ok");
}catch (Exception e){
e.printStackTrace();
conn.rollback();
}finally {
if(conn != null){
try{
conn.setAutoCommit(true);
conn.close();
}catch (SQLException e){
System.err.println(e);
}
}
}
System.out.println("第一次事务结束后");
students = getAllStudents();
for(Student student:students){
System.out.println(student);
}
try{
conn = getConnection();
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
conn.setAutoCommit(false);
updateName(conn, students.get(0).id,"张三");
updateName(conn, students.get(1).id,"李四");
throw new RuntimeException("第二次事务出错");
}catch (Exception e){
conn.rollback();
}finally {
if(conn != null){
try{
conn.setAutoCommit(true);
conn.close();
}catch (Exception e){
System.err.println(e);
}
}
}
System.out.println("第二次事务结束");
students = getAllStudents();
for(Student student:students){
System.out.println(student);
}
}
static void updateName(Connection conn, long id, String name) throws SQLException{
try(PreparedStatement ps = conn.prepareStatement("update students set name=? where id=?")){
ps.setObject(1, name);
ps.setObject(2, id);
ps.executeUpdate();
}
}
static List<Student> getAllStudents() throws SQLException{
try(Connection conn = getConnection()){
try(PreparedStatement ps = conn.prepareStatement("select * from students")){
ResultSet rs = ps.executeQuery();
List<Student> list = new ArrayList<>();
while(rs.next()){
long id = rs.getLong("id");
long classId = rs.getLong("class_id");
String name = rs.getString("name");
String gender = rs.getString("gender");
list.add(new Student(id,classId,name,gender));
}
return list;
}
}
}
static Connection getConnection() throws SQLException {
return DriverManager.getConnection(JDBC_URL,JDBC_USER,JDBC_PASSWORD);
}
}

3. 数据库事务总结
- 具有ACID特性
* Atomicity:原子性
* Consistency:一致性
* Isolation:隔离型
* Durability:持久性 - JDBC提供了事务的支持
廖雪峰Java15JDBC编程-3JDBC接口-4JDBC事务的更多相关文章
- 廖雪峰Java15JDBC编程-3JDBC接口-5JDBC连接池
1. JDBC连接池 1.1 JDBC连接池简介 线程池可以复用一个线程,这样大量的小任务通过线程池的线程执行,就可以避免反复创建线程带来的开销. 同样JDBC可以复用一个JDBC连接 JDBC的连接 ...
- 廖雪峰Java15JDBC编程-3JDBC接口-1JDBC简介
JDBC:Java DataBase Connectivity Java程序访问数据库的标准接口 使用Java程序访问数据库的时候,Java代码并不是直接通过TCP连接去访问数据库,而是通过JDBC接 ...
- 廖雪峰Java15JDBC编程-3JDBC接口-3JDBC更新
使用update语句的时候,需要通过JDBC实现update语句的执行,这个时候仍然通过PreparedStatement对象来使用,直接传入update语句,然后通过setObject传入占位符的值 ...
- 廖雪峰Java15JDBC编程-3JDBC接口-2JDBC查询
我们可以使用JDBC查询来执行select语句. 1. Statement try(Connection conn = DriverManager.getConnection(JDBC_URL, JD ...
- 廖雪峰Java15JDBC编程-2SQL入门-2insert/select/update/delete
1. INSERT用于向数据库的表中插入1条记录 insert into 表名 (字段1,字段2,...) values (数据1,数据2,数据3...) 示例 -- 如果表存在,就删除 drop t ...
- 廖雪峰Java15JDBC编程-2SQL入门-1SQL介绍
1.SQL:结构化查询语言 Structured Query Language 针对关系数据库设计 各种数据库基本一致 允许用户通过SQL查询数据而不关心数据库底层存储结构 1.1 SQL使用: 可以 ...
- 廖雪峰Java15JDBC编程-1关系数据库基础-1关系数据库简介
1.数据库 1.1 定义 数据库是按照数据结构来组合.存储和管理数据的软件. 1.2 数据库模型 数据库有层次模型.网状模型.关系模型三种模型. 2 关系数据库 关系数据库是建立在关系模型上的数据库, ...
- 廖雪峰Java6IO编程-1IO基础-1IO简介
1.IO简介 IO是指Input/Output,即输入和输出: Input指从外部读取数据到内存,例如从磁盘读取,从网络读取. * 为什么要把数据读到内存才能处理这些数据呢? * 因为代码是在内存中运 ...
- 廖雪峰Java6IO编程-2input和output-1inputStream
1.InputStream 1.1InputStream是所有输入流的超类: int read() * 读取下一个字节,并返回字节(0-255) * 如果已读到末尾,返回-1 * read()方法是阻 ...
随机推荐
- Openstack贡献者须知 2 — 社区工作运作 & 代码贡献流程
目录 目录 前文列表 订阅邮件列表 Mailing Lists 社区工作运作流程 Openstack 代码贡献流程 PEP8 Python编程风格 查阅相关资源 前文列表 Openstack贡献者须知 ...
- BOM DOM 简介
BOM和DOM简介 BOM,Browser Object Model ,浏览器对象模型. BOM主要提供了访问和操作浏览器各组件的方式. 浏览器组件: window(浏览器窗口) locati ...
- PAT_A1004#Counting Leaves
Source: PAT A1004 Counting Leaves (30 分) Description: A family hierarchy is usually presented by a p ...
- SpringBoot-集成PageHelper5.1.2踩坑
背景就不介绍了,项目是SpringBoot+MyBatis搭建的,需要集成git上的PageHelper5.1.2,这个插件大家都比较熟悉了 之前一直用的PageHelper4.0.3,集成是这样的: ...
- Hadoop搭建,上传文件时出现错误,没有到主机的路由
解决方案:(1)从namenode主机ping其它slaves节点的主机名(注意是slaves节点的主机名),如果ping不通,原因可能是namenode节点的/etc/hosts 未配置主机名与IP ...
- 不同JDK版本之间的intern()方法的区别-JDK6 VS JDK6+
String s = new Stirng(“a”); s.intern(); JDK6:当调用intern()方法时,如果字符串常量池先前已创建出该字符串对象,则返回池中的该字符串的引用.否则,将此 ...
- vue 项目 去哪儿
去哪儿项目 使用vue +vue-router+vuex +axios完成,包括3个部分内容 1.首页演示,包括首页header,首页轮播图,周末去哪儿,热销推荐开发 2.城市列表页面开发 ,包括城市 ...
- Let's Encryt免费SSL证书申请[我司方案]
Let's Encrypt颁发的证书是目前生产的大多数浏览器都信任的,您只需下载并运行Let's Encrypt客户端来生成一个证书即可. 在颁发证书之前,需要验证您的域名的所有权.首先,在您的主机上 ...
- 【JZOJ6273】欠钱
description analysis 读懂题就可知\(b\)的收益即为\(a\)到\(b\)这一条链上边权的最小值 那么就是动态维护一个森林,询问链上最小值,同时必须满足儿子走向父亲 明显\(LC ...
- 廖雪峰Java16函数式编程-2Stream-6reduce
1. 聚合方法 Stream.reduce()是一个Stream的聚合方法:把一个Stream的所有元素聚合成一个结果 例如: Stream.of(1, 2, 3, 4, 5).count(); // ...