java mysql数据库回退_数据库事务及Java中的处理

事 务是指一组相互依赖的操作行为,举一个举得不能再被举的例子就是银行交易,当然还有其他像网上购物,电子货币交易等等,事务的成功取决于这些相互依赖的操 作行为是否都能执行成功,只要有一个操作行为失败,就意味着整个事务失败。例如:bill和tom的QQ账户的QQ币点数都是500点,现在bill把 100点QQ币转到tom的QQ币账号上,这个事务就包含以下操作行为:

---- bill的QQ币账号减少100点

---- tom的QQ币账号增加100点

这两个操作作为一个不可分割的工作单元,假如仅仅第一步操作执行成功,但是第二步执行失败,那么整个事务失败,回滚到事务开始前的状态,bill和tom 的QQ账户的QQ币点数还依然是500点。显然如果没有事务的概念,那么就会造成bill的100点QQ币神秘的消失了...

数据库的事务就是上面提到的事务在RDB中的实现,它由一组在业务逻辑上相互依赖的SQL语句组成,假设以上QQ账户在DB中的qq_account表结构如下:

------------------------------------

id       name      balance

------------------------------------

1         bill           500

2         tom          500

------------------------------------

以上事务的SQL可以表示为:

UPDATE qq_account SET balance=400 WHERE id=1;

UPDATE qq_account SET balance=600 WHERE id=2;

只要两条SQL语句有一个执行失败,整个事务就失败,qq_account表中的数据就必须回退到最初的状态,而不会被更新。

数据库事务必须具备ACID特性,具体的含义如下:

----- Atomic(原子性):只整个数据库事务是不可分割的工作单元。只有事务中所有的操作执行成功,才算整个事务成功;事务中任何一个SQL语句执行失败,那么已经执行成功的SQL语句也必须撤销,数据库状态应该退回到执行事务前的状态。通过上面的例子我们已经看得很清楚了。

----- Consistency(一致性):指数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。例如,不管上面的例子里面QQ币转账的事务成功还是失败,都应该保证事务结束后qq_account表中bill和tom的QQ币总额为1000点。

----- Isolation(隔离性):指的是在并发的环境之中,当不同的事务同时操作相同的数据时,每个事务都有各自的完整数据空间,这里涉及的事情就多了,我想在后面单独总结成一篇文章。

----- Durability(持久性):指的是只有事务成功结束,它对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库后,数据库还能恢复到事务成功结束时的状态。

事务的ACID只是一个抽象的概念,具体是由RDBMS来实现的。数据库管理系统用日志来保证事务的原子性、一致性和持久性。日志记录了事务 对数据库所做的更新,如果某个事务在执行过程中发生了错误,就可以根据日志,撤销事务对数据库已经做的更新,使数据库回退到执行事务前的初始状态。所以不 论讲到什么数据库,都会有专门的一章来讲日志,呵呵,终于从本质上明白了日志的作用。

至于事务的隔离性,RDBMS则是采用锁机制来实现的。当多个事务同时更新数据库中的临界数据时,只允许持有锁的事务才能更新该数据,其他事务必须等待, 直到前一个数据释放了锁,其他事务才可能有机会来进行更新,这和我们在OS中学的进程的并发时所谈到的锁机制原理差不多。

上面提到了数据库事务的ACID特性,那么谁来保证数据库事务具有ACID呢?其实,只要向数据库系统声明一个事务,数据库系统就会自动保证事务的ACID特性的。那么下面从抽象的概念上来看看怎么声明事务:

-- BEGIN 事务的开始边界

-- COMMIT 事务的正常结束边界,提交事务,永久保存被事务更新后的数据库状态。

-- ROLLBACK 事务的异常结束边界,撤销事务,使数据退回到执行事务前的初始状态。

数据库系统支持以下两种事务模式:

-- 自动提交模式:每个SQL语句都是一个独立的事务,当数据库系统执行完一个SQL语句后,会自动提交事务。

-- 手工提交事务:必须由数据库的客户端显式指定事务开始边界和结束边界。

以MySQL为例,如果我们启动一个MySQL客户端,就会得到一个独立的数据库连接。每个数据库连接都有一个全局变量@@autocommit,表示当前的事务模式,它有两个可选值:

-- 0:表示手工提交模式。

-- 1:默认值,表示自动提交模式。

可以通过SQL命令 SELECT @@autocommit 来查看当前的事务模式。如果想要把当前的事务模式改为手工提交模式,可以使用以下SQL命令 SET autocommit=0 来实现。

还要多说几句,在MySQL中,数据库表分为三种类型:INNODB、BDB、MyISAM。其中前两种类型的表支持数据库事务,而MyISAM类型的表 不支持事务。在使用CREATE TABLE命令创建表时默认的类型为MyISAM,如果希望更改的话可以通过如下DLL来进行:

ALTER TABLE table_name TYPE=INNODB;

如果希望在创建时就指定表的类型,可以通过以下DLL来进行:

CREATE TABLE table_name(

.....

.....

) TYPE=INNODB;

好了,下面我们来看几个在实践应用程序中声明事务的模板。

1.在MySQL.exe客户端程序中声明事务

我们以一条插入语句为例,当然如果是自动提交模式的话,每一个SQL语句都是一个单独的事务,我们直接运行以下语句就可以了:

mysql>INSERT INTO qq_account VALUES('leon', 1000);

下面我们看看在手动模式下的命令:

mysql>SET autocommit=0;

mysql>BEGIN;

mysql>INSERT INTO qq_account VALUES('leon', 1000);

mysql>COMMIT;

这样一个事务就完成了,下面我们看一看如何撤销一个事务,很简单也许你已经猜到了吧

mysql>BEGIN;

mysql>INSERT INTO qq_account VALUES('leon', 1000);

mysql>ROLLBACK;

2.在JDBC API中声明事务

没有什么难度,我们直接来看看代码:

Connection con = null;

Statement stmt = null;

try {

con = DriverManager.getConnection(dbUrl,dbUser,dbPwd);

//设置手工提交事务模式

con.setAutoCommit(false);

stmt = con.createStatement();

stmt.executeUpdate("......");

//提交事务

con.commit();

} catch(SQLException e) {

if(con != null) {

try {

//操作不成功则撤销事务

con.rollback();

} catch(SQLException e) {

......

}

}

......

} finally {

if(con != null) {

try{

if(stmt != null) {

try {

stmt.close();

} catch(SQLException e) {

......

}

}

con.close();

} catch(SQLException e) {

......

}

}

}

没有什么可再说的了,代码详细得已经像是一篇技术文章了,需要注意的是对于新建的Connection实例来说,在默认的情况下采用自动提交的事务模式, 通过方法 con.setAutoCommit(false)来设置手动提交模式。

3.在Hibernate API中声明事务

Hibernate封装了JDBC API和JTA API,尽管应用程序可以绕过Hibernate API,直接通过JDBC API和JTA API来声明事务,但是这不利于跨平台的开发,下面我们只考虑Hibernate API的实现:

Configuration config = new Configuration().configure();

SessionFactory factory = config.buildSessionFactory();

Session session = factory.openSession();

Transation tx = null;

try {

//开始一个事务

tx = session.beginTransaction();

Account account = new Account("leon",1000);

session.save(account);

//提交事务

tx.commit();

} catch(HibernateException e) {

if(tx != null) {

try {

//操作不成功则撤销事务

tx.rollback();

} catch(HibernateException e) {

......

}

}

......

} finally {

if(session != null) {

try {

session.close();

} catch(HibernateException e) {

......

}

}

}

上面的代码和JDBC API很相像,值得注意的是,在任何时候,一个Session只允许有一个未提交的事务。以下代码对于一个Session同时声明了两个未提交的事务,这是不允许的:

tx1 = session.beginTransaction();

tx2 = session.beginTransaction();

......

tx1.commit();

tx2.commit();

关于事务并发问题,当初在学习DB2的时候,就一直想弄明白,可是当时是为了应试,老师也没有讲得太明白,所以一直决心要总结一下,后来呢,就一直拖到了今天...闲话少讲了,开始正题吧!

在并发的环境之中,一个数据库系统会同时为各种各样的客户端提供服务,对于同时运行的多个事务,当这些事务访问数据库中相同的数据时如果没有采取必要的隔离机制,就会出现各种并发问题,可以把这些问题归纳为一下五种:

---- 第一类丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖。

---- 脏读:一个事务读到了另一个事务未提交的更新数据。

---- 虚读:一个事务读到了另一个事务已经提交的新插入的数据。

---- 不可重复读:一个事务读到另一个事务已提交的更新数据。

---- 第二类丢失更新:一个事务覆盖另一个事务已提交的更新数据,这是不可重复读的一个特例。

这样的定义实在太过死板,而且让人也很难理解,还是通过一个例子来仔细的分析一下每一种并发问题,以下引用孙卫琴老师的《精通Hibernate:JAVA对象持久化技术详解》里面的例子,个人认为这个例子很不错。

假设现在一所银行有一个取款事务和一个支票转账事务操作同一个银行账户的情形,首先我们假设两个事务顺序执行,而不是并发执行,那麽整个的步骤如下:

---- 银行客户在银行前台请求取款100元,出纳员先查询账户信息,得知存款余额为1000元。

---- 出纳员判断存款余额大于取款额,支付给客户100元,并将账户上的存款余额改为900元。

---- 出纳员处理一张转账支票,该支票向此账户汇入100元,出纳员先查询账户信息,得知存款余额为900元。

---- 出纳员将存款余额改为1000元。

可以很清楚的看到,如果顺序的执行这两个事务,不会出现任何问题,可是现实之中并非如此简单,如果两个事务分别由两个出纳员同时执行那麽可能就会出现并发的问题,下面我们分别来介绍:

1.第一类丢失更新

时 间   取款事务                                  转账事务

--------------------------------------------------------------------------------------------

T1     开始事务

T2                                                      开始事务

T3     查询账户余额为1000元

T4                                                      查询账户余额为1000元

T5                                                      汇入100元把余额改为1100元

T6                                                      提交事务

T7     取出100元把余额改为900元

T8     撤销事务余额恢复为1000元

----------------------------------------------------------------------------------------------

如果按照以上的时间次序并发执行的话,由于支票转账事务对存款余额所做的更新被取款事务的撤销所覆盖,所以客户会损失100元。

2.脏 读

时 间    取款事务                                  转账事务

----------------------------------------------------------------------------------------------

T1      开始事务

T2                                                      开始事务

T3     查询账户余额为1000元

T4

T5     取出100元把余额改为900元

T6                                                      查询账户余额为900元(脏读)

T7     撤销事务余额恢复为1000元

T8                                                      汇入100元把余额改为1000元

T9                                                      提交事务

----------------------------------------------------------------------------------------------

由于支票转账事务查询了取款事务未提交的更新数据,并且在这个查询结果的基础上进行了更新操作,如果取款事务最后被撤销 ,会导致银行客户损失100元。

3.虚 读

下面我们另举一个例子来看看什么是虚读。

时 间    注册事务                       统计事务

----------------------------------------------------------------------------------------------

T1     开始事务

T2                                          开始事务

T3                                          统计注册客户总数为10000人

T4     注册一个新用户

T5     提交事务

T6                                          统计注册客户总数为10001人

T7                                          到底哪一个统计数据有效?

-----------------------------------------------------------------------------------------------

统计事务无法相信查询的结果,因为查询结果是不确定的,随时可能被其他事务改变。

对于实际应用,在一个事务中不会对相同的数据查询两次,假定统计事务在T3时刻统计注册客户的总数,执行SELECT语句,在T6时刻不再查询数据库,而 是直接打印统计结果为10000,这个统计结果与数据库当中数据是有所出入的,确切的说,它反映的是T3时刻的数据状态,而不是当前的数据状态。应该根据 实际需要来决定是否允许虚读。以上面的统计事务为例,如果仅仅想大致了解一下注册客户总数,那麽可以允许虚读;如果在一个事务中,会依据查询结果来做出精 确的决策,那麽就必须采取必要的事务隔离措施,避免虚读。

4.不可重复读

时 间    取款事务                                   转账事务

----------------------------------------------------------------------------------------------

T1      开始事务

T2                                                      开始事务

T3     查询账户余额为1000元

T4                                                      查询账户余额为1000元

T5     取出100元把余额改为900元

T6     提交事务

T7                                                     查询账户余额为900元

T8                                                     余额到底是1000元还是900元?

-----------------------------------------------------------------------------------------------

如上面所示,如果支票转账事务两次查询账户的存款余额,但得到了不同的查询结果,这使得银行出纳员无法相信查询结果,因为查询结果是不确定的,随时可能被其他事务改变。

5.第二类丢失更新

时 间    取款事务                                  转账事务

----------------------------------------------------------------------------------------------

T1      开始事务

T2                                                      开始事务

T3     查询账户余额为1000元

T4                                                      查询账户余额为1000元

T5     取出100元把余额改为900元

T6     提交事务

T7

T8                                                      汇入100元把余额改为1100元

T9                                                      提交事务

----------------------------------------------------------------------------------------------

每个事务都不知道其他事务的存在,最后一个事务对记录所做的更新将覆盖由其他事务对记录所做的已提交的更新。上面的例子里由于支票转账事务覆盖了取款事务 对存款余额所做的更新,导致银行最后损失了100元,哈哈,你是不是开始喜欢第二类丢失更新了呢?

怎样才能消除这些并发问题呢?请听下回.......

分享到:

18e900b8666ce6f233d25ec02f95ee59.png

72dd548719f0ace4d5f9bca64e1d7715.png

2007-06-22 13:51

浏览 7322

评论

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

JPA 2.0 中的动态类型安全查询(如何通过 Criteria API 构建动态查询并减少运行时失败)...

简介: 如果编译器能够对查询执行语法正确性检查,那么对于 Java 对象而言该查询就是类型安全的。Java™Persistence API (JPA) 的 2.0 版本引入了 Criteria API,这个 API 首次将类型安全查询引入到 Java 应用程序中,并为在运行时动…

QB无缘无故丢失!

24号用财付通支付冲的13个QB,冲好后查帐户有QB(都没出现错误),过一天查帐户QB不翼而飞,!期间并没有买过什么东西,也不知道怎么查QB的交易详细记录, 感觉没有冲上,由于支…

详解JPA 2.0动态查询机制:Criteria API(2)

详解JPA 2.0动态查询机制:Criteria API(2) 2009-11-13 09:24 Pinaki Poddar IBMDW 字号: T | T JPA 2.0引入了 Criteria API,这个 API 首次将类型安全查询引入到 Java 应用程序中,并为在运行时动态地构造查询提供一种机制。本文介绍如何使用…

PA 2.0 中的动态类型安全查询

JPA 2.0 中的动态类型安全查询 如何通过 Criteria API 构建动态查询并减少运行时失败 如果编译器能够对查询执行语法正确性检查,那么对于 Java 对象而言该查询就是类型安全的。Java™Persistence API (JPA) 的 2.0 版本引入了 Criteria API,这个 API 首次…

JPA 2.0 中的动态类型安全查询

如何通过 Criteria API 构建动态查询并减少运行时失败 如果编译器能够对查询执行语法正确性检查,那么对于 Java 对象而言该查询就是类型安全的。Java™Persistence API (JPA) 的 2.0 版本引入了 Criteria API,这个 API 首次将类型安全查询引入到 Java 应…

详解JPA 2.0动态查询机制:Criteria API

转载自详解JPA 2.0动态查询机制:Criteria API-51CTO。若有侵权,请留言删除。 自从 JPA 于 2006 年首次被引入之后,它就得到了 Java 开发社区的广泛支持。该规范的下一个主要更新 —— 2.0 版本 (JSR 317) —— 将在 2009 年年底完成。JPA 2.0 引入的关键…

基于C语言的平衡二叉树操作(包含完整代码)

平衡二叉树的定义: 为避免树的高度增长过快,降低二叉排序树的性能,规定在插入和删除二叉树结点时,要保证任意结点的左、右子树高度差的绝对值不超过1,将这样的二义树称为平衡二叉树AVL (Balanced Binary Tree),简称平衡树。 平衡…

chatgpt赋能python:如何在Python中撤回输错的指令?

如何在Python中撤回输错的指令? 作为一名有10年Python编程经验的工程师,我们时常会遇到输错指令的情况。在Python中输错指令常常是不可避免的,特别是当你快速编写代码时。然而,如果你不知道如何撤回这些错误的指令,这…

手机备份到底备份什么

手机备份到底备份什么 文章目录 手机备份到底备份什么起因准备如何快速备份开始备份文件备份聊天记录备份图片备份软件备份 往期回顾 最后,点个关注不迷路 手机太卡了,换不起手机,所以就备份一下,刷一下 起因 手机开始卡了&#x…

华为云服务的使用方法详解--以照片备份与恢复为例

既然iPhone有iCloud,小米有小米云服务,华为当然也有自己的华为云服务。但是有些花粉可能还不知道华为云服务到底要怎么使用,今天小编就以照片的备份与恢复为例,跟大家说说华为云备份的使用方式。 一、什么是华为云备份 简单来说…

华为手机[助手]备份的db通讯录文件如何恢复到其他手机

华为手机[助手]备份的db通讯录文件如何恢复到其他手机 如果你是属于下面的情况分析需要的文件第一步最后一步结束 如果你是属于下面的情况 1.如果你使用华为手机并且用华为手机助手备份了,但是你想把文件恢复到其他手机上(其他品牌的安卓手机或者苹果手…

chatgpt赋能python:Python中的按位取反

Python中的按位取反 Python中的按位取反是一种常见的操作,它可以让我们快速地对二进制的数字进行取反操作。在本文中,我们将介绍Python中的按位取反操作,并探讨它的用途和示例。 什么是按位取反 按位取反是一种将二进制数中的每一位进行反…

【Android取证篇】华为手机OTG备份密码重置教程

【Android取证篇】华为手机OTG备份密码重置教程 ​ 提取华为设备遇到OTG备份出来的数据信息软件无法正常解析时,排除数据提取不完整、软件设备等问题,可考虑重置华为的备份密码,重新备份数据再分析—【suy】 文章目录 【Android取证篇】华为…

华为手机MATE10所有分区备份与数据恢复方法

华为手机MATE10所有分区备份与数据恢复方法 作者:爱吃干锅牛肉的喵 时间2020-3-23 前言: 前段时间笔者手机的root权限出问题,误操作重破解ROOT权限导致数据全部wipe并且系统也坏了。没办法重刷机。因为重新刷机,分区表结构都变了…

华为手机备忘录资料备份

为什么80%的码农都做不了架构师?>>> [备份] ->选择备份 ->选择备份到SD卡还是内存卡 -> 选择需要备份的项目 ->确定 备忘录 在memo.db中,通过sqlite浏览器可以打开. 转载于:https://my.oschina.net/GMT/blog/855829

华为手机备份的通讯录是什么文件_手机怎么备份通讯录?华为手机备份方法大全...

原标题:手机怎么备份通讯录?华为手机备份方法大全 现在有很多人选择国产手机,而华为手机作为国产手机中的佼佼者,使用的人也有很多,今天我们就来聊聊会为手机里的备份。 1、本地备份 华为手机中自带有备份的功能&#…

C++实现sqlite单表增删改查的详细步骤

1.环境准备 coding之前需要先安装好C的集成开发环境, 我这里选择的是Visual Studio 2022,本来想使用CLion的, 但是破解太麻烦,懒得整了。 Visual Studio 2022 2.项目创建及编码 启动visual studio, 点击创建项目,选…

环境变量设置export 命令详解

环境变量通俗来讲,就是指定一个路径,编译工具或运行软件时,任务进程会按照设置的路径来搜索文件或使用工具。如果不设置环境变量,又想使用该条命令,则需要加上绝对路径,否则我们需要把文件复制到系统标准命…

centos7环境变量设置

目录 一、 环境变量概念 1、环境变量的含义 2、环境变量的分类 3、Linux环境变量 二、常用的环境变量 1、查看环境变量 1)env命令:查看当前用户全部的环境变量。 2)echo命令:查看当前用户全部的环境变量,符号$不…

chatgpt赋能python:Python中调换数据位置的方法

Python中调换数据位置的方法 在Python编程中,我们经常需要操作数据的位置,例如调换数组中的元素顺序、交换多个变量的值等。在本篇文章中,我们将介绍Python中调换数据位置的常用方法,并给出相应的代码示例。 1.使用临时变量交换…