深入解析Spring托管下Hibernate Session的生命周期

Session生命周期的影响因素

Hibernate Session的生命周期受到其自身属性和方法的影响,简单的说:

  1. SessionFactory的openSession() 方法会开启一个session。

  2. Session的flushMode会决定session何时进行flush。

  3. Session的flush()方法会对session进行强制flush。

  4. Session的close()方法会关闭session。

然而在Spring托管中,session并不是程序员自己控制的,session的生命周期交由Spring管理。影响session生命周期的情况有这几种:

  1. 使用SessionFactory.getCurrentSession()方法。

  2. 使用HibernateTemplate来屏蔽对session的直接访问。

  3. 使用HibernateTransactionManager,并在session使用区域外围包装了本地事务,甚至是事务嵌套。

  4. 使用了JtaTransactionManager,并在session使用区域外围包装了Jta事务,甚至是事务嵌套。

  5. 使用了OpenSessionInViewFilter。

以上情况都对Session的open时间, flushMode, flush时间和close时间有影响。

SessionFactory.getCurrentSession()

查看SessionFactoryImpl可以看到,此方法从CurrentSessionContext中获取存在其中的session。在非Spring环境下,CurrentSessionContext的实现是由“hibernate.current_session_context_class”属性来控制的。但是在Spring环境下,CurrentSessionContext的实现则变成了SpringSessionContext或者SpringJtaSessionContext

伪代码:

// SessionFactoryImpl
// 详细代码请看org.hibernate.internal.SessionFactoryImpl:getCurrentSession
getCurrentSession() {
    return currentSessionContext.currentSession();
}

// SpringSessionContext
// 详细代码请看org.springframework.orm.hibernate4.SpringSessionContext:currentSession
currentSession() {
    session = map.getSessionByKey(this.sessionFactory);
    if (session == null && existTransaction) {
        session = this.sessionFactory.openSession();
        if (currentTransaction.readOnly) session.setFlushMode(MANUAL);
        else session.setFlushMode(AUTO);
        map.setSessionByKey(this.sessionFactory, session);
    }
    return session;
}

上面的伪代码中,假设map是一个存储SessionFactory <-> session的地方(后面会分析map的具体实现和用途),currentSession方法会返回:

  1. 已有的session。

  2. 新建的session (当前没有session,并且存在transaction)。并把FlushMode设置为AUTO或者MANUAL(For readonly)。

  3. throws exception (当前没有session,也没有transaction)。

HibernateTemplate

包装在HibernateTemplate中的session会受到Transaction的影响。

// 查看org.springframework.orm.hibernate4.HibernateTemplate:doExecute
doExecute(callback) {
    try { session = sessionFactory.getCurrentSession();} 
    catch(e){}
    if (session==null) {
        session = sessionFactory.openSession();
        session.setFlushMode(MANUAL);
        isNew=true;
    }
    callback.invoke();
    if (isNew) session.close();    
}

由上面伪代码可以看出,在已经存在session或者transaction的环境下,session不受HibernateTemplate的任何影响。如果当前不存在session和transaction,getCurrentSession会抛出exception。HibernateTemplate会开启一个新的session,并把flushMode设为MANUAL,最后在callback结束后会关闭新建的session。

HibernateTransactionManager

HibernateTransactionManager为本地事务管理器,它不支持分布式数据源,它将事务交给了openSession(), session.commint()和session.rollBack()。任何一个声明了@Transactional的方法,都会被org.springframework.transaction.interceptor.TransactionInterceptor做AOP切割。其父类的方法invokeWithinTransaction执行了一个transaction。

// org.springframework.transaction.interceptor.TransactionAspectSupport
invokeWithinTransaction(invocation) {
  tranInfo = createTransactionIfNecessary();
  transactionManager.doBegin(tranInfo); // 包含在源码createTransactionIfNecessary()中,这里拎出来单说
  invocation.proceed();
  transactionManager.doCommit(tranInfo); // 包含在源码commitTransactionAfterReturning()中
  transactionManager.doCleanupAfterCompletion(tranInfo); // 包含在源码commitTransactionAfterReturning()中
}

上面transactionManager是类HibernateTransactionManager的实例,且doBegin,doCommit与doCleanupAfterCompletion为类中的三个方法。上面伪代码中的四行代码对session的生命周期至关重要。

  1. createTransactionIfNecessary方法会创建一个包装好的transaction,它包含了所有transaction所需要的信息。但它不是UserTransaction和JDBC transaction,所以它只是一个虚拟的transaction实例。transaction实例会查看是否已经存在session或者connection,如果有,则将其存入自己的域中。

  2. 第一步中生成的transaction是否已经存在session, 如果已经存在则跳过。如果没有,真正调用SessionFactory的openSession方法,flushMode设为AUTO。

  3. 先调用session.flush(), 再调用session.commit().

  4. 释放connections。如果session是在第2步中新创建的,则执行session.close()或者注册一个deffer close(OpenViewInFilter中的isSingleSession=false),否则什么也不做。

JtaTransactionManager

在JTA事务下,userTransaction与session在表层代码上不存在任何联系。session的生命周期是如何跟userTransaction绑定在一起的呢?它们之间的桥梁在org.springframework.transaction.support.TransactionSynchronizationManager。TransactionSynchronizationManager中定义的都是ThreadLocal变量,保存了当前线程中正在运行的Transaction信息,以及SpringSessionSynchronization的实例。TransactionSynchronizationManager中都是静态方法,提供对变量的访问。

每一个SpringSessionSynchronization的实例中均保存了一对sessionFactory和session,并提供了对session的操作方法。我们不需要关心这些方法,这些方法都是由TransactionManager自动调用的。

从下面一段代码出发,我们看看TransactionManager都做了什么。

@Transactional
public void transfer(int amount, int form, int to) {
    Session sessionA = this.sessionFactoryA.getCurrentSession();
    Account a = sessionA.load(Account.class, from);
    a.setAmount(a.getAmount()-amount);
    Session sessionB = this.sessionFactoryB.getCurrentSession();
    Account b = sessionB.load(Account.class, to);
    b.setAmount(b.getAmount()+amount);
}

方法会被TransactionIntercepter切割。这部分的原代码同HibernateTransactionManager是相同的,但为了便于理解,我将改变一下伪代码:

// org.springframework.transaction.interceptor.TransactionAspectSupport
invokeWithinTransaction(invocation) {
  tranInfo = createTransactionIfNecessary();
  transactionManager.doBegin(tranInfo); // 包含在源码createTransactionIfNecessary()中,这里拎出来单说
  invocation.proceed();
  transactionManager.triggerBeforeCommit();
  transactionManager.doCommit(tranInfo); // 包含在源码commitTransactionAfterReturning()中
  transactionManager.triggerBeforeComplete()
  transactionManager.doCleanupAfterCompletion(tranInfo); // 包含在源码commitTransactionAfterReturning()中
}
  1. 在执行方法前,intercepter会使用JtaTransactionManager启动一个UserTransaction,并设置TransactionSynchronizationManager中的状态,表明线程处在Transaction中。

  2. sessionFactoryA.getCurrentSession()会使用已经存在的session,如果不存在,则调用openSession()创建一个新的session,flushMode设为AUTO。创建一个SpringSessionSynchronization,将sessionA和sessionFactoryA放入其中。最后调用TransactionSynchronizationManager.registerSynchronization(springSessionSynchronization),把SpringSessionSynchronization放入TransactionSynchronizationManager的synchronization集合中。 此段的源代码可以查看org.springframework.orm.hibernate3.SessionFactoryUtils:doGetSession()。

  3. sessionB的创建过程与步骤2相同,只是它拥有自己的SpringSessionSynchronization。

  4. 在triggerBeforeCommit()方法中,会循环取出TransactionSynchronizationManager下的SpringSessionSynchronization,并调用它的beforeCommit方法,里面会对其包含的session进行flush。

  5. doCommit方法执行userTransaction的commit。

  6. triggerBeforeComplete方法会循环调用SpringSessionSynchronization的beforeComplete方法,如果session是在当前事务创建的,则调用它的close或defer close。如果是事务之前早已存在的session,则不进行close,只释放connection。

OpenSessionInViewFilter

无论是在JTA还是本地事务情况下,有几个sessionFactory实例,就应该声明多少个OSIF。每一个OSIF对应一个sessionFactory,这样才能为每一个sessionFactory都提前创建一个session。

OSIF下有一个属性isSingleSession,如果为true,则整个request中,相同的sessionFactory下只创建一个session,并一直保持到filter结束。如果isSingleSession值为false,OSIF不会创建session,它只会声明在此线程中每个transaction中创建的session,都使用defer close。也就是在filter结束的时候才会close。

总结

以代码为比对:

@Transactional
public void action() {
  Session s = sessionFactory.getCurrentSession();
  ... ...
}

// with HibernateTemplate
@Transactional
public void action() {
  this.hibernateTemplate.save();
}

使用OSIF(isSingleSession=true) 使用OSIF(isSingleSession=false) 不用OSIF
在HibernateTransactionManager中

OSIF创建session,FlushMode可配置

事务边界获取已有session。

事务开始。

进入action()。

获取已有session。

执行操作。

退出action()。

事务边界。

自动flush session。

事务提交。

OSIF中关闭session。

OSIF开启defer close。

事务边界创建新session(FlushMode=AUTO)。

事务开始。

进入action()。

获取已有session。

执行操作。

退出action()。

事务边界

自动flush session。

事务提交。

OSIF中关闭所有session。


事务边界创建新session(FlushMode=AUTO)。

事务开始。

进入action()。

获取已有session。

执行操作。

退出action()。

事务边界

自动flush session。

事务提交。

自动关闭session。

HibernateTransactionManager与HibernateTemplate

同上


同上 同上
在JtaTransactionManager中

OSIF创建session,FlushMode可配置

事务开始。

进入action()。

获取已有session。

执行操作。

退出action()。

事务边界。

自动flush session。

事务提交。

OSIF中关闭session。


OSIF开启defer close。

事务开始。

进入action()。

获取已有session(FlushMode=AUTO)。

执行操作。

退出action()。

事务边界

自动flush session。

事务提交。

OSIF中关闭所有session。

事务开始。

进入action()。

创建新session(FlushMode=AUTO)。

执行操作。

退出action()。

事务边界

自动flush session。

事务提交。

自动close session。

JtaTransactionManager与HibernateTemplate中 同上 同上 同上
不在Transaction中,并使用HibernateTemplate

OSIF创建session,flushMode可配

HibernateTemplate复用session。

session flush依赖template的flushMode属性。

OSIF关闭session。

HibernateTemplate创建session。

session flush依赖template的flushMode属性。

OSIF关闭session。

HibernateTemplate创建session。

session flush依赖template的flushMode属性。

HibernateTemplate关闭session。







你可能感兴趣的:(Hibernate,session,生命周期,Sping)