ITEEDU

17.4. 会话断开连接(Session disconnection)

The first approach described above is to maintain a single Session for a whole business process thats spans user think time. (For example, a servlet might keep a Session in the user's HttpSession.) For performance reasons you should

上面提到的第一种方法是对于对一个用户的一次登录产生的整个商业过程维护一个Session。(举例来说,servlet有可能会在用户的HttpSession中保留一个Session)。为性能考虑,你必须

  1. 提交Transaction(或者JDBC连接),然后

  2. (在等待用户操作前,)断开Session与JDBC连接。

Session.disconnect()方法会断开会话与JDBC的连接,把连接返还给连接池(除非是你自己提供这个连接的)。

Session.reconnect()方法会得到一个新的连接(你也可以自己提供一个),重新开始会话。在重新连接后,你可以通过对任何可能被其它事务更新的对象调用Session.lock()方法,来强迫对你没有更新的数据进行版本检查。你不需要对正在更新的数据调用lock()。

这是一个例子:

SessionFactory sessions;
List fooList;
Bar bar;
....
Session s = sessions.openSession();

Transaction tx = null;
try {
    tx = s.beginTransaction();

    fooList = s.find(
    	"select foo from eg.Foo foo where foo.Date = current date"
        // uses db2 date function
    );
    bar = (Bar) s.create(Bar.class);

    tx.commit();
}
catch (Exception e) {
    if (tx!=null) tx.rollback();
    s.close();
    throw e;
}
s.disconnect();
接下来:
s.reconnect();

try {
    tx = sessions.beginTransaction();

    bar.setFooTable( new HashMap() );
    Iterator iter = fooList.iterator();
    while ( iter.hasNext() ) {
        Foo foo = (Foo) iter.next();
        s.lock(foo, LockMode.READ);    //check that foo isn't stale
        bar.getFooTable().put( foo.getName(), foo );
    }

    tx.commit();
}
catch (Exception e) {
    if (tx!=null) tx.rollback();
    throw e;
}
finally {
    s.close();
}

从上面的例子可以看到Transaction和Session之间是多对一的关系。一个Session表示了应用程序与持久存储之间的一个对话,Transaction把这个对话分隔成一个个具有原子性的单元。

17.5. 悲观锁定(Pessimistic Locking)

用户不需要在锁定策略上花费过多时间,通常我们可以选定一种隔离级别(isolationn level),然后让数据库完成所有的工作。高级用户可能希望得到悲观锁定或者在新的事务开始时重新得到锁。

LockMode类定义了Hibernate需要的不同的锁级别。锁由以下的机制得到:

  • LockMode.WRITE在Hibernate更新或插入一行数据时自动得到。

  • LockMode.UPGRADE在用户通过SELECT ... FOR UPDATE这样的特定请求得到,需要数据库支持这种语法。

  • LockMode.UPGRADE_NOWAIT在用户通过SELECT ... FOR UPDATE NOWAIT这样的特定请求在Oracle数据库环境下得到。

  • LockMode.READ在Hibernate在不断读(Repeatable Read)和序列化(Serializable)的隔离级别下读取数据时得到。也可以通过用户的明确请求重新获得。

  • LockMode.NONE表示没有锁。所有对象在Transaction结束时会切换到这种锁模式,通过调用update()或者saveOrUpdate()与会话进行关联的对象,开始时也会在这种锁模式。

“明确的用户请求”会以下的几种方式出现:

  • 调用Session.load(),指定一种LockMode。

  • 调用Session.lock()。

  • 调用Query.setLockMode()。

如果在调用Session.load()时指定了UPGRADE或者UPGRADE_NOWAIT,并且请求的对象还没有被会话调入,那么这个对象会以SELECT ... FOR UPDATE的方式调入。如果调用load()在一个已经调入的对象,并且这个对象调入时的锁级别没有请求时来得严格,Hibernate会对这个对象调用lock()。

Session.lock()会执行版本号检查的特定的锁模式是:READ,UPGRADE或者UPGRADE_NOWAIT。(在UPGRADE或者UPGRADE_NOWAIT,SELECT ... FOR UPGRADE使用的情况下。)

如果数据库不支持所请求的锁模式,Hibernate将会选择一种合适的受支持的锁模式替换(而不是抛出一个异常)。这确保了应用具有可移植性。