【问题标题】:Proper way to autowire a Hibernate Session in a Spring Transaction JUnit test在 Spring Transaction JUnit 测试中自动装配 Hibernate 会话的正确方法
【发布时间】:2009-09-30 21:32:57
【问题描述】:

这个问题类似于之前的one。我正在尝试在我的一项 Spring-JUnit-Transactional 测试中 @Autowire 一个休眠会话,但我得到了这个异常:

java.lang.IllegalStateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional ...

这是我的 JUnit 类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/applicationContext.xml"})
@TransactionConfiguration(transactionManager="transactionManager")
@Transactional
public class MyTest {
    @Qualifier("session")
    @Autowired
    private Session session;

    @Test
    public void testSomething() {
        session.get(User.class, "me@here.com");
    }
}

如果我 @Autowire a SessionFactory 并像这样以编程方式(而不是在 Spring XML 中定义它)获取我的 Session,则一切正常:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/applicationContext.xml"})
@TransactionConfiguration(transactionManager="transactionManager")
@Transactional
public class MyTest{    
    @Qualifier("sessionFactory")
    @Autowired
    private SessionFactory sessionFactory;

    @Test
    public void testSomething() {
    Session session = SessionFactoryUtils.getSession(sessionFactory, false);
        session.get(User.class, "me@here.com");
    }
}

但是,如果我在 Spring XML 中使用 <aop:scoped-proxy /> 定义 Session,我可以让我的原始示例工作,如下所示:

<?xml version="1.0" encoding="UTF-8"?>

<beans  xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
        ">

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        ...
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation"><value>classpath:/hibernate.cfg.xml</value></property>
        <property name="configurationClass">
            <value>org.hibernate.cfg.AnnotationConfiguration</value>
        </property>
    </bean>

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

    <tx:annotation-driven transaction-manager="transactionManager"/>

    <bean id="session" class="org.springframework.orm.hibernate3.SessionFactoryUtils" factory-method="getSession" scope="prototype">
        <constructor-arg ref="sessionFactory" />
        <constructor-arg value="false" />
        <!-- This is seems to be needed to get rid of the 'No Hibernate Session' error' -->
        <aop:scoped-proxy />
    </bean>
</beans>

我的问题是:为什么需要&lt;aop:scoped-proxy /&gt;,因为我的单元测试中应该只有一个线程绑定事务上下文?什么定义我的Hibernate Session bean 的正确方法?

【问题讨论】:

    标签: java hibernate spring transactions junit


    【解决方案1】:

    SessionFactoryUtils.getSession() 与任何其他获取 Session 的方式一样好。它和 HibernateDaoSupport.getSession() 做的事情是一样的。

    您需要 scoped-proxy 的原因是时间问题。如果没有范围代理,它似乎是在测试开始之前注入会话,因此在事务开始之前注入,所以你会得到错误。

    通过添加作用域代理,它代理 Session 并注入它,因此它不会预先注入实际会话(在事务开始之前),而只会在测试运行时获取它并在它实际需要时调用它反对它。

    【讨论】:

      【解决方案2】:

      我认为“正确”的方式是注入SessionFactory,并以编程方式从中获取会话。您收到异常的原因归结为 SessionFactoryUtils.getSession() 的记录行为:

      获取给定的休眠会话 会话工厂。知道并愿意 返回任何现有的对应 会话绑定到当前线程, 例如使用时 休眠事务管理器。将要 否则创建一个新会话,如果 “allowCreate”为真。

      由于没有将会话绑定到当前事务,因此它失败了。

      我的建议是使用HibernateTemplate - 在您的上下文中定义一个,然后将其自动连接到您的测试中。 HibernateTemplate 具有与战争会话大部分相同的操作,但为您执行会话处理位。你应该能够做到:

      hibernateTemplate.get(User.class, "me@here.com");
      

      【讨论】:

      • 感谢您的回复。如果我将“allowCreate”设置为 true,Spring 似乎会创建第二个非事务性数据库会话,即 @Transactional 注释不会在测试期间回滚我的更改。自动装配 HibernateTemplate 的问题是我有依赖于 Session 的 DAO 级别的类。我想我可以让它们依赖于 HibernateTemplate 然后按照你的建议做一个 get(User.class ...) 。但是,我觉得我违反了得墨忒耳法则,因为 DAO 类的真正依赖是 Session 而不是 HibernateTemplate。
      • 你的 DAO 是用 Session 注入的,还是用 SessionFactory 注入的?如果您要注入 Session,您可能需要重新考虑,这可能不是一个好主意。
      • DAO 被注入 Session。你能解释一下为什么这不是一个好主意吗?谢谢。
      • 因为Session是一个短命的对象,你一般不会像那样注入短命的对象。
      • 再次感谢您的回复。是的,这是真的,但我的 Session 对象的范围是“请求”,Spring 应该在每个 HTTP 请求结束时终止 Session。
      猜你喜欢
      • 2014-03-11
      • 2020-02-09
      • 2011-04-09
      • 1970-01-01
      • 1970-01-01
      • 2018-03-07
      • 1970-01-01
      • 1970-01-01
      • 2015-03-03
      相关资源
      最近更新 更多