11. 事务和并发 / 11.1. Session和事务范围(transaction scope) / 11.1.3. 关注对象标识(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对象标识和判等行为如何实现的问题。