简体   繁体   English

Spring中的Hibernate事务管理器配置

[英]Hibernate transaction manager configurations in Spring

In my project I use Hibernate with programmatic transaction demarcation. 在我的项目中,我使用Hibernate进行程序化事务划分。 Every time in my Service methods i write something similar to this. 每次在我的服务方法中,我都会写一些类似的东西。

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

Now i'm going to refactor my code with declarative transaction management. 现在,我将使用声明式事务管理来重构我的代码。 What i got now ... 我现在得到了什么......

  <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 class: 服务类:

@Service
public class TransactionalService {

    @Autowired
    private SessionFactory factory;

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

And simple test - 而且简单的测试 -

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

When i try to get user email outside of Transactional method, i got Lazy initialization exception, email is my case is a simple string. 当我尝试在Transactional方法之外获取用户电子邮件时,我得到了Lazy初始化异常,电子邮件是我的情况是一个简单的字符串。 Hibernate does not even perform sql query, until i call any getters on my POJO. Hibernate甚至不执行sql查询,直到我在我的POJO上调用任何getter。

1) what i am doing wrong here ? 1)我在这里做错了什么?

2) Is this approach valid ? 2)这种方法有效吗?

3) Can you suggest any opensources project wich work on spring/hibernate with annotation based configuration ? 3)您能否建议任何基于注释的配置在spring / hibernate上工作的开源项目?

Update 更新

For some reason if i replace getCurrentSession with openSession this code works fine. 出于某种原因,如果我用openSession替换getCurrentSession ,这段代码工作正常。 Can someone explain it please ? 有人可以解释一下吗?

Thank you 谢谢

Ok, finally i realized what was the problem. 好的,最后我意识到问题是什么。 In code above i used load instead of get. 在上面的代码中,我使用load而不是get。 Session.load did not actually hit the databased. Session.load实际上没有击中数据库。 That's the reason why i get lazy-initialization exception outside of @Transactional method 这就是为什么我在@Transactional方法之外得到延迟初始化异常的原因

If i use openSession instead of getCurrentSession, session is opened outside of scope spring container. 如果我使用openSession而不是getCurrentSession,则会在范围弹簧容器之外打开会话。 As result session was not close and it allow me to read object properties outside of @Transactional method 结果会话没有关闭,它允许我读取@Transactional方法之外的对象属性

The reason Hibernate does not perform any SQL queries until you call the getters is because I believe the FetchType is set to LAZY. Hibernate在调用getter之前不执行任何SQL查询的原因是因为我认为FetchType设置为LAZY。 To fix this problem you will need to change the FetchType to EAGER in your POJO: 要解决此问题,您需要在POJO中将FetchType更改为EAGER:

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

    /*Other data members*/

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

}

I personally never had to specify Basic types to have an EAGER FetchType so I'm not entirely sure why your configuration requires this. 我个人从来没有必要指定基本类型才能拥有EAGER FetchType,所以我不完全确定你的配置需要这个。 If its only in your tests it could be due to the way you have your JUnit tests configured. 如果仅在您的测试中,它可能是由于您配置JUnit测试的方式。 It should have something like this on the class declaration: 它应该在类声明上有这样的东西:

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

}

As for a good resource I always find SpringByExample to be helpful. 至于一个好的资源,我总是觉得SpringByExample很有帮助。

EDIT 编辑

So I'm not entirely sure what is wrong with your configuration. 所以我不完全确定你的配置有什么问题。 It does differ from the way I have mine set up so here is my typical configuration in hopes that it helps. 它确实与我设置的方式不同,所以这是我的典型配置,希望它有所帮助。 The hibernate.transaction.factory_class could be a key property you are missing. hibernate.transaction.factory_class可能是您缺少的关键属性。 I also use the AnnotationSessionFactoryBean : 我还使用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"/>

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

Be aware that load() will throw an unrecoverable exception if there is no matching database row. 请注意,如果没有匹配的数据库行,load()将抛出不可恢复的异常。 If the class is mapped with a proxy, load() just returns an uninitialized proxy and does not actually hit the database until you invoke a method of the proxy. 如果使用代理映射类,则load()只返回未初始化的代理,并且在调用代理方法之前实际上不会命中数据库。 This is useful if you wish to create an association to an object without actually loading it from the database. 如果您希望创建与对象的关联而不实际从数据库加载它,这将非常有用。 It also allows multiple instances to be loaded as a batch if batch-size is defined for the class mapping. 如果为类映射定义了批量大小,它还允许将多个实例作为批处理加载。

From above documentation in your case bean is mapped with spring proxy so load just returns. 从上面的案例bean中的文档与spring代理映射,因此load只返回。 Hence you need to use get() instead of load() which always hits the database. 因此,您需要使用get()而不是load(),它总是命中数据库。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM