事 务是指一组相互依赖的操作行为,举一个举得不能再被举的例子就是银行交易,当然还有其他像网上购物,电子货币交易等等,事务的成功取决于这些相互依赖的操 作行为是否都能执行成功,只要有一个操作行为失败,就意味着整个事务失败。例如: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元,哈哈,你是不是开始喜欢第二类丢失更新了呢?

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

分享到:

2007-06-22 13:51

浏览 7322

评论

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

  1. Java综合知识总结_数据库篇

    JavaMySQL 一.JDBC连接数据库步骤(以MYSQL为例) 1.加载JDBC驱动程序: 通过Class类的forName方法实现,并将驱动地址放进去 成功加载后,会将Driver类的实例注册到 ...

  2. MySQL数据库变量_数据库参数_MySQL变量_系统变量_用户变量

    文章目录 MySQL 变量分类 系统变量 查看系统变量 设置系统变量 如何通过配置文件来设置变量值 通过命令行选项来设置变量值 动态设置全局级的系统变量 设置静态的系统变量 设置会话级的系统变量 引用 ...

  3. mysql账目表_数据库1(数据库、表及表数据、SQL语句)

    01数据库概念 A: 什么是数据库 数据库就是存储数据的仓库,其本质是一个文件系统,数据按照特定的格式将数据存储起来, 用户可以对数据库中的数据进行增加,修改,删除及查询操作. B: 什么是数据库管理 ...

  4. 数据库关机_数据库:MySQL常见的三种存储引擎InnoDB、MyISAM、MEMORY的区别?

    InnoDB.MyISAM两种存储引擎的大致区别? 1.InnoDB支持事务,MyISAM不支持, 这一点是非常之重要.事务是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyI ...

  5. mysql数据库 菜鸟_数据库-MySQL入门

    什么是数据库? 一定方式储存在一起.能与多个用户共享.具有尽可能小的冗余度.与应用程序彼此独立的数据集合 数据库管理系统(简称DBMS):是为管理数据库而设计的电脑软件系统,一般具有存储.截取.安全保 ...

  6. 驾校管理系统(SSM,JSP,JAVA,MYSQL)【源码,数据库脚本,论文】

    驾校管理系统(SSM,JSP,JAVA,MYSQL)(毕业论文近13000字以上, 包含程序代码,MySql数据库,数据库脚本) [下载全套源码] [项目包含] [项目运行环境] Eclipse + ...

  7. 管理mysql表知识点_数据库复习提纲(必考知识点整理)

    第1章 绪论 1 数据:数据是数据库中存储的基本对象. 数据库(DB):是长期存储在计算机内.有组织的.可共享的大量数据的集合. 数据库管理系统(DBMS):提供数据定义语言(DDL),用户通过它可以 ...

  8. mysql数据库算法_数据库:MySQL索引背后的数据结构及算法原理【转】

    原文:http://blog.codinglabs.org/articles/theory-of-mysql-index.html 摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话 ...

  9. mysql sqlite转换_数据库转换工具(SqliteToMysql)

    SqliteToMysql是一款用于SQLITE和MYSQL之间的数据库转换工具.它能够将SQLITE数据转换成MYSQL数据库,让用户自主配置转换条件,满足用户的数据库格式需要.. 相关软件软件大小 ...

最新文章

  1. Ubuntu 镜像使用帮助
  2. 'keytool' 不是内部或外部命令,也不是可运行的程序
  3. 写好的python如何在其它电脑上运行_如何在另一个文件中运行一个python文件?
  4. SpringCloud工作笔记066---断路器(Curcuit Breaker)模式
  5. 牛X网整理的JAVA面试题
  6. 如何判断Linux服务器是否被入侵?
  7. 【7.17总结】 匈牙利算法(二分图最大匹配)
  8. mysql数据库最多列_mysql多列索引和最左前缀
  9. 深度学习图解 - 具备高中数学知识就能从入门到精通的神书 Andrew W· Trask
  10. MATLAB 常用绘图命令
  11. 图片轮播,鼠标放上去即停止,鼠标移除即播放下一张图片
  12. Oracle 10g安装图解教程
  13. 【中间件技术】第四部分 Web Service规范(10) Web Service规范
  14. 电信物联网平台,java后台对接电信北向应用,订阅平台消息
  15. HEVC/H.265编码HM码率控制
  16. php 中margin-top,margin-top是什么意思?
  17. python五角星教程_绘制五角星_清华尹成python入门教程_少儿编程视频-51CTO学院
  18. javaweb JAVA JSP 流浪狗管理系统(宠物狗管理系统)jsp小宠物在线管理网站源码
  19. Unknown database ‘ ‘
  20. Codeforces Round #767 (Div. 2)题解

热门文章

  1. 关于芯片,这里有你没看过的硬核科普
  2. 安卓源码避坑指南3——拨打电话的SIM卡无效导致蓝牙断开连接
  3. Linux——安装mysql数据库
  4. word给目录的页码加括号
  5. php获取laydate,详解关于laydate.js加载laydate.css路径错误问题
  6. 翻译小窍门-2009年最伤感的86句经典语录
  7. 认证方式之SSO和Basic
  8. 通常情况下,我们每天花一两个小时学习建站,一个月左右就能掌握一种简单开源程序建站
  9. 大学计算机科技论文格式,大学生论文格式及范文
  10. 零基础进入java行业,我是怎么做到的