一 事务定义及特性
1、数据库事务的定义:数据库事务(Database Transaction) 是指由一个或多个SQL语句组成的工作单元,这个工作单元中的SQL语句相互依赖,如果有一个SQL语句执行失败,就必须撤销整个工作单元。
以银行转账为例:
2、数据库事务必须具备ACID特征
A: Atomic 原子性:整个事务不可分割,要么都成功,要么都撤销。
C: Consistency 一致性:事务不能破坏关系数据的完整性和业务逻辑的一致性,例如转账,应保证事务结束后两个账户的存款总额不变。
I: Isolation 隔离性:多个事务同时操纵相同数据时,每个事务都有各自的完整数据空间
D: Durability 持久性:只要事务成功结束,对数据库的更新就必须永久保存下来,即使系统发生崩溃,重启数据库后,数据库还能恢复到事务成功结束时的状态。
3、数据库事务的生命周期
4、声明事务的边界
事务的开始边界(BEGIN)
事务的正常结束边界(COMMIT): 提交事务,永久的保存被事务更新后的数据库状态。
事务的异常结束边界(ROLLBACK): 撤销事务,使数据库退回到执行事务前的初始状态。
5、通过JDBC API来声明事务边界
Connection类提供了用于控制事务的方法:
setAutoCommit(boolean autoCommit):设置是否自动提交事务
commit(): 提交事务
rollback(): 撤销事务
try { con = java.sql.DriverManager.getConnection(dbUrl,dbUser,dbPwd); //设置手工提交事务模式 con.setAutoCommit(false); stmt = con.createStatement(); //数据库更新操作1 stmt.executeUpdate("update ACCOUNTS set BALANCE=900 where ID=1 "); //数据库更新操作2 stmt.executeUpdate("update ACCOUNTS set BALANCE=1000 where ID=2 "); con.commit(); //提交事务 }catch(Exception e) { try{ con.rollback(); //操作不成功则撤销事务 }catch(Exception ex){ //处理异常 …… } //处理异常 …… }finally{…}
二 声明事务的开始边界
Transaction tx = session.beginTransaction();
提交事务
tx.commit();
撤销事务
tx.rollback();
三 多个事务并发运行的问题
1、单个事务能保证单项业务的数据完整性,但是当多个事务同步运行时可能带来并发问题,具体体现在:
第一类丢失更新:在撤销一个事务时,把其它事务提交的更新数据覆盖。
脏读:一个事务读到另一事务未提交的更新数据。
虚读:一个事务读到另一事务已提交的新插入的数据。
不可重复读:一个事务读到另一事务已提交的更新数据。
第二类丢失更新:这是不可重复读中的特例,一个事务覆盖另一事务已提交的更新数据。
四 数据库系统锁的基本原理
1、当一个事务访问某种数据库资源时,如果执行select语句,必须先获得共享锁,如果执行insert、update或delete语句,必须获得独占锁。
2、当第二个事务也要访问相同资源时,如果执行select语句,也必须获得共享锁,如果执行insert、update或delete语句,也必须获得独占锁。此时根据已经放置在资源上的锁的类型,来决定第二个事务应该等待第一个事务解除对资源的锁定,还是可以立刻获得锁。
五 锁的分类:
1)从应用程序的角度分为:
(1)悲观锁
(2)乐观锁
2)数据库系统按照封锁程度可分为:
(1)共享锁:用于读数据操作,它非独占的,允许其他事务同时读取其锁定的资源,但不允许其他事务更新它。
(2)独占锁:也叫排他锁,适用于修改数据的场合。他所锁定的资源,其他事物不能读取也不能修改。
(3)更新锁:在更新操作的初始阶段用来锁定可能要修改的资源,这可以避免使用共享锁造成的死锁现象。
六 数据库的事务隔离级别
1、隔离级别的种类
为了解决数据库事务并发运行时的各种问题数据库系统提供四种事务隔离级别:
Read Uncommitted(读未提交数据):
它可以防止第一类丢失更新问题,但没有解决脏读以上的并发问题。它的事务隔离性最低。
Read Committed(读已提交数据):
它可以防止脏读以下的并发问题,但没有解决不可重复读以上的并发问题。
Repeatable Read(可重复读):
它可以防止不可重复读(包括第二类丢失更新)以下的并发问题,但没有解决幻读问题。
Serializable(串行化):
提供最严格的事务隔离性。它把事务隔离成连续的一个接一个地执行,而不是并发执行。在这种隔离级别下,不会出现任何的并发问题。
2、隔离级别所能避免能并发的问题
3、设置隔离级别的原则
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
对于多数据应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读,而且具有较好的并发性能。尽管它会导致不可重复读,虚读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序悲观锁和乐观锁来解决。
4、在hibernate中设置隔离级别
JDBC数据库连接使用数据库系统默认的隔离级别。在Hibernate的配置文件中可以显示地设置隔离级别。每一种隔离级别对应着一个正整数。
1:Read Uncommitte
2:Read Committed
4:Repeatable Read
8:Serializable
七 在应用程序中采用悲观锁和乐观锁
1、应用程序中解决不可重复读问题
采用悲观锁和乐观锁的作用:当数据库系统采用Red Committed隔离级别时,会导致不可重复读和第二类丢失更新的并发问题。在可能出现这种问题的场合,可以在应用程序中采用乐观锁或悲观锁来避免这类问题。
2、悲观锁和乐观锁的概念
悲观锁:在数据有加载的时候就给其进行加锁,直到该锁被释放掉,其他用户才可以进行修改,优点:数据的一致性保持得很好,缺点:不适合多个用户并发访问。当一个锁住的资源不被释放掉的时候,这个资源永远不会被其他用户进行修改,容易造成无限期的等待。
乐观锁:就是在对数据进行修改的时候,对数据才去版本或者时间戳等方式来比较,数据是否一致性来实现加锁。
3、利用悲观锁协调并发运行的事务
4、使用乐观锁
乐观锁是由应用程序提供的一种机制,这种机制既能保证多个事务并发访问数据,又能防止第二类丢失更新问题。
在应用程序中,可以利用Hibernate提供的版本控制功能来实现乐观锁。对象-关系映射文件中的<version>元素和<timestamp>元素都具有版本控制功能:
<version>元素利用一个递增的整数来跟踪数据库表中记录的版本
<timestamp>元素用时间戳来跟踪数据库表中记录的版本
<version>进行版本控制: 当Hibernate更新一个Account对象时,会根据它的id与version属性到ACCOUNTS表中去定位匹配的记录,假定Account对象的version属性为0,那么在取款事务中Hibernate执行的update语句为: update ACCOUNTS set NAME=’Tom’,BALANCE=900,VERSION=1 where ID=1 and VERSION=0; 如果存在匹配的记录,就更新这条记录,并且把VERSION字段的值加1。当支票转账事务接着执行以下update语句时: update ACCOUNTS set NAME=’Tom’,BALANCE=1100,VERSION=1 where ID=1 and VERSION=0; 由于ID为1的ACCOUNTS记录的版本已经被取款事务修改,因此找不到匹配的记录,此时Hibernate会抛出StaleObjectStateException
乐观锁并发取款事务和支票转账:
八 缓存
缓存是计算机领域的概念,它介于应用程序和永久性数据存储源之间
Hibernate的缓存一般分为3类:
一级缓存
二级缓存
查询缓存
1、Session内的缓存即一级缓存;
2、二级缓存是进程或集群范围内的缓存,可以被所有的Session共享;
二级缓存是可配置的插件;
选择合适的缓存插件,配置其自带的配置文件。
选择需要使用二级缓存的持久化类,设置它的二级缓存的并发访问策略。
3、查询是数据库技术中最常用的操作,Hibernate为查询提供了缓存,用来提高查询速度,优化查询性能
查询缓存依赖于二级缓存