【问题标题】:Hibernate transaction manager configurations in SpringSpring 中的 Hibernate 事务管理器配置
【发布时间】:2011-07-05 08:19:08
【问题描述】:

在我的项目中,我使用带有程序化事务划分的 Hibernate。 每次在我的 Service 方法中,我都会写一些类似的东西。

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 方法之外获取用户电子邮件时,我遇到了延迟初始化异常,我的情况是电子邮件是一个简单的字符串。在我调用 POJO 上的任何 getter 之前,Hibernate 甚至不执行 sql 查询。

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

  2. 这种方法有效吗?

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

更新

出于某种原因,如果我将 getCurrentSession 替换为 openSession,则此代码可以正常工作。谁能解释一下?

【问题讨论】:

  • 用户的电子邮件是否在不同的表中?

标签: java hibernate spring spring-transactions


【解决方案1】:

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

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

【讨论】:

    【解决方案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;
    
    }
    

    我个人从来不需要指定 Basic 类型来拥有 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"/>
    

    【讨论】:

    • 谢谢解答,其实User的任何字段都没有惰性初始化,当然我也没有指定简单字符串的获取策略。我相信问题出在弹簧配置中
    • 能否请您发布您的 LazyInitializationException 的堆栈跟踪?如果这是您遇到的异常,就不会有某种形式的延迟初始化,这似乎很奇怪。
    • 错误 org.hibernate.LazyInitializationException - 无法初始化代理 - 没有会话 org.hibernate.LazyInitializationException:无法初始化代理 - org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java: 132) 在 org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190) 处的 org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:174)
    【解决方案3】:

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

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

    在您的案例中,bean 被映射到 spring 代理,因此加载只是返回。因此,您需要使用 get() 而不是 load(),它总是会访问数据库。

    【讨论】:

      猜你喜欢
      • 2017-07-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-13
      • 2012-10-16
      • 2010-10-18
      • 2016-12-02
      • 2011-05-19
      相关资源
      最近更新 更多