【问题标题】:Spring & Hibernate: non transactional service methodsSpring & Hibernate:非事务性服务方法
【发布时间】:2011-05-19 16:50:21
【问题描述】:

我希望我的读取方法不使用事务,因为这根本不需要,我只用@Transactional 标记我的创建/更新方法。但是我该怎么做呢?我有一个非常基本的 Spring 配置等......

SessionFactory 被注入到我的 DAO 中,并且在每个方法中我调用 sessionFactory.getCurrentSession().doQueryStuff();

然而,这会导致这个错误:

org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here

如果你需要我的 Spring 配置:

<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:oxm="http://www.springframework.org/schema/oxm"
xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
    http://www.springframework.org/schema/oxm
    http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util-3.0.xsd">

<context:annotation-config />
<context:component-scan base-package="be.howest.kidscalcula" />
<mvc:annotation-driven />

<bean id="viewResolver"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix">
        <value>/WEB-INF/views/</value>
    </property>
    <property name="suffix">
        <value>.jsp</value>
    </property>
</bean>


<bean id="myDataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost/kidscalcula" />
    <property name="username" value="root" />
    <property name="password" value="" />
</bean>

<bean class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
    id="sessionFactory">
    <property name="dataSource" ref="myDataSource" />
    <property name="mappingResources">
        <list>
            <value>be/howest/kidscalcula/model/Foto.hbm.xml</value>
            <value>be/howest/kidscalcula/model/Kindleerplanonderdeel.hbm.xml
            </value>
            <value>be/howest/kidscalcula/model/Klas.hbm.xml</value>
            <value>be/howest/kidscalcula/model/Leerkracht.hbm.xml</value>
            <value>be/howest/kidscalcula/model/Leerling.hbm.xml</value>
            <value>be/howest/kidscalcula/model/Leerplan.hbm.xml</value>
            <value>be/howest/kidscalcula/model/LeerplanOefenreeks.hbm.xml
            </value>
            <value>be/howest/kidscalcula/model/Leerplanonderdeel.hbm.xml</value>
            <value>be/howest/kidscalcula/model/Niveau.hbm.xml</value>
            <value>be/howest/kidscalcula/model/Oefenreeks.hbm.xml</value>
            <value>be/howest/kidscalcula/model/Overgangsregel.hbm.xml</value>
            <value>be/howest/kidscalcula/model/Rapport.hbm.xml</value>
            <value>be/howest/kidscalcula/model/RapportLeerplanonderdeel.hbm.xml
            </value>
            <value>be/howest/kidscalcula/model/Schooljaar.hbm.xml</value>
            <value>be/howest/kidscalcula/model/Subonderdeel.hbm.xml</value>
        </list>
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
            <prop key="hibernate.connection.pool_size">3</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.format_sql">true</prop>
            <prop key="hibernate.use_sql_comments">true</prop>
            <prop key="hibernate.cache.use_second_level_cache">false</prop>

        </props>
    </property>
</bean>

<!-- Configure the multipart resolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- one of the properties available; the maximum file size in bytes -->
    <property name="maxUploadSize" value="500000" />
</bean>


<!--
    Transaction manager for a single Hibernate SessionFactory (alternative
    to JTA)
-->
<bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory">
        <ref bean="sessionFactory" />
    </property>
</bean>

<tx:annotation-driven />

此错误是否与传播是标准必需的事实有关?

【问题讨论】:

    标签: java hibernate spring transactions annotations


    【解决方案1】:

    如果没有(休眠)会话,您将无法访问数据。

    提供此类会话的一种方法是(使用 spring 时):

    • org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
    • org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter (这取决于您使用持久性提供程序的方式)

    这个 fiters 的积极副作用是,当 JSP 视图被渲染时会话仍然打开 - 所以在渲染时访问这样一个尚未加载的属性时不会有延迟加载问题。

    @见http://community.jboss.org/wiki/OpenSessioninView

    【讨论】:

      【解决方案2】:

      将方法标记为事务性和只读,以确保事务不会修改任何数据:

      @Transactional(readOnly=true)
      

      在使用 Hibernate 时,这是一个有用的优化。要详细了解此注释的含义,请查看一篇很棒的帖子:Read-Only transactions with Spring and Hibernate

      【讨论】:

      • 我知道这是存在的,但是在没有事务的情况下进行数据读取和使用事务进行数据读取之间的性能差异是什么,但在 readOnly=true 上?我想避免在只读方法中使用@Transactional,因为一开始就不需要事务。
      • 你不能那样做。 Hibernate 要求所有数据访问操作(包括只读)必须在事务内部进行。
      • 不是hibernate,所有的db操作都是开启和关闭一个事务,甚至是SQL开发者中的一个简单的select,比如对一个事务进行读操作是没有问题的。如果您有很多读取,则将所有这些读取都放在一个独特的事务中会更加优化......
      • “只读”事务(令人惊讶)不一定会阻止数据库更新。 Oracle,例如不支持通过 JDBC 的 RO 事务:docs.oracle.com/cd/B19306_01/java.102/b14355/apxtips.htm
      【解决方案3】:

      正如消息中明确指出的,没有会话绑定到线程,并且您指定的配置不允许创建“非事务”会话。我将尝试解释这些场景。

      当在您的配置中启用 Spring 的事务时(如您所做的那样),Spring 将使用代理包装 SessionFactory 对象,以防止在调用 SessionFactory.getCurrentSession 时创建新会话(如果尚未存在)。代理只会获取绑定到本地线程的当前会话,并且您的代码不允许创建临时会话(非事务性)。这就是您的配置阻止非事务性会话创建的方式。

      当你用@Transactional注解一个方法/类时,Spring的TransactionInterceptor会创建一个会话,并在调用该方法时将其绑定到当前线程,以便以后可用。 Spring 的 OpenSessionInViewFilter,如果启用,也会将会话绑定到当前线程。由于您没有执行任何这些操作,因此找不到线程绑定会话。

      你应该做的是用@Transactional(readOnly = True) 注释你的类,然后用@Transactional(readOnly = False) 注释你的创建/更新/删除方法。在读取数据时,您必须拥有一个只读事务,这一点很重要。这可以确保在该调用期间没有人可以提交数据。

      【讨论】:

        【解决方案4】:

        即使使用 Spring 事务管理,也可以在事务之外启动会话。致电sessionFactory.openSession() 以获取新会话。此会话是本地的,因为它没有绑定到线程,因此在您调用sessionFactory.getCurrentSession() 时不会返回。您必须对其进行管理,并确保在会话结束或发生错误时正确关闭它。

        【讨论】:

          猜你喜欢
          • 2014-07-08
          • 2018-06-21
          • 2015-03-26
          • 2012-04-13
          • 2020-06-21
          • 2012-01-29
          • 1970-01-01
          • 1970-01-01
          • 2011-06-13
          相关资源
          最近更新 更多