3

我正在尝试让 hibernate-release-4.1.8.Final 与 Spring 3.1.3.RELEASE 一起使用。

Spring appContext如下:

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="configLocation">
        <value>
            classpath:hibernate.cfg.xml
        </value>
    </property>
    <property name="hibernateProperties">
        <value>
            hibernate.show_sql=true
            hibernate.format_sql=true
            hibernate.current_session_context_class=thread
        </value>
    </property>
    <property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
    <property name="targetDataSource">
        <ref local="mainDataSource"/>
    </property>
</bean>
<!-- mysql with bonecp thread pooling using a lazy jdbc connection -->
<bean id="mainDataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
    <property name="driverClass" value="com.mysql.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql:..."
    ...
</bean>

<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
    <property name="dataSource" ref="dataSource"/>
</bean>

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="*" rollback-for="ServletException" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>
<aop:config>
    <aop:pointcut id="serviceMethods" expression="execution(* com.mytest.service.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>
</aop:config>

以上内容与显示 Hibernate 4 集成的最新 Spring 文档非常相似。注意,我更喜欢使用 spring-aop 横切而不是@Transaction注释。根据 Spring 文档,hibernate4.HibernateTransactionManager应该支持声明性配置的两种样式:注释和 XML。但真的吗?这是发生的事情:

常见的 DAO 结构:

public abstract class MyHibernateDAOImpl<T> implements BaseDAO<T> {
    // spring-injected sessionFactory
    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public Session getSession() {
        //ignore orm.hibernate4.SessionFactoryUtils for now -- will be explained later
        //Session s = SessionFactoryUtils.openSession(sessionFactory);

        //spring-recommended approach to getting session
        Session s = sessionFactory.getCurrentSession();
        return s;
    }

    //basic query
    public List<UserState> getSomeData() {
        Query query = getSession().createQuery("from SomeData");
        return query.list();
    }
}

不使用注释。我的理解是HibernateTransactionManagerinorm.hibernate4将加入 aop-created TransactionThreadLocal并将其绑定到 Session。似乎绑定永远不会发生?或者也许我没有处理Session我认为我正在处理的问题?

假设我的应用程序是标准 Spring-mvc 应用程序,其中 spring-web 库来自与我展示的相同的发布包。此外,视图org.springframework.orm.hibernate4.support.OpenSessionInViewInterceptor用于延长会话生命周期,我没有任何问题......

在 Tomcat6 中运行会产生以下 DEBUG...

[2012-11-10 03:08:59,864] INFO [http-8443-exec-3] MyController.logRequest(273) | Session GET /data/InitialData
[2012-11-10 03:08:59,887] DEBUG [http-8443-exec-3] HibernateTransactionManager.doGetTransaction(290) | Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
[2012-11-10 03:08:59,889] DEBUG [http-8443-exec-3] AbstractPlatformTransactionManager.getTransaction(365) | Creating new transaction with name [com.betterment.service.user.impl.UserStateServiceImpl.getAllStates]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-ServletException
[2012-11-10 03:08:59,889] DEBUG [http-8443-exec-3] HibernateTransactionManager.doBegin(352) | Preparing JDBC Connection of Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
[2012-11-10 03:08:59,915] DEBUG [http-8443-exec-3] AbstractTransactionImpl.begin(158) | begin
[2012-11-10 03:08:59,915] DEBUG [http-8443-exec-3] LogicalConnectionImpl.obtainConnection(295) | Obtaining JDBC connection
[2012-11-10 03:08:59,916] DEBUG [http-8443-exec-3] LogicalConnectionImpl.obtainConnection(301) | Obtained JDBC connection
[2012-11-10 03:08:59,916] DEBUG [http-8443-exec-3] JdbcTransaction.doBegin(69) | initial autocommit status: true
[2012-11-10 03:08:59,916] DEBUG [http-8443-exec-3] JdbcTransaction.doBegin(71) | disabling autocommit
[2012-11-10 03:08:59,921] DEBUG [http-8443-exec-3] HibernateTransactionManager.doBegin(413) | Exposing Hibernate transaction as JDBC transaction [org.hibernate.engine.jdbc.internal.proxy.ConnectionProxyHandler@76566fb[valid=true]]

到目前为止一切顺利......创建了一个事务,我们建立了一个连接,甚至还有一个说法是找到了一个线程绑定的会话。日志输出将我的调试光标放在DAO.getSomeData方法内,就在getSession()被调用之前以及在我创建查询并拉出 list() 之前。

现在,当getSession()被调用时 aSession被返回 - 但不能被调试器检查 - 奇怪。尝试从 中创建 hql 查询时SessionTransaction回滚 - 暗示它实际上并未绑定到ThreadLocal

[2012-11-10 03:16:06,972] DEBUG [http-8443-exec-3] AbstractPlatformTransactionManager.processRollback(843) | Initiating transaction rollback
[2012-11-10 03:16:06,973] DEBUG [http-8443-exec-3] HibernateTransactionManager.doRollback(496) | Rolling back Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
[2012-11-10 03:16:06,973] DEBUG [http-8443-exec-3] AbstractTransactionImpl.rollback(203) | rolling back
[2012-11-10 03:16:06,974] DEBUG [http-8443-exec-3] JdbcTransaction.doRollback(164) | rolled JDBC Connection
[2012-11-10 03:16:06,974] DEBUG [http-8443-exec-3] JdbcTransaction.releaseManagedConnection(126) | re-enabling autocommit
[2012-11-10 03:16:06,982] DEBUG [http-8443-exec-3] HibernateTransactionManager.doCleanupAfterCompletion(564) | Not closing pre-bound Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] after transaction
[2012-11-10 03:16:06,983] DEBUG [http-8443-exec-3] SessionImpl.disconnect(564) | Disconnecting session
[2012-11-10 03:16:06,983] DEBUG [http-8443-exec-3] LogicalConnectionImpl.releaseConnection(314) | Releasing JDBC connection
[2012-11-10 03:16:06,983] DEBUG [http-8443-exec-3] LogicalConnectionImpl.releaseConnection(332) | Released JDBC connection
[2012-11-10 03:16:06,983] DEBUG [http-8443-exec-3] ConnectionProxyHandler.physicalConnectionReleased(219) | HHH000163: Logical connection releasing its physical connection
[2012-11-10 03:16:06,984] DEBUG [http-8443-exec-3] ConnectionProxyHandler.physicalConnectionReleased(219) | HHH000163: Logical connection releasing its physical connection
[2012-11-10 03:16:06,984] DEBUG [http-8443-exec-3] ConnectionProxyHandler.physicalConnectionReleased(219) | HHH000163: Logical connection releasing its physical connection
[2012-11-10 03:16:06,997] DEBUG [http-8443-exec-3] OpenSessionInViewInterceptor.afterCompletion(141) | Closing Hibernate Session in OpenSessionInViewInterceptor
[2012-11-10 03:16:06,999] ERROR [http-8443-exec-3] ErrorFilter.doFilter(50) | Unexpected error in session 'mySession' javax.servlet.ServletException: org.hibernate.HibernateException: createQuery is not valid without active transaction

我尝试的下一个变体是回到SessionFactoryUtils. 它有效,但似乎有很大的副作用。请注意,这是减少的orm.hibernate4.SessionFactoryUtils,而不是 hibernate3 的。这SessionFactory只会暴露一个openSessionand closeSession。有了这个工厂,getSession()看起来像:

public Session getSession() {
    //try with orm.hibernate4.SessionFactoryUtils
    Session s = SessionFactoryUtils.openSession(sessionFactory);
    return s;
}

通过SessionFactoryUtils运行,我确实看到了QueryTranslator最终产生数据结果的结果。问题是我打开了一个会话,在我的整个外部事务完成之前不会关闭。每次调用getSession()都会创建一个新连接,我很快就会用完线程。这是 MySQL>show processlist 观察到的,它显示了在获取 JDBC 连接后我允许 BoneCP 和 Tomcat 冻结的最大连接数:

[2012-11-10 03:31:56,887] DEBUG [http-8443-exec-3] LogicalConnectionImpl.obtainConnection(295) | Obtaining JDBC connection
[2012-11-10 03:31:56,887] DEBUG [http-8443-exec-3] LogicalConnectionImpl.obtainConnection(301) | Obtained JDBC connection

Tomcat 将永远这样坐着。

解决方法似乎是积极的连接管理。我介绍hibernate.connection.release_mode=after_statement了我的<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">定义。

只有 after_statement 似乎有效。有了这个设置,程序执行无问题的琐碎和非琐碎运行。请注意,hibernate.current_session_context_class=thread似乎没有任何影响。总体而言,使用 release_mode 感觉非常 hacky。在每个语句之后积极释放连接的需要必须对负载下的应用程序产生不良后果。

那么 - 发生了什么?为什么不getCurrentSession()返回没有绑定到线程的活动事务的会话?注释是确定何时启动事务@Transactional的唯一方法吗?hibernate4.LocalSessionFactoryBeanSpring Docs 似乎在主题上非常稀疏。

此外,切换到hibernate3.LocalSessionFactoryBean(并将引用 hibernate4 的其他配置切换回 hibernate3)会产生讨厌的java.lang.NoClassDefFoundError: org/hibernate/cache/CacheProvider,它被 stackoverflow 上的其他线程覆盖。

有任何想法吗?非常感谢一些输入。谢谢!

4

0 回答 0