Categories
不学无术

Hibernate Community Documentation 之 Chapter 5. Locking 锁(翻译)

私人翻译,如有错误请大胆指出,我相信不会有人转载:)
原文见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)关联的对象亦以这种锁模式启动。

 
 
附注:有关锁的其他参考文章

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.