Почему мой (весенний) HibernateTransactionManager не работает в калитке? - PullRequest
3 голосов
/ 22 сентября 2010

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

Я конвертирую небольшое калитка + хранилище данных + спящий режим веб-приложение для использования калитка + пружина + спящий режим . У меня есть класс обслуживания DAO с Hibernate SessionFactory, внедренный Spring. Я могу выполнять операции только для чтения с использованием фабрики сеансов (по умолчанию автокоммит включен). Я хочу использовать HibernateTransactionManager и аннотацию @Transactional для выполнения транзакционной операции.

Я определяю реализацию службы DAO, которая использует внедренный SessionFactory в методе, помеченном @Transactional:

public class DAO implements IDAO {
 @SpringBean
 private SessionFactory sessionFactory;

 public DAO() {
  super();
 }

 @Transactional
 public Object execute(SessionUnit sessionUnit) {
  Session sess = sessionFactory.getCurrentSession();
  Object result;
  result = sessionUnit.run(sess);
  sess.flush();
  return result;
 }

 public void setSessionFactory(SessionFactory sessionFactory) {
  this.sessionFactory = sessionFactory;
 }

 @Transactional
 public boolean isObjectPersistent(Object object) {
  return sessionFactory.getCurrentSession().contains(object);
 }
}

Когда я пытаюсь вызвать isObjectPersistent(), я получаю исключение гибернации, потому что никто не вызвал session.beginTransaction ():

Caused by: org.hibernate.HibernateException: contains is not valid without active transaction
 at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:338)
 at $Proxy38.contains(Unknown Source)
 at com.gorkwobbler.shadowrun.karma.db.hibernate.DAO.isObjectPersistent(DAO.java:35)
(reflection stuff omitted...)
 at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
 at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
 at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
(reflection stuff omitted...)
 at org.apache.wicket.proxy.LazyInitProxyFactory$JdkHandler.invoke(LazyInitProxyFactory.java:416)
 at org.apache.wicket.proxy.$Proxy36.isObjectPersistent(Unknown Source)

Я также заметил из полной трассировки стека, что OpenSessionInViewFilter вызывается, я не уверен, что это актуально. Дайте мне знать, если вам понадобится остаток трассировки стека.

Если я создаю собственный подкласс WebRequestCycle, который начинает транзакцию, я могу обойти это. Мне кажется, это подрывает цель @Transactional, и моя реализация этого также оказалась проблематичной.

Вот мое приложениеContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Reference: http://wicketinaction.com/2009/06/wicketspringhibernate-configuration/ -->
<beans default-autowire="autodetect"
    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:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <!-- bean definitions -->
    <bean id="wicketApplication" class="com.gorkwobbler.shadowrun.karma.view.wicket.core.WicketApplication" />

    <bean id="placeholderConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="ignoreUnresolvablePlaceholders" value="false" />
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
        <property name="ignoreResourceNotFound" value="false" />
        <property name="locations">
            <list>
                <value>classpath*:/application.properties</value>
            </list>
        </property>
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName">
            <value>${jdbc.driver}</value>
        </property>
        <property name="url">
            <value>${jdbc.url}</value>
        </property>
        <property name="username">
            <value>${jdbc.username}</value>
        </property>
        <property name="password">
            <value>${jdbc.password}</value>
        </property>
    </bean>

    <tx:annotation-driven transaction-manager="txManager" />

    <!-- setup transaction manager  -->
    <bean id="txManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory">
            <ref bean="sessionFactory" />
        </property>
    </bean>

    <!-- hibernate session factory -->
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="configLocation">
            <value>classpath:/hibernate.cfg.xml</value>
        </property>
        <property name="dataSource" ref="dataSource" />
        <property name="hibernateProperties">
            <props>
            </props>
        </property>
        <property name="packagesToScan">
            <list>
                <value>com.gorkwobbler.shadowrun.karma.domain</value>
                <value>com.gorkwobbler.shadowrun.karma.domain.*</value>
            </list>
        </property>
    </bean>

    <bean id="dao"
        class="com.gorkwobbler.shadowrun.karma.db.hibernate.DAO">
        <property name="sessionFactory">
            <ref bean="sessionFactory" />
        </property>
    </bean>

    <!-- Don't know what this is for, but it was in the sample config I started from --> 
    <!-- <context:component-scan base-package="com.gorkwobbler.shadowrun.karma" />  -->
</beans>

Как заставить мой DAO начать транзакцию, зафиксировать в конце этого метода или выполнить откат при ошибке? Я хочу использовать максимально минимальную / стандартную конфигурацию; Я предпочитаю аннотации над XML, если предоставляется выбор.

Edit:

Я пересмотрел applicationContext выше, чтобы удалить вещи конфигурации AOP, которые все равно не работали.

Используя отладчик, я определил, что SessionImpl, хранящийся в карте держателя сеанса TransactionInterceptor, не совпадает с сеансом, который SessionImpl извлекается в методе DAO, когда я вызываю sessionFactory.getCurrentSession (). Кто-нибудь может объяснить, почему это так? Что я делаю неправильно? Магия не работает. = (* * Тысяча тридцать одна

Редактировать

Во время запуска я также вижу следующее сообщение в консоли:

WARN  - stractEhcacheRegionFactory - No TransactionManagerLookup found in Hibernate config, XA Caches will be participating in the two-phase commit!

Ответы [ 2 ]

3 голосов
/ 23 сентября 2010

Оказывается, проблема была не в информации о конфигурации, которую я выложил.Извините!

Моя указанная выше конфигурация связана с внешним файлом hibernate.cfg.xml, в котором объявлено следующее свойство:

    <!-- Enable Hibernate's automatic session context management -->
    <property name="current_session_context_class">thread</property>

Я должен был скопировать это из некоторого примера файла конфигурации hibernate где-нибудь.Это свойство заставило мою SessionFactory игнорировать контекст сеанса, предоставленный Spring, и использовать вместо него локальный контекст потока.Удаление этого свойства устранило проблему (hibernate по умолчанию использует JTA-контекст, если ничего не указано).

2 голосов
/ 22 сентября 2010

Это можно сделать намного проще.Прочтите этот раздел 13.3.3 (Hibernate) Декларативное разграничение транзакций , особенно последняя часть.Этой конфигурации обычно достаточно, если вы используете @Transactional:

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

  <!-- SessionFactory, DataSource, etc. omitted -->

  <bean id="transactionManager"
            class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
  </bean>

  <tx:annotation-driven/>

  <bean id="myProductService" class="product.SimpleProductService">
    <property name="productDao" ref="myProductDao"/>
  </bean>

</beans>

Чтобы ответить на ваши комментарии:

Нет, если вы вводите компоненты через Spring, Spring выполняет подключение,hibernate не должен ничего знать о весне, вот и весь смысл использования пружины (для разделения отдельных слоев).Сервис и дао - это разделение, которое используют не все.Важной частью является то, что открытые методы, поддерживаемые интерфейсом, помечены как транзакционные, потому что все методы, помеченные как транзакционные, будут перехвачены прокси-сервером, выполняющим обработку транзакций, а другие - нет.Прочитать общий раздел о декларативном управлении транзакциями в Spring , чтобы понять процесс (это относится ко всем транзакционным технологиям, а не только к спящему режиму).

...