Spring и Hibernate: нетранзакционные методы обслуживания

Я хочу, чтобы мои методы чтения не использовали транзакцию, так как это вообще не нужно, я только помечаю свои методы создания/обновления @Transactional. Но как мне это сделать? У меня довольно базовая конфигурация Spring с etc...

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 />

Does this error have anything to do with the fact that Propagation is standard Required?


person toomuchcs    schedule 03.12.2010    source источник


Ответы (4)


Можно начать сеанс вне транзакции даже при использовании управления транзакциями Spring. Позвоните sessionFactory.openSession(), чтобы получить новый сеанс. Этот сеанс является локальным в том смысле, что он не привязан к потоку и поэтому не будет возвращен при вызове sessionFactory.getCurrentSession(). Вам нужно будет управлять им и убедиться, что он правильно закрыт в конце вашего сеанса или в случае возникновения ошибки.

person Eric    schedule 04.03.2011

Отметьте метод как транзакционный и только для чтения, чтобы убедиться, что транзакция не изменяет никаких данных:

@Transactional(readOnly=true)

Это полезная оптимизация при работе с Hibernate. Чтобы узнать больше о значении этой аннотации, вот отличный пост: Транзакции только для чтения с Spring и Hibernate

person Javier Ferrero    schedule 03.12.2010
comment
Я знаю, что это существует, но в чем разница в производительности между чтением данных без транзакции и чтением с транзакцией, но с readOnly=true? Я хотел избежать использования @Transactional в методах только для чтения, поскольку в первую очередь не требуется транзакция. - person toomuchcs; 03.12.2010
comment
Ты не можешь это делать. Hibernate требует, чтобы все операции доступа к данным (включая только чтение) выполнялись внутри транзакции. - person Javier Ferrero; 03.12.2010
comment
Это не спящий режим, все операции БД открывают и закрывают транзакцию, даже простой выбор в разработчике SQL, например, поэтому не проблема поставить операции чтения в транзакцию. Если у вас много чтений, еще более оптимизировано сделать все эти чтения внутри уникальной транзакции... - person Sebastien Lorber; 07.11.2011
comment
Транзакции только для чтения (на удивление) НЕ обязательно предотвращают обновления базы данных. Oracle, например, не поддерживает транзакции RO через JDBC: docs. oracle.com/cd/B19306_01/java.102/b14355/apxtips.htm - person SteveT; 12.12.2012

Как ясно сказано в сообщении, ни один сеанс не привязан к потоку, а указанная вами конфигурация НЕ позволяет создавать сеанс «без транзакции». Я постараюсь объяснить каждый из этих сценариев.

Когда транзакция Spring включена в вашей конфигурации (как вы сделали), Spring обернет объект SessionFactory прокси-сервером, который предотвращает создание новых сеансов, если они еще не существуют, при вызове SessionFactory.getCurrentSession. Прокси-сервер будет получать только текущую сессию, которая привязана к локальному потоку, и создание специального сеанса (нетранзакционного) не разрешено вашим кодом. Вот как ваша конфигурация предотвращает создание нетранзакционного сеанса.

Когда вы аннотируете метод/класс с помощью @Transactional, Spring TransactionInterceptor создаст сеанс и привяжет его к текущему потоку при вызове метода, чтобы он был доступен позже. Spring OpenSessionInViewFilter, если он включен, также привязывает сеанс к текущему потоку. Поскольку вы ничего из этого не делаете, сеанс с привязкой к потоку не найден.

Что вам нужно сделать, так это аннотировать свой класс с помощью @Transactional(readOnly = True), а затем аннотировать ваши методы создания/обновления/удаления с помощью @Transactional(readOnly = False). Важно, чтобы у вас была транзакция только для чтения, когда вы читаете данные. Это гарантирует, что никто не сможет зафиксировать данные во время этого вызова.

person Vineeth    schedule 03.12.2010

Вы не можете получить доступ к данным без (спящего) сеанса.

Один из способов обеспечить такой сеанс (при использовании spring):

  • org.springframework.orm.hibernate3.support.OpenSessionInViewFilter or
  • org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter (это зависит от того, как вы используете поставщика сохраняемости)

Положительным побочным эффектом этого фитера является то, что сеанс все еще открыт, когда отображаются JSP-представления, поэтому у вас не будет проблем с ленивой загрузкой при доступе к такому еще не загруженному свойству во время рендеринга.

@см. http://community.jboss.org/wiki/OpenSessioninView

person Ralph    schedule 03.12.2010