11

在我的项目中,我使用带有程序化事务划分的 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 方法之外获取用户电子邮件时,我得到了延迟初始化异常,我的情况是电子邮件是一个简单的字符串。Hibernate 甚至不执行 sql 查询,直到我在我的 POJO 上调用任何 getter。

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

  2. 这种方法有效吗?

  3. 你能推荐任何在 spring/hibernate 上使用基于注释的配置的开源项目吗?

更新

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

4

3 回答 3

11

好的,我终于意识到问题所在了。在上面的代码中,我使用加载而不是获取。Session.load 实际上并没有命中数据库。这就是我在@Transactional 方法之外得到延迟初始化异常的原因

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

于 2011-02-28T20:03:05.497 回答
2

在调用 getter 之前,Hibernate 不执行任何 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"/>
于 2011-02-27T19:34:08.940 回答
0

从休眠文档https://docs.jboss.org/hibernate/orm/4.2/manual/en-US/html/ch11.html#objectstate-loading

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

从您案例中的上述文档中,bean 被映射为 spring 代理,因此负载只是返回。因此,您需要使用 get() 而不是 load() 总是命中数据库。

于 2015-02-13T12:49:23.320 回答