hibernate支持两种锁:悲观锁(Pessimistic Locking)和乐观锁(Optimistic Locking) 悲观锁:指的是对数据库数据被外界的修改持保守态度(无论是本系统的事务处理,或者是外部系统的事务处理),在整个数据处理的过程数据都处于锁定的状态。hibernate中的
hibernate支持两种锁:悲观锁(Pessimistic Locking)和乐观锁(Optimistic Locking)
悲观锁:指的是对数据库数据被外界的修改持保守态度(无论是本系统的事务处理,或者是外部系统的事务处理),在整个数据处理的过程数据都处于锁定的状态。hibernate中的悲观锁,是依靠数据库中的锁机制(因为只有数据库层才能控制本系统和外部系统对数据库的数据操作)。
例如”select * from user where userName=’Johnson’ for update“这条sql锁定了user表中所有userName=’Johnson’的记录,本次事务提交之前,外界无法修改这些记录。
hibernate中的悲观锁,也是基于数据库的锁机制实现的。
<code class=" hljs lasso"><span class="hljs-built_in">String</span> hqlStr <span class="hljs-subst">=</span><span class="hljs-string">"from TUser as user where user.name='Lili'"</span>; Query query <span class="hljs-subst">=</span> session<span class="hljs-built_in">.</span>createQuery(hqlStr); query<span class="hljs-built_in">.</span>setLockMode(<span class="hljs-string">"user"</span>,LockMode<span class="hljs-built_in">.</span>UPGRADE); <span class="hljs-comment">//加锁</span> <span class="hljs-built_in">List</span> userList <span class="hljs-subst">=</span> query<span class="hljs-built_in">.</span><span class="hljs-built_in">list</span>();<span class="hljs-comment">//执行查询,获取数据</span></code>
上面的代码中setLockMode第一个参数指定了别名为user的返回的记录进行上锁。
生成的sql为:
<code class=" hljs applescript">select tuser0_.<span class="hljs-property">id</span> <span class="hljs-keyword">as</span> <span class="hljs-property">id</span>, tuser0_.<span class="hljs-property">name</span> <span class="hljs-keyword">as</span> <span class="hljs-property">name</span>, tuser0_.group_id <span class="hljs-keyword">as</span> group_id, tuser0_.user_type <span class="hljs-keyword">as</span> user_type, tuser0_.sex <span class="hljs-keyword">as</span> sex <span class="hljs-keyword">from</span> t_user tuser0_ <span class="hljs-keyword">where</span> (tuser0_.<span class="hljs-property">name</span>='Erica' ) <span class="hljs-keyword">for</span> update</code>
可见hibernate通过数据库中的for update子句实现悲观锁机制。
hibernate的加锁模式:
LockMode.NONE : 无锁机制。
LockMode.WRITE : Hibernate 在 Insert 和 Update 记录的时候会自动获取。
LockMode.READ : Hibernate 在读取记录的时候会自动获取。
以上这三种锁机制一般由 Hibernate 内部使用,如 Hibernate 为了保证 Update过程中对象不会被外界修改,会在 save 方法实现中自动为目标对象加上 WRITE 锁。
LockMode.UPGRADE :利用数据库的 for update 子句加锁。
LockMode. UPGRADE_NOWAIT : Oracle 的特定实现,利用 Oracle 的 for update nowait 子句实现加锁。
上面这两种锁机制是我们在应用层较为常用的,加锁一般通过以下方法实现:
Criteria.setLockMode
Query.setLockMode
Session.lock
相对于悲观锁,乐观锁的锁机制就显得比较宽松。悲观锁大部分情况依靠数据库的锁机制实现,来保证最大程度的独占性。但另一方面数据库的开销非常大,尤其对于长事务来说。
乐观锁大部分是基于数据版本(version)记录机制实现。即在数据表中增加一个版本标识,读取出数据时,连带这个版本标识一起读出,更新数据的时候,把版本标识加1。将提交版本数据跟数据库中当前版本信息对比,如果提交的数据中版本号大于数据表当前的版本号,则允许更新,否则认为是过期数据。
hibernate的乐观锁主要有两种方式:version和时间戳
举个配置的例子:
<code class=" hljs xml"> <span class="hljs-pi"><?xml version="1.0"?></span> <span class="hljs-doctype"><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"></span> <span class="hljs-tag"><<span class="hljs-title">hibernate-mapping</span>></span> <span class="hljs-tag"><<span class="hljs-title">class</span> <span class="hljs-attribute">name</span>=<span class="hljs-value">"test.Dir"</span> <span class="hljs-attribute">table</span>=<span class="hljs-value">"t_dir"</span>></span> <span class="hljs-tag"><<span class="hljs-title">id</span> <span class="hljs-attribute">name</span>=<span class="hljs-value">"id"</span> <span class="hljs-attribute">type</span>=<span class="hljs-value">"string"</span> <span class="hljs-attribute">unsaved-value</span>=<span class="hljs-value">"null"</span>></span> <span class="hljs-tag"><<span class="hljs-title">column</span> <span class="hljs-attribute">name</span>=<span class="hljs-value">"id_"</span> <span class="hljs-attribute">sql-type</span>=<span class="hljs-value">"char(32)"</span> <span class="hljs-attribute">not-null</span>=<span class="hljs-value">"true"</span> /></span> <span class="hljs-tag"><<span class="hljs-title">generator</span> <span class="hljs-attribute">class</span>=<span class="hljs-value">"uuid.hex"</span> /></span> <span class="hljs-tag"></<span class="hljs-title">id</span>></span> <span class="hljs-comment"><!--这里version节点必须要配在id之后--></span> <span class="hljs-tag"><<span class="hljs-title">version</span> <span class="hljs-attribute">column</span>=<span class="hljs-value">"version_"</span> <span class="hljs-attribute">name</span>=<span class="hljs-value">"version"</span> /></span> <span class="hljs-tag"><<span class="hljs-title">property</span> <span class="hljs-attribute">name</span>=<span class="hljs-value">"name"</span> <span class="hljs-attribute">column</span>=<span class="hljs-value">"name_"</span> <span class="hljs-attribute">type</span>=<span class="hljs-value">"string"</span>/></span> <span class="hljs-tag"><<span class="hljs-title">property</span> <span class="hljs-attribute">name</span>=<span class="hljs-value">"size"</span> <span class="hljs-attribute">column</span>=<span class="hljs-value">"size_"</span> <span class="hljs-attribute">type</span>=<span class="hljs-value">"long"</span> /></span> <span class="hljs-tag"><<span class="hljs-title">many-to-one</span> <span class="hljs-attribute">name</span>=<span class="hljs-value">"dir"</span> <span class="hljs-attribute">column</span>=<span class="hljs-value">"pid_"</span> <span class="hljs-attribute">class</span>=<span class="hljs-value">"test.Dir"</span> /></span> <span class="hljs-tag"></<span class="hljs-title">class</span>></span> <span class="hljs-tag"></<span class="hljs-title">hibernate-mapping</span>></span> </code>
乐观锁带来的负面问题:如果两个不同的事务同时读取一条数据并进行更新时,程序会报异常:org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)。这时候就要捕获异常,然后处理并提醒用户再次提交。
同样,乐观锁也有局限性。就是只控制了本系统的事务并发操作,而外部系统对数据表的操作却无法控制,此时有个解决办法就是:在存储过程里实现乐观锁机制,这样无论是本系统或是外部系统的事务操作,数据库都可以控制。所以,在设计阶段,尽量考虑到各种情况,究竟是在程序端实现好,还是数据库端实现比较好。