私人翻译,如有错误请大胆指出,我相信不会有人转载:)
原文见https://docs.jboss.org/hibernate/orm/4.0/devguide/en-US/html/ch05.html
所谓“锁”是指用来在关系型数据库中防止在数据读取(read)到数据使用(used)之间其内容发生变化的操作。
锁的实现分为两种,分别是乐观(Optimistic)与悲观(Pessimestic)。
锁策略
乐观锁
乐观锁假设多组事务(transaction)能够互不干涉地并发完成,因此这些事务执行时不需要将他们影响到的数据加锁。在事务提交(commit)前,每个事务都会校验以确保它的数据没有被别人改动过。如果校验的结果是已有修改冲突(conflicting modifications),整个事务就会回滚(rollback)。
悲观锁
悲观锁假设事务之间会相互冲突,因此要求在数据读取时上锁,直至数据使用完毕后才释放锁。
Hibernate为以上两种锁策略都提供了支持。
5.1 乐观锁 Optimistic
如果你的应用使用了长事务或是由许多数据库事务组成的会话(conversation),你可以通过存储版本(version)数据使得当一个实体(entity)被两个会话更新时,最后一个提交更改的会话会被告知出现冲突,并且此时不会影响到前一个会话进行的更改。该方法保证了一定的隔离性,扩展性好且特别适用于常读取、少写入(Read-Often Write-Sometimes)的场景。
5.1.1 专用版本号 Dedicated version number
实现乐观锁的版本号机制是通过@Version 注释来实现的
@Entity public class Flight implements Serializable { ... @Version @Column(name="OPTLOCK") public Integer getVersion() { ... } }
此处,Version属性被映射到OPTLOCK列,实体管理器(the entity manager)通过这个注释来检测有冲突的更新,借此防止数据更新被最后一个写入提交操作给覆盖(last-commit-wins)。
Version列可以是任意类型的,只要你定义并且实现一个自定义的UserVersionType即可。
Hibernate禁止你的应用手动变更版本号(version number)。如需手动增加版本号,请查看下列相关文档:
LockModeType.OPTIMISTIC_FORCE_INCREMENT 或 LockModeType.PESSIMISTIC_FORCE_INCREMENTcheck。
如果版本号是由数据库通过注入triggert自动生成的,需使用注释
@org.hibernate.annotations.Generated(GenerationTime.ALWAYS)
<version column="version_column" name="propertyName" type="typename" access="field|property|ClassName" unsaved-value="null|negative|undefined" generated="never|always" insert="true|false" node="element-name|@attribute-name|element/@attribute|." />
column | 存储版本号的列名. 可选,默认是属性名. |
name | 需要持久化的类名 |
type | 版本号存储类型. 可选,默认是 integer . |
access | Hibernate访问属性时使用的方式. 可选, 默认是 property . |
unsaved-value | Indicates that an instance is newly instantiated and thus unsaved. This distinguishes it from detached instances that were saved or loaded in a previous session. The default value,undefined , indicates that the identifier property value should be used. Optional. |
generated | 表明这是由数据库生成的版本号. 可选,默认是never . |
insert | 是否在SQL insert 语句中加入版本号. 默认是 true , 但如果你的版本号列有默认值0的话,可以设置成false. |
5.1.2 时间戳 Timestamp
与版本号相比,时间戳在实现锁时稍显不可靠一些,但如果应用想要借此实现一些其他功能,还是有用的。如果@Version 注释应用在Date或Calendar对象上时,会自动采用时间戳实现。
@Entity public class Flight implements Serializable { ... @Version public Date getLastUpdate() { ... } }
如果你用注释@org.hibernate.annotations.Source 特别指定,Hibernate可以从数据库或者JVM中撷取时间戳的值:可以是org.hibernate.annotations.SourceType.DB或者是org.hibernate.annotations.SourceType.VM,如果不特别指定,默认值是使用数据库的时间戳。
如果指定了@org.hibernate.annotations.Generated(GenerationTime.ALWAYS) 注释,时间戳的值可以借由数据库直接生成,而不是Hibernate。
<timestamp column="timestamp_column" name="propertyName" access="field|property|ClassName" unsaved-value="null|undefined" source="vm|db" generated="never|always" node="element-name|@attribute-name|element/@attribute|." />
column | 存放时间戳的列名。可选,默认值为属性名。 |
name | The name of a JavaBeans style property of Java type Date or Timestamp of the persistent class. |
access | Hibernate用于访问属性的策略。可选,默认为property。 |
unsaved-value | A version property which indicates than instance is newly instantiated, and unsaved. This distinguishes it from detached instances that were saved or loaded in a previous session. The default value of undefined indicates that Hibernate uses the identifier property value. |
source | Whether Hibernate retrieves the timestamp from the database or the current JVM. Database-based timestamps incur an overhead because Hibernate needs to query the database each time to determine the incremental next value. However, database-derived timestamps are safer to use in a clustered environment. Not all database dialects are known to support the retrieval of the database’s current timestamp. Others may also be unsafe for locking, because of lack of precision. |
generated | 是否由数据库内部自动生成时间戳的值。可选,默认值是never。 |
5.2 悲观锁 Pessimistic
通常情况下,你只需要指明一个JDBC的隔离等级(lsolation level),把锁的问题交给数据库处理就行了。如果你确实需要获得独占锁(exclusive locks)或是在事务开始时重新得到(re-obtain)锁,Hibernate会提供给你需要的方法。
Hibernate总是使用数据库的锁机制,从不在内存中给对象上锁
5.2.1 LockMode类
LockMode类指定了Hibernate能取得的各种锁级别。
LockMode.WRITE | 在Hibernate执行update或insert行时自动获取 |
LockMode.UPGRADE | 在用户显式地使用 SELECT ... FOR UPDATE 语句时(仅当数据库支持该语法) |
LockMode.UPGRADE_NOWAIT | 在用户对Oracle数据库显式地使用SELECT ... FOR UPDATE NOWAIT 语句时. |
LockMode.READ | 在Hibernate以Repeatable Read或是Serializable隔离级别读取时自动获取,亦可由用户显式地获取 |
LockMode.NONE | 不启用锁。在事物结束后,所有的对象将会切换到这一状态。通过调用update()或saveOrUpdate()方法与会话(session)关联的对象亦以这种锁模式启动。 |
附注:有关锁的其他参考文章