为什么我的(弹簧)HibernateTransactionManager不能在wicket中工作?

时间:2010-09-22 07:16:09

标签: java hibernate spring transactions wicket

我试图将其缩短到我认为相关的内容,我希望这是足够的而不是压倒性的。请帮忙!

我正在转换一个小的 wicket + databinder + hibernate Web应用程序,以使用 wicket + spring + hibernate 。我有一个DAO服务类,Spring注入了一个hibernate SessionFactory。我可以使用会话工厂执行只读操作(默认情况下自动提交已启用)。我想要做的是使用HibernateTransactionManager和@Transactional注释来进行事务操作。

我定义了一个DAO服务实现,它在标记为@Transactional的方法中使用了一个注入的SessionFactory:

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()时,我得到一个hibernate异常,因为没有人调用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的目的,而我的实现也证明是有问题的。

这是我的applicationContext.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。

修改

我修改了上面的applicationContext,删除了无法正常工作的AOP配置。

使用调试器,我确定存储在TransactionInterceptor的会话持有者映射中的SessionImpl与调用sessionFactory.getCurrentSession()时在DAO方法中检索的SessionImpl不是同一个会话。谁能解释为什么会这样?我究竟做错了什么?神奇不起作用。 =(

修改

我还在启动时在控制台中注意到以下消息:

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

2 个答案:

答案 0 :(得分:3)

事实证明问题实际上并不在我发布的配置信息中。遗憾!

我的上述配置链接到外部化的hibernate.cfg.xml,它声明了以下属性:

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

我一定是从某个样本的hibernate配置文件中复制了这个。此属性导致我的SessionFactory忽略spring提供的会话上下文,并在其位置使用线程本地上下文。删除此属性修复了问题(如果没有指定,则hibernate默认使用JTA上下文)。

答案 1 :(得分:2)

这可以更加简单。阅读本节13.3.3 (Hibernate) Declarative transaction demarcation,尤其是最后一部分。如果您使用@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就会接线,冬眠不需要知道弹簧的任何信息,这就是使用弹簧的整个过程(解耦各个层)。服务和dao是一个并非每个人都使用的分离。重要的一点是接口支持的公共方法被标记为事务性的,因为标记为事务的所有方法都将被执行事务处理的代理拦截,而其他方法则不会。

您可能需要阅读有关Declarative Transaction Managmenet in Spring的常规部分以了解该过程(这适用于所有事务技术,而不仅仅是休眠)。