Spring中的Hibernate事务管理器配置

时间:2011-02-27 19:03:18

标签: java hibernate spring spring-transactions

在我的项目中,我使用Hibernate进行程序化事务划分。 每次在我的服务方法中,我都会写一些类似的内容。

Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
.. perform operation here
session.getTransaction().commit();

现在我要用声明式事务管理重构我的代码。我现在得到了什么...

  <context:component-scan base-package="package.*"/>

  <bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
    <property name="configurationClass">
        <value>org.hibernate.cfg.AnnotationConfiguration</value>
    </property>
  </bean>

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


  <bean id="txManager"  class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory">
         <ref local="mySessionFactory"/>
    </property>
  </bean>

服务类:

@Service
public class TransactionalService {

    @Autowired
    private SessionFactory factory;

    @Transactional
    public User performSimpleOperation() {
        return (User)factory.getCurrentSession().load(User.class, 1L);
    }
}

简单的测试 -

@Test
public void useDeclarativeDem() {
    FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext("spring-config.xml");
    TransactionalService b = (TransactionalService)ctx.getBean("transactionalService");
     User op = b.performSimpleOperation();
     op.getEmail();

当我尝试在Transactional方法之外获取用户电子邮件时,我得到了Lazy初始化异常,电子邮件是我的情况是一个简单的字符串。 Hibernate甚至不执行sql查询,直到我在我的POJO上调用任何getter。

1)我在这里做错了什么?

2)这种方法有效吗?

3)您能否建议任何基于注释的配置在spring / hibernate上工作的开源项目?

更新

出于某种原因,如果我用 openSession 替换 getCurrentSession ,此代码可以正常工作。有人可以解释一下吗?

谢谢

3 个答案:

答案 0 :(得分:11)

好的,最后我意识到问题出在哪里。在上面的代码中,我使用load而不是get。 Session.load实际上没有击中数据库。这就是我在@Transactional方法

之外得到延迟初始化异常的原因

如果我使用openSession而不是getCurrentSession,则会在范围弹簧容器之外打开会话。结果会话没有关闭,它允许我读取@Transactional方法

之外的对象属性

答案 1 :(得分:2)

Hibernate在调用getter之前不执行任何SQL查询的原因是因为我认为FetchType设置为LAZY。要解决此问题,您需要在POJO中将FetchType更改为EAGER:

@Entity
@Table(name = "user")
public class User {

    /*Other data members*/

    @Basic(fetch = FetchType.EAGER)
    private String email;

}

我个人从来没有必要指定基本类型才能拥有EAGER FetchType,因此我不完全确定您的配置需要这个。如果仅在您的测试中,它可能是由于您配置JUnit测试的方式。它应该在类声明中具有类似的东西:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"/test-app-config.xml"})
@Transactional
public class UserServiceTest {

}

至于一个好的资源,我总觉得SpringByExample很有帮助。

修改

所以我不完全确定您的配置有什么问题。它确实与我设置的方式不同,所以这是我的典型配置,希望它有所帮助。 hibernate.transaction.factory_class可能是您缺少的关键属性。我也使用AnnotationSessionFactoryBean

<!-- DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
    p:driverClassName="com.mysql.jdbc.Driver" 
    p:url="jdbc:mysql://localhost/dbname"
    p:username="root"
    p:password="password"/>

<!-- Hibernate session factory -->
<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
    p:dataSource-ref="dataSource">
    <property name="packagesToScan" value="com.beans"/>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</prop>
        </props>
    </property>
</bean> 

<!-- Transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory">
        <ref bean="sessionFactory" />
    </property>
</bean>

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

答案 2 :(得分:0)

来自Hibernate文档https://docs.jboss.org/hibernate/orm/4.2/manual/en-US/html/ch11.html#objectstate-loading

请注意,如果没有匹配的数据库行,load()将抛出不可恢复的异常。如果使用代理映射类,则load()只返回未初始化的代理,并且在调用代理方法之前实际上不会命中数据库。如果您希望创建与对象的关联而不实际从数据库加载它,这将非常有用。如果为类映射定义了批量大小,它还允许将多个实例作为批处理加载。

从上面的案例中,bean的文档与spring代理映射,因此load只返回。因此,您需要使用get()而不是load(),它始终会访问数据库。