精华帖 (4) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2012-05-26
最后修改:2012-06-09
引述要学习Spring框架的技术内幕,必须事先掌握一些基本的Java知识,正所谓“登高必自卑,涉远必自迩”。以下几项Java知识和Spring框架息息相关,不可不学(我将通过一个系列分别介绍这些Java基础知识,希望对大家有所帮助。):
[1] Java反射知识-->Spring IoC :http://www.iteye.com/topic/1123081 [2] Java动态代理-->Spring AOP :http://www.iteye.com/topic/1123293 [3] 属性编辑器,即PropertyEditor-->Spring IoC:http://www.iteye.com/topic/1123628 [4] XML基础知识-->Spring配置:http://www.iteye.com/topic/1123630 [5] 注解-->Spring配置:http://www.iteye.com/topic/1123823 [6] 线程本地变更,即ThreadLocal-->Spring事务管理:http://www.iteye.com/topic/1123824 [7] 事务基础知识-->Spring事务管理:http://www.iteye.com/topic/1124043 [8] 国际化信息-->MVC:http://www.iteye.com/topic/1124044 [9] HTTP报文-->MVC:http://www.iteye.com/topic/1124408 Spring虽然提供了灵活方便的事务管理功能,但这些功能都是基于底层数据库本身的事务处理机制工作的。要深入了解Spring的事务管理和配置,有必要先对数据库事务的基础知识进行学习。 何为数据库事务 “一荣俱荣,一损俱损”这句话很能体现事务的思想,很多复杂的事物要分步进行,但它们组成一个整体,要么整体生效,要么整体失效。这种思想反映到数据库上,就是多个SQL语句,要么所有执行成功,要么所有执行失败。 数据库事务有严格的定义,它必须同时满足 4 个特性:原子性(Atomic)、一致性(Consistency)、隔离性(Isolation)和持久性(Durabiliy),简称为ACID。下面是对每个特性的说明。
在这些事务特性中,数据“一致性”是最终目标,其他的特性都是为达到这个目标的措施、要求或手段。 数据库管理系统一般采用重执行日志保证原子性、一致性和持久性,重执行日志记录了数据库变化的每一个动作,数据库在一个事务中执行一部分操作后发生错误退出,数据库即可以根据重执行日志撤销已经执行的操作。此外,对于已经提交的事务,即使数据库崩溃,在重启数据库时也能够根据日志对尚未持久化的数据进行相应的重执行操作。 和Java程序采用对象锁机制进行线程同步类似,数据库管理系统采用数据库锁机制保证事务的隔离性。当多个事务试图对相同的数据进行操作时,只有持有锁的事务才能操作数据,直到前一个事务完成后,后面的事务才有机会对数据进行操作。Oracle数据库还使用了数据版本的机制,在回滚段为数据的每个变化都保存一个版本,使数据的更改不影响数据的读取。 数据并发的问题 一个数据库可能拥有多个访问客户端,这些客户端都可以并发方式访问数据库。数据库中的相同数据可能同时被多个事务访问,如果没有采取必要的隔离措施,就会导致各种并发问题,破坏数据的完整性。这些问题可以归结为5类,包括3类数据读问题(脏读、不可重复读和幻象读)以及2类数据更新问题(第一类丢失更新和第二类丢失更新)。下面,我们分别通过实例讲解引发问题的场景。 脏读(dirty read) A事务读取B事务尚未提交的更改数据,并在这个数据的基础上操作。如果恰巧B事务回滚,那么A事务读到的数据根本是不被承认的。来看取款事务和转账事务并发时引发的脏读场景: 在这个场景中,B希望取款500元而后又撤销了动作,而A往相同的账户中转账100元,就因为A事务读取了B事务尚未提交的数据,因而造成账户白白丢失了500元。在Oracle数据库中,不会发生脏读的情况。 引用 一个有结巴的人在饮料店柜台前转悠,老板很热情地迎上来说:“喝一瓶?”结巴连忙说:“我…喝…喝…”老板麻利地打开易拉罐递给结巴,结巴终于憋出了他的那句话:“我…喝…喝…喝不起啊!”
不可重复读(unrepeatable read) 不可重复读是指A事务读取了B事务已经提交的更改数据。假设A在取款事务的过程中,B往该账户转账100元,A两次读取账户的余额发生不一致: 在同一事务中,T4时间点和T7时间点读取账户存款余额不一样。 幻象读(phantom read) A事务读取B事务提交的新增数据,这时A事务将出现幻象读的问题。幻象读一般发生在计算统计数据的事务中,举一个例子,假设银行系统在同一个事务中,两次统计存款账户的总金额,在两次统计过程中,刚好新增了一个存款账户,并存入100元,这时,两次统计的总金额将不一致: 如果新增数据刚好满足事务的查询条件,这个新数据就进入了事务的视野,因而产生了两个统计不一致的情况。 幻象读和不可重复读是两个容易混淆的概念,前者是指读到了其他已经提交事务的新增数据,而后者是指读到了已经提交事务的更改数据(更改或删除),为了避免这两种情况,采取的对策是不同的,防止读取到更改数据,只需要对操作的数据添加行级锁,阻止操作中的数据发生变化,而防止读取到新增数据,则往往需要添加表级锁——将整个表锁定,防止新增数据(Oracle使用多版本数据的方式实现)。 第一类丢失更新 A事务撤销时,把已经提交的B事务的更新数据覆盖了。这种错误可能造成很严重的问题,通过下面的账户取款转账就可以看出来: A事务在撤销时,“不小心”将B事务已经转入账户的金额给抹去了。 第二类丢失更新 A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失: 上面的例子里由于支票转账事务覆盖了取款事务对存款余额所做的更新,导致银行最后损失了100元,相反如果转账事务先提交,那么用户账户将损失100元。 数据库锁机制 数据并发会引发很多问题,在一些场合下有些问题是允许的,但在另外一些场合下可能却是致命的。数据库通过锁的机制解决并发访问的问题,虽然不同的数据库在实现细节上存在差别,但原理基本上是一样的。 按锁定的对象的不同,一般可以分为表锁定和行锁定,前者对整个表进行锁定,而后者对表中特定行进行锁定。从并发事务锁定的关系上看,可以分为共享锁定和独占锁定。共享锁定会防止独占锁定,但允许其他的共享锁定。而独占锁定既防止其他的独占锁定,也防止其他的共享锁定。为了更改数据,数据库必须在进行更改的行上施加行独占锁定,INSERT、UPDATE、DELETE和SELECT FOR UPDATE语句都会隐式采用必要的行锁定。下面我们介绍一下Oracle数据库常用的5种锁定。
事务隔离级别 尽管数据库为用户提供了锁的DML操作方式,但直接使用锁管理是非常麻烦的,因此数据库为用户提供了自动锁机制。只要用户指定会话的事务隔离级别,数据库就会分析事务中的SQL语句,然后自动为事务操作的数据资源添加上适合的锁。此外数据库还会维护这些锁,当一个资源上的锁数目太多时,自动进行锁升级以提高系统的运行性能,而这一过程对用户来说完全是透明的。 ANSI/ISO SQL 92标准定义了4个等级的事务隔离级别,在相同数据环境下,使用相同的输入,执行相同的工作,根据不同的隔离级别,可以导致不同的结果。不同事务隔离级别能够解决的数据并发问题的能力是不同的,如表9-1所示。 事务的隔离级别和数据库并发性是对立的,两者此增彼长。一般来说,使用READ UNCOMMITED隔离级别的数据库拥有最高的并发性和吞吐量,而使用SERIALIZABLE隔离级别的数据库并发性最低。 SQL 92定义READ UNCOMMITED主要是为了提供非阻塞读的能力,Oracle虽然也支持READ UNCOMMITED,但它不支持脏读,因为Oracle使用多版本机制彻底解决了在非阻塞读时读到脏数据的问题并保证读的一致性,所以,Oracle的READ COMMITTED隔离级别就已经满足了SQL 92标准的REPEATABLE READ隔离级别。 SQL 92推荐使用REPEATABLE READ以保证数据的读一致性,不过用户可以根据应用的需要选择适合的隔离等级。 JDBC对事务支持 并不是所有的数据库都支持事务,即使支持事务的数据库也并非支持所有的事务隔离级别,用户可以通过Connection#getMetaData()方法获取DatabaseMetaData对象,并通过该对象的supportsTransactions()、supportsTransactionIsolationLevel(int level)方法查看底层数据库的事务支持情况。 Connection默认情况下是自动提交的,也即每条执行的SQL都对应一个事务,为了能够将多条SQL当成一个事务执行,必须先通过Connection#setAutoCommit(false)阻止Connection自动提交,并可通过Connection#setTransactionIsolation()设置事务的隔离级别,Connection中定义了对应SQL 92标准4个事务隔离级别的常量。通过Connection#commit()提交事务,通过Connection#rollback()回滚事务。下面是典型的JDBC事务数据操作的代码: 代码清单9-1 JDBC事务代码 Connection conn ; try{ conn = DriverManager.getConnection();//①获取数据连接 conn.setAutoCommit(false); //②关闭自动提交的机制 conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); //③设置事务隔离级别 Statement stmt = conn.createStatement(); int rows = stmt.executeUpdate( "INSERT INTO t_topic VALUES(1,’tom’) " ); rows = stmt.executeUpdate( "UPDATE t_user set topic_nums = topic_nums +1 "+ "WHERE user_id = 1"); conn.commit();//④提交事务 }catch(Exception e){ … conn.rollback();//⑤回滚事务 }finally{ … } 在JDBC 2.0中,事务最终只能有两个操作:提交和回滚。但是,有些应用可能需要对事务进行更多的控制,而不是简单地提交或回滚。JDBC 3.0(JDK 1.4及以后的版本)引入了一个全新的保存点特性,Savepoint 接口允许用户将事务分割为多个阶段,用户可以指定回滚到事务的特定保存点,而并非像JDBC 2.0一样只回滚到开始事务的点,如图9-1所示。 下面的代码使用了保存点的功能,在发生特定问题时,回滚到指定的保存点,而非回滚整个事务,如代码清单9-2所示: 代码清单9-2 使用保存点的事务代码 … Statement stmt = conn.createStatement(); int rows = stmt.executeUpdate( "INSERT INTO t_topic VALUES(1,’tom’)"); Savepoint svpt = conn.setSavepoint("savePoint1");//①设置一个保存点 rows = stmt.executeUpdate( "UPDATE t_user set topic_nums = topic_nums +1 "+ "WHERE user_id = 1"); … //②回滚到①处的savePoint1,①之前的SQL操作,在整个事务提交后依然提交, //但①到②之间的SQL操作被撤销了 conn.rollback(svpt); … conn.commit();//③提交事务 并非所有数据库都支持保存点功能,用户可以通过DatabaseMetaData#supportsSavepoints()方法查看是否支持。 这些文章摘自于我的《Spring 3.x企业应用开发实战》,我将通过连载的方式,陆续在此发出。欢迎大家讨论。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2012-05-29
支持一下楼主,请问下,这些内容都是从《Spring 3.x企业应用开发实战》中可以得到的吗?
|
|
返回顶楼 | |
发表时间:2012-05-29
demonkind 写道 支持一下楼主,请问下,这些内容都是从《Spring 3.x企业应用开发实战》中可以得到的吗?
是的。 |
|
返回顶楼 | |
发表时间:2012-05-31
数据库锁机制那块能举点共享锁及排他锁实际例子就更好了
目前只是谈了一些专业术语,希望能完善 |
|
返回顶楼 | |
发表时间:2012-06-22
正在读《Spring 3.x企业应用开发实战》,但是在跑第12章的例子的时候,TestMybatisBbtForum的testForumMethod的方法运行的时候发现无法提交到数据库,testTemplateDao是可以查询到数据的,会是什么原因?
|
|
返回顶楼 | |
发表时间:2012-07-04
请问楼主。
除了JDBC之外,还有什么其他事务管理技术? JDBC底层是用tcp连接DB的吗? JDBC的底层事务控制是如何做到的? web应用启动之后,是如何建立JDBC连接池的? |
|
返回顶楼 | |
发表时间:2012-07-31
nanjingjiangbiao 写道 请问楼主。
除了JDBC之外,还有什么其他事务管理技术? JDBC底层是用tcp连接DB的吗? JDBC的底层事务控制是如何做到的? web应用启动之后,是如何建立JDBC连接池的? 一下是自我理解,可能有错的地方. 希望不要误人子弟啊,呵呵. 1. JDBC 只是 提供一个接口,依赖各个数据库的具体实现. jdbc 要求各个数据库实现这些功能而已. 比如你应该听说过 oracle 的 ojdbc14.jar ,就是来实现 jdbc 的. 2. 不确定,不猜测. 3. 各个数据库自己控制的. jdbc 只是一个接口.又各个数据库实现. 4. 两种方式, 1)自定义一个数据库连接池, 现在常用的数据库连接池有 c3p0 ,dbcp,boneCP ,以及 阿里的产品 druid. 等等 2)使用web服务器的数据库连接池,也就是 JNDI 方式. tomcat 7.0 以前的 dbcp , 7.0以后 JDBC Pool,还有WebSphere 自己的 数据库连接池,其他的没用过了. |
|
返回顶楼 | |
发表时间:2012-07-31
LZ 的文章都挺好的,
人脑有限啊,买本参照书籍, 这样也不怕喝酒喝糊涂了,哈哈. |
|
返回顶楼 | |
发表时间:2012-07-31
nanjingjiangbiao 写道 请问楼主。
除了JDBC之外,还有什么其他事务管理技术? JDBC底层是用tcp连接DB的吗? JDBC的底层事务控制是如何做到的? web应用启动之后,是如何建立JDBC连接池的? 1.除了JDBC之外,还有什么其他事务管理技术? 事务管理即ACID: 原子性(Atomicity) 事务是数据库的逻辑工作单位,事务中包括的诸操作要么全做,要么全不做。 一致性(Consistency) 事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。 隔离性(Isolation) 一个事务的执行不能被其他事务干扰。 持续性/永久性(Durability) 一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。 一般指数据库对数据处理的管理特性,但不限于数据库,如消息管理器也有这个要求。 2.JDBC底层是用tcp连接DB的吗? 是的,使用TCP/IP 3.JDBC的底层事务控制是如何做到的? 这个要看具体的数据库自己的实现。一般的原理是通过锁机制和多版本机制; 4.web应用启动之后,是如何建立JDBC连接池的? Web容器一般有自带的JDBC连接池,如JBoss Weblogic,Websphere都有,在管理控制台配置即可。 也可以使用开源的JDBC连接池,著名的有DBCP,C3P0 |
|
返回顶楼 | |
发表时间:2012-08-03
最后修改:2012-08-03
请问楼主:
1.Spring是如何管理这些事务的? 2.在并发访问服务器的时候,又是怎么保证每个客户端得到的都是实时的数据? 3.怎样避免发生更新错误造成数据丢失,hibernate和Spring是否有内部机制保证数据库操作的正确性? 4.具体怎样配置? |
|
返回顶楼 | |