首先关于Hibernate事务控制,下面是非常权威的资料,
https://docs.jboss.org/hibernate/orm/4.0/devguide/en-US/html/ch02.html#d0e1055 官方文档
http://www.360doc.com/content/07/0307/21/11192_388255.shtml 这篇应该是翻译自官方文档
数据库事务
数据库中的多个原子操作应该绑定成一个整体,要么同时执行成功,要么失败而放弃所有操作,这就是事务。
事务具备四个特性, 原子性,一致性,隔离性,持续性。
一致性:即在事务控制下的系统状态要与其业务逻辑保持一致,要么所有数据库操作都成功了,提交了事务,符合“业务成功”的业务逻辑;要么有其中一部分或者全部操作失败了,那么就回滚数据库,符合“业务失败”的业务逻辑。
隔离性:各个事务互不干扰。
单个数据库事务即使有一连串的操作,通常还是比较好控制,但是多个数据库事务(并发)就会比较复杂。并发操作容易引发一些事务一致性的问题,主要要是数据丢失,脏读等等。为了解决并发事务带来的这些问题,一般的数据库都会提供几种事务隔离级别,即通过不同级别的读取权限(例如要求必须commit之后的数据才能读取)或者不同的数据库锁来避免并发导致的问题。
JAVA编程中的事务控制一般分三类,一类是本地事务控制,另一类是分布式事务控制(JTA),还有就是容器事务(CMT)。所谓本地事务控制,指的是单一数据源的事务控制,Java中通常使用JDBC提供的API就已经足够了。而分布式事务控制通常指的是那些涉及多个数据源分布式事务控制,JTA规范就是专门用来解决分布式事务控制的。所谓容器事务,主要指的是J2EE应用服务器提供的事务管理,局限于EJB应用使用。
数据库锁
为了得到最大的性能,一般数据库都有并发机制,不过带来的问题就是数据访问的冲突。为了解决这个问题,大多数数据库用的方法就是数据的锁定。
加锁是实现数据库并发控制的一个非常重要的技术。当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更新操作。
数据库层面的锁
在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。当数据对象被加上排它锁时,其他的事务不能对它读取和修改。加了共享锁的数据对象可以被其他事务读取,但不能修改。数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。
根据保护的对象不同,Oracle数据库锁可以分为以下几大类:DML锁(最常见),DDL锁等。
DML锁的目的在于保证并发情况下的数据完整性,主要包括TM锁(表级锁)和TX锁(事务锁或行级锁)。
当Oracle执行DML语句时,系统自动在所要操作的表上申请TM类型的锁。当TM锁获得后,系统再自动申请TX类型的锁。
在数据行上只有X锁(排他锁)。在 Oracle数据库中,当一个事务首次发起一个DML语句时就获得一个TX锁,该锁保持到事务被提交或回滚。当两个或多个会话在表的同一条记录上执行 DML语句时,第一个会话在该条记录上加锁,其他的会话处于等待状态。当第一个会话提交后,TX锁被释放,其他会话才可以加锁。
软件设计层面的锁
数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁。
- 悲观锁
就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住。
- 乐观锁
而乐观锁就是认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让用户返回错误的信息,让用户决定如何去做。 乐观锁不能解决脏读的问题(需要交给应用程序)。
在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;但如果系统的并发非常大并且是读多写少的话,,悲观锁定会带来非常大的性能问题,乐观锁可以接受少量的数据冲突,所以我们就要选择乐观锁定的方法.
关于数据库锁可以参考这里 https://zhidao.baidu.com/question/158112371.html
关于乐观锁和悲观锁可以参考 http://www.javaweb1024.com/java/JavaWebzhongji/2015/09/06/847.html
并发事务问题
在并发的事务中,可能会发生下面的问题,
1.第一类丢失更新:撤消一个事务时,把其它事务已提交的更新的数据覆盖了。
2.脏读:一个事务读到另一个事务未提交的更新数据。
3.幻读:一个事务执行两次查询,但第二次查询比第一次查询多出了一些数据行。(因为中途有事务插入了数据)
4.不可重复读:一个事务两次读同一行数据,可是这两次读到的数据不一样。(因为中途有事务更新了数据)
5.第二类丢失更新:这是不可重复读中的特例,一个事务覆盖另一个事务已提交的更新数据。
具体过程如下(摘抄自 http://blog.chinaunix.net/uid-20577907-id-3355493.html )
1. 第一类丢失更新(lost update): 在完全未隔离事务的情况下,两个事物更新同一条数据资源,某一事物异常终止,回滚造成第一个完成的更新也同时丢失
如上图,事务2的更新将会丢失。
要避免第一类丢失更新,可以设置隔离级别为 TRANSACTION_UNCOMMITTED ,
不允许两个事务同时修改数据。 即当事务1写的时候,不允许事务2同时写(但可读),这样就不会造成更新丢失。
2. 脏读(dirty read):如果第二个事务查询到第一个事务还未提交的更新数据,形成脏读。
例如上图,如果事务1在T7时刻rollback,那么事务2读到的就是错误的数据,即脏数据。
要避免脏读,设置隔离级别 TRANSACTION_COMMITTED 。即事务2只能读取到事务1提交后的数据。
读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。也就是事务读取的数据必须是其他事务已确认的数据。可通过“瞬间共享读锁”和“排他写锁”实现。
3. 虚读(phantom read):一个事务执行两次查询,第二次结果集包含第一次中没有或者某些行已被删除,造成两次结果不一致,只是另一个事务在这两次查询中间插入或者删除了数据造成的。
例如上图,事务1第一次查询只有一条记录,第二次查询则有两条记录,因为事务2插入了一条。
要避免虚读(幻读),需要设置最高隔离级别 TRANSACTION_SERIALIZABLE,插入数据不能通过简单行级锁实现,只能在表级别加锁,因此事务1和事务2只能按顺序执行。
即所有事务按顺序依次执行,保证事务在读的过程中,不会有别的事务插入新数据,这无法简单通过行级锁实现,因此需要串行化,让事务按开始的顺序依次执行。
4. 不可重复读(unrepeated read):一个事务两次读取同一行数据,结果得到不同状态结果,如中间正好另一个事务更新了该数据,两次结果相异,不可信任。
例如上图,事务1第一次查询和第二次查询的结果不一致,原因是事务2中途更新了数据。
要避免不可重读,需设置隔离级别为TRANSACTION_REPEATABLE_READ,事务1读事务时不允许事务2改数据,反过来当事务1在改数据时候,不允许事务1读数据,即不允许一个事务读取正在改的数据,也不允许一个事务在改的同时别的事务也在读。
读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。也就是同一事务内两次读取的数据必须相同
5. 第二类丢失更新(second lost updates):是不可重复读的特殊情况,如果两个事务都读取同一行,然后两个都进行写操作,并提交,第一个事务所做的改变就会丢失。
例如上图,在事务1更新了数据,还没有提交的时候,事务2也更新了数据,然后事务1提交事务,接着事务2才提交数据,那么事务1更新的数据将被覆盖。
http://blog.csdn.net/qjyong/article/details/1874599
事务隔离级别
一般来说,实际开发中,直接操作数据库中各种锁的几率相对比较少,更多的是利用数据库提供的四个隔离级别,未提交读、已提交读、可重复读、可序列化,那隔离级别和锁是什么关系?通俗来说,隔离级别是锁的一个整体打包解决方案,我的理解是隔离封装了锁。
一般的数据库会通过事务隔离,通过事务隔离可以解决上面并发事务遇到的问题,在JDBC中也对数据库的隔离级别设置了以下配置项
TRANSACTION_NONE JDBC驱动不支持事务
TRANSACTION_READ_UNCOMMITTED 允许脏读、不可重复读和幻读
TRANSACTION_READ_COMMITTED 禁止脏读,但允许不可重复读和幻读
TRANSACTION_REPEATABLE_READ 禁止脏读和不可重复读,单运行幻读
TRANSACTION_SERIALIZABLE 禁止脏读、不可重复读和幻读
具体分析如下,
JDBC对并发事务问题的解决办法
- 避免第一类更新遗失
如果要避免第一类更新遗失问题,JDBC 可通过Connection 的setTransactionIsolation()设置为TRANSACTION_UNCOMMITTED,如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。通常这也是具备事务功能的数据库引擎会采取的最低隔离层级。不过这个隔离层级读取错误数据的机率太高,一般默认不会采用这种隔离级别。
- 避免脏读
设置Read Committed(读取提交内容),这是多数数据库默认数据库的默认设置。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。也就是事务读取的数据必须是其他事务已确认的数据。JDBC 可通过Connection 的setTransactionIsolation()设置为TRANSACTION_COMMITTED 来提示数据库指定此隔离行为
- 避免重复读取不一致
如果要避免无法重复的读取问题, 可以设置隔离层级为“可重复读取”(Repeatable read),这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。也就是同一事务内两次读取的数据必须相同。JDBC 可通过Connection 的setTransactionIsolation()设置为TRANSACTION_REPEATABLE_READ 来提示数据库指定此隔离行为。
- 避免幻读
如果隔离行为设置为可重复读取,但发生幻读现象,可以设置隔离层级为“可循序”(Serializable),它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。也就是在有事务时若有数据不一致的疑虑,事务必须可以按照顺序逐一进行。JDBC 可通过Connection 的setTransactionIsolation()设置为TRANSACTION_SERIALIZABLE 来提示数据库指定此隔离行为。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、虚读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
http://blog.csdn.net/mr__fang/article/details/10965359
上面的概念比较头晕, 总结了一下几种数据冲突和解决方案如下表,发现所谓的隔离级别本质上就是调整读写权限和顺序。
Hibernate事务控制
Hibernate本身不提供事务控制行为(没有添加任何附加锁定的行为),而是直接在Hibernate底层使用JDBC事务,或者JTA事务,或者CMT事务控制。
-JDBC事务
JDBC通常是对于单一数据源的事务控制。
-分布式事务(JTA)
JTA允许应用程序执行分布式事务处理——在两个或多个网络计算机资源上访问并且更新数据,这些数据可以分布在多个数据库上
-容器事务(CMT)
容器事务主要是J2EE应用服务器提供的,容器事务大多是基于JTA完成,这是一个基于JNDI的,相当复杂的API实现。
Hibernate中,通过SessionFactory可以打开一个session,通过session的beginTraansaction可以开启一个事务。
SessionFactory对象是线程安全的,可被所有线程共享,但创建代价很高,通常只会在程序启动的时候创建,在程序退出时候关闭。
session对象是轻量级的,也是线程不安全的(但从Hibernate3开始,可以使用SessionFactory.getCurrentSesion()获取一个上下文相关的线程,解决了这个问题)。对于单个业务进程,单个的 工作单元而言,它只被使用一次,然后就丢弃。只有在需要的时候,Session 才会获取一个JDBC的Connection(或一个Datasource) 对象。所以你可以放心的打开和关闭Session,甚至当你并不确定一个特定的请 求是否需要数据访问时,你也可以这样做。(一旦你实现下面提到的使用了请求拦截的模式,这就 变得很重要了。
Session 缓存了处于持久化状态的每个对象(Hibernate会监视和检查脏数据)。 这意味着,如果你让Session打开很长一段时间,或是仅仅载入了过多的数据, Session占用的内存会一直增长,直到抛出OutOfMemoryException异常。这个 问题的一个解决方法是调用clear() 和evict()来管理 Session的缓存,但是如果你需要大批量数据操作的话,最好考虑 使用存储过程。在第14章 批量处理(Batch processing)中有一些解决方案。在用户会话期间一直保持 Session打开也意味着出现脏数据的可能性很高。
TransactionFactory是Hibernate底层用来创建事务的工厂,可以根据配置文件生产不同类型的事务(JDBC/JTA/CMT),程序员通常不会直接控制它,而是由session间接控制,即session封装了TransactionFactory.
Hibernate的所有持久化访问都需要在session下进行,但打开一个session并不一定连接数据库,因此一个长session(什么是长session,后面介绍)是可以接受的。但是对于数据库事务,则是需要尽量短,降低数据库锁定造成的资源争用。
一个操作单元(Unit of work)的范围是多大?单个的Hibernate Session能跨越多个 数据库事务吗?还是一个Session的作用范围对应一个数据库事务的范围?应该何时打开 Session,何时关闭Session?,你又如何划分数据库事务的边界呢?
以上问题对于不同的应用场景,就会有不同的策略了,这涉及到事务范围,通常我们会考虑三种场景:
一、操作单元(Unit of work)
首先是在单个线程中, 不要因为一次简单的数据库调用,就打开和关闭一次Session,即不要使用session-per-operation的模式,而是应该将多个操作作为一组,当作一个操作单元来提交。
在多用户的client/server应用程 序中,最常用的模式是 每个请求一个会话(session-per-request)。 在这种模式下,来自客户端的请求被发送到服务器端(即Hibernate持久化层运行的地方),一 个新的Hibernate Session被打开,并且执行这个操作单元中所有的数据库操作。 一旦操作完成(同时发送到客户端的响应也准备就绪),session被同步,然后关闭。你也可以使用单 个数据库事务来处理客户端请求,在你打开Session之后启动事务,在你关闭 Session之前提交事务。会话和请求之间的关系是一对一的关系,这种模式对 于大多数应用程序来说是很棒的。
真 正的挑战在于如何去实现这种模式:不仅Session和事务必须被正确的开始和结束, 而且他们也必须能被数据访问操作访问。用拦截器来实现操作单元的划分,该拦截器在客户端请求达到服 务器端的时候开始,在服务器端发送响应(即,ServletFilter)之前结束。我们推荐 使用一个ThreadLocal 变量,把 Session绑定到处理客户端请求的线 程上去。这种方式可以让运行在该线程上的所有程序代码轻松的访问Session(就像访问一 个静态变量那样)。你也可以在一个ThreadLocal 变量中保持事务上下文环境,不过这依赖 于你所选择的数据库事 务划分机制。这种实现模式被称之为 ThreadLocal Session和 Open Session in View。你可以很容易的扩展本文前面章节展示的 HibernateUtil 辅助类来实现这种模式。当然,你必须找到一种实现拦截器的方法,并 且可以把拦截器集成到你的应用环境中。请参考Hibernate网站上面的提示和例子。
二、应用程序事务(长事务)
很多业务处理流程都需 要一系列完整的和用户之间的交互,即用户对数据库的交叉访问。在基于web的应用和企业 应用中,跨用户交互的数据库事务是无法接受的。考虑下面的例子:
在界面的第一屏,打开对话框,用户所看到的数据是被一个特定的 Session 和数据 库事务载入(load)的。用户可以随意修改对话框中的数据对象。
5分钟后,用户点击“保存”,期望所做出的修改被持久化;同时他也期望自己是唯一修改这个信息的人,不会出现 修改冲突。
从用户的角度来看,我们把这个操作单元称为应用程序长事务(application transaction)。 在你的应用程序中,可以有很多种方法来实现它。
头一个幼稚的做法是,在用户思考的过程中,保持Session和数据库事务是打开的, 保持数据库锁定,以阻止并发修改,从而保证数据库事务隔离级别和原子操作。这种方式当然是一个反模式, 因为数据库锁定的维持会导致应用程序无法扩展并发用户的数目。
很明显,我们必须使用多个数据库事务来实现一个应用程序事务。在这个例子中,维护业务处理流程的 事务隔离变成了应用程序层的部分责任。单个应用程序事务通常跨越多个数据库事务。如果仅仅只有一 个数据库事务(最后的那个事务)保存更新过的数据,而所有其他事务只是单纯的读取数据(例如在一 个跨越多个请求/响应周期的向导风格的对话框中),那么应用程序事务将保证其原子性。这种方式比听 起来还要容易实现,特别是当你使用了Hibernate的下述特性的时候
- 自动版本化
Hibernate能够自动进行乐观并发控制 ,如果在用户思考 的过程中发生并发修改冲突,Hibernate能够自动检测到。
- 脱 管对象(Detached Objects)
- 如果你决定采用前面已经讨论过的 session-per-request模式,所有载入的实例在用户思考的过程 中都处于与Session脱离的状态。Hibernate允许你把与Session脱离的对象重新关联到Session 上,并且对修改进行持久化,这种模式被称为 session-per-request-with-detached-objects。自动版本化被用来隔离并发修改。
- 长生命周期的Session (Long Session)
- Hibernate 的Session 可以在数据库事 务提交之后和底层的JDBC连接断开,当一个新的客户端请求到来的时候,它又重新连接上底层的 JDBC连接。这种模式被称之为session-per-application-transaction,这种情况可 能会造成不必要的Session和JDBC连接的重新关联。自动版本化被用来隔离并发修改。
关注对象标识(Considering object identity)
应用程序可能在两个不同的Session中并发访问同一持久化状态,但是, 一个持久化类的实例无法在两个 Session中共享。因此有两种不同的标识语义:
数据库标识
foo.getId().equals( bar.getId() )
JVM 标识
foo==bar
对于那些关联到 特定Session (也就是在单个Session的范围内)上的对象来说,这 两种标识的语义是等价的,与数据库标 识对应的JVM标识是由Hibernate来保 证的。不过,当应用程序在两个不同的session中并发访问具有同一持久化标 识的业务对象实例的时候,这个业务对象的两个实例事实上是不相同的(从 JVM识别来看)。这种冲突可以通过在同步和提交的时候使用自动版本化和乐 观锁定方法来解决。
这种方式把关于并发的头疼问题留给了Hibernate和数据库; 由于在单个线程内,操作单元中的对象识别不 需要代价昂贵的锁定或其他意义上的同步,因此它同时可以提供最好的可伸缩性。只要在单个线程只持有一个 Session,应用程序就不需要同步任何业务对象。在Session 的范围内,应用程序可以放心的使用==进行对象比较。
不过,应用程序在Session的外面使用==进行对象比较可能会 导致无法预期的结果。在一些无法预料的场合,例如,如果你把两个脱管对象实例放进同一个 Set的时候,就可能发生。这两个对象实例可能有同一个数据库标 识(也就是说, 他们代表了表的同一行数据),从JVM标识的定义上来说,对脱管的对象而言,Hibernate无法保证他们 的的JVM标识一致。开发人员必须覆盖持久化类的equals()方法和 hashCode() 方法,从而实现自定义的对象相等语义。警告:不要使用数据库标识 来实现对象相等,应该使用业务键值,由唯一的,通常不变的属性组成。当一个瞬时对象被持久化的时 候,它的数据库标识会发生改变。如果一个瞬时对象(通常也包括脱管对象实例)被放入一 个Set,改变它的hashcode会导致与这个Set的关系中断。虽 然业务键值的属性不象数据库主键那样稳定不变,但是你只需要保证在同一个Set 中的对象属性的稳定性就足够了。请到Hibernate网站去寻求这个问题更多的详细的讨论。请注意,这不是一 个有关Hibernate的问题,而仅仅是一个关于Java对象标识和判等行为如何实现的问题。
12.3.3.脱管对象(deatched object)和自动版本化
这种方式下,与持久化存储的每次交互都发生在一个新的Session中。 然而,同一持久化对象实例可以在多次与数据库的 交互中重用。应用程序操纵脱管对象实例 的状态,这个脱管对象实例最初是在另一个Session 中载入的,然后 调用 Session.update(),Session.saveOrUpdate(), 或者 Session.merge() 来重新关联该对象实例。
<>
<>
<>
代码内容 // foo is an instance loaded by a previous Session foo.setProperty("bar"); session = factory.openSession(); Transaction t = session.beginTransaction(); session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded already t.commit(); session.close(); |
Hibernate会再一次在同步的时候检查对象实例的版本,如果发生更新冲突,就抛出异常。
如果你确信对象没有被修改过,你也可以调用lock() 来设置 LockMode.READ(绕过所有的缓存,执行版本检查),从而取 代 update()操作。
12.3.4.定制自动版本化行为
对于特定的属性和集合,通过为它们设置映射属性optimistic-lock的值 为false,来禁止Hibernate的版本自动增加。这样的话,如果该属性 脏数据,Hibernate将不再增加版本号。
遗留系统的数据库Schema通常是静态的,不可修改的。或者,其他应用程序也可能访问同一数据 库,根本无法得知如何处理版本号,甚至时间戳。在以上的所有场景中,实现版本化不能依靠 数据库表 的某个特定列。在的映射中设置 optimistic-lock="all"可以在没有版本或者时间戳属性映射的情况下实现 版本检查,此时Hibernate将比较一行记录的每个字段的状态。请注意,只有当Hibernate能够比 较新旧状态的情况下,这种方式才能生效,也就是说, 你必须使用单个长生命周期Session模式,而不能使用 session-per-request-with-detached-objects模式。
有些情况下,只要更改不发生交错,并发修改也是允许的。当你在 的映射中设置optimistic-lock="dirty",Hibernate在同步的时候将只比较有脏 数据的字段。
在 以上所有场景中,不管是专门设置一个版本/时间戳列,还是进行全部字段/脏数据字段比较, Hibernate都会针对每个实体对象发送一条UPDATE(带有相应的 WHERE语句 )的SQL语句来执行版本检查和数据更新。如果你对关联实体 设置级联关系使用传播性持久化(transitive persistence),那么Hibernate可能会执行不必 要的update语句。这通常不是个问题,但是数据库里 面对on update点火 的触发器可能在脱管对象没有任何更改的情况下被触发。因此,你可以在 的映射中,通过设置select-before-update="true" 来定制这一行为,强制Hibernate SELECT这个对象实例,从而保证, 在更新记录之前,对象的确是被修改过。
12.4.悲观锁定(Pessimistic Locking)
用户其实并不需要花很多精力去担心锁定策略的问题。通常情况下,只要为JDBC连接指定一下隔 离级别,然后让数据库去搞定一切就够了。然而,高级用户有时候希望进行一个排它的悲观锁定, 或者在一个新的事务启动的时候,重新进行锁定。
Hibernate总是使用数据库的锁定机制,从不在内存中锁定对象!
类LockMode 定义了Hibernate所需的不同的锁定级别。一个锁定 可以通过以下的机制来设置:
当Hibernate更新或者插入一行记录的时候,锁定级别自动设置为LockMode.WRITE。
当用户显式的使用数据库支持的SQL格式SELECT ... FOR UPDATE 发送SQL的时候,锁定级别设置为LockMode.UPGRADE
当用户显式的使用Oracle数据库的SQL语句SELECT ... FOR UPDATE NOWAIT 的时候,锁定级别设置LockMode.UPGRADE_NOWAIT
当Hibernate在“可重复读”或者是“序列化”数据库隔离级别下读取数据的时候,锁定模式 自动设置为LockMode.READ。这种模式也可以通过用户显式指定进行设置。
LockMode.NONE 代表无需锁定。在Transaction结束时, 所有的对象都切换到该模式上来。与session相关联的对象通过调用update() 或者saveOrUpdate()脱离该模式。
"显式的用户指定"可以通过以下几种方式之一来表示:
调用 Session.load()的时候指定锁定模式(LockMode)。
调用Session.lock()。
调用Query.setLockMode()。
如 果在UPGRADE或者UPGRADE_NOWAIT锁定模式下调 用Session.load(),并且要读取的对象尚未被session载入过,那么对象 通过SELECT ... FOR UPDATE这样的SQL语句被载入。如果为一个对象调用 load()方法时,该对象已经在另一个较少限制的锁定模式下被载入了,那 么Hibernate就对该对象调用lock() 方法。
如果指定的锁定模式是READ, UPGRADE 或 UPGRADE_NOWAIT,那么Session.lock()就 执行版本号检查。(在UPGRADE 或者UPGRADE_NOWAIT 锁定模式下,执行SELECT ... FOR UPDATE这样的SQL语句。)
如果数据库不支持用户设置的锁定模式,Hibernate将使用适当的替代模式(而不是扔出异常)。 这一点可以确保应用程序的可移植性。
http://www.360doc.com/content/07/0307/21/11192_388255.shtml
ref
http://www.360doc.com/content/07/0307/21/11192_388255.shtml
https://docs.jboss.org/hibernate/orm/4.0/devguide/en-US/html/ch02.html
http://blog.chinaunix.net/uid-20577907-id-3355493.html
http://blog.csdn.net/qjyong/article/details/1874599