从Spring 2.5.6升级到3.2.6导致Spring Transaction Management不再适用于JBoss 5.1.0和Hibernate 3.5.6

时间:2014-02-14 19:08:38

标签: java spring hibernate transactions jboss5.x

我们使用Spring管理的交易很好​​:

JBoss 5.1.0.GA Spring.2.5.6 Hibernate.3.5.6(原生使用)。

升级到Spring 3.2.6(需要升级,因为JBoss 5,Spring 2.5.6和使用Autowiring for Dependency Injection存在一些问题,现在已经解决了)。在Spring升级之后,Spring管理的Transaction已经不再适用了。

这是services-context.xml文件:

<?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:tx="http://www.springframework.org/schema/tx"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xmlns:jee="http://www.springframework.org/schema/jee"
  xmlns:context="http://www.springframework.org/schema/context"
  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/jee 
            http://www.springframework.org/schema/jee/spring-jee-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">

  <context:annotation-config/>
  <context:component-scan base-package="com.mycompany, com.mycompany.imports"/>  

  <jee:jndi-lookup id="dataSource" jndi-name="java:/myDB"/>

  <bean id="hibernateSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="configLocation" value="hibernate.cfg.xml"/>
  </bean>

  <bean id="benefitsRepository" class="com.mycompany.repository.impl.BenefitsHibernateRepository">
    <property name="sessionFactory" ref="hibernateSessionFactory"/>
  </bean>

  <!-- ******************* Configure Transactions ************************** -->

  <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

  <aop:config>
    <aop:pointcut id="serviceOperation" expression="execution(* com.mycompany.service..*Service.*(..))"/>
    <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/>
  </aop:config>

  <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="search*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
</beans>

我们要注入Spring配置事务的Service类如下所示:

package com.mycompany.service.impl;

@Service
public class BenefitsServiceImpl implements BenefitsService
{
    @Autowired
    protected BenefitsRepository        _benefitsRepository;    

    public void assignBenefitToEmployee(Employee employee, Long benefitId)
    {
        // JTA Transaction should begin here

        // We load persistent benefit object (proxy) no database call made yet
        // Note: Spring's HibernateTemplate is going to open Hibernate session/transaction here   
        Benefit benefit = _benefitsRepository.loadBenefit(benefitId);     

        // Hibernate session/transaction is already closed and further calls on proxy are causing LazyInitializationException
        // This is where Hibernate LazyInitializationException gets thrown
        benefit.getEmployees();  

        // JTA Transaction should end here (but obviously not the case as the hibernate session is already closed!)             
    }
}

上面的代码在Spring 2.5.6中运行良好,但是在升级到Spring 3.2.6后,不再注入事务。 HibernateTemplate的默认行为是在通过getHibernateTempalte()。get / find调用进行调用时开始/结束事务,除非已经存在JTA或其他事务,在这种情况下它将传播它。

在Spring升级之前,JTA事务处于服务级别,因此会话在服务调用期间保持打开状态(在我们的示例中为assignBenefitToEmployee()方法调用)。但是现在由于事务不再起作用,Hibernate会话在Repository级别关闭,从而导致LazyInitializationException。

我们可以通过进行以下更改轻松解决问题

_benefitsRepository.loadBenefit(benefitId); // want this to work
// change to :
_benefitsRepository.getBenefit(benefitId); // works

这显然有效,但目标是在服务级别启用事务。我们将此作为测试用例来确保正确配置事务。

为了完整性,这就是Repository的样子:

package com.mycompany.repository.impl;

public class BenefitsHibernateRepository extends HibernateDaoSupport implements BenefitsRepository
{

    public Benefit loadBenefit(Integer benefitId)
    {
        return (Benefit) getHibernateTemplate().load(Benefit.class, benefitId);
    }
}

让我重新进行迭代,所有这些都与Spring 2.5.6完美配合。唯一改变的是:

  1. 升级Spring 2.5.6 - &gt; 3.2.6
  2. 自动装配一些bean(在我们不使用任何注释/自动装配之前)
  3. 我也尝试通过

    配置交易
    <context:annotation-config/>
    <context:component-scan base-package="com.mycompany, com.mycompany.imports"/> 
    <tx:annotation-driven transaction-manager="txManager"/>
    

    但这也没有奏效。

    还尝试将事务类型从JTA更改为Hibernate:

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

    这也没有用。耗尽选项,任何想法??

    以下是有关我们如何构建EAR以及如何初始化Spring的其他信息:

    MyApp.ear:

    • FrontEnd.war(Spring MVC)
      • /WEB-INF/dispatcher-servlet.xml
      • /WEB-INF/applicationContext.xml(导入EJBs.jar项目中的另一个applicationContext.xml)
    • WebServices.war
    • CoreServices.jar(Hibernate)
      • services-context.xml(上面显示的那个,尝试配置事务)
      • beanRefContext.xml
    • EJBs.jar(EJB 2.1)
      • /com/myapp/applicationContext.xml

    这就是FrontEnd.war中的web.xml:

     <context-param>
         <param-name>parentContextKey</param-name>
         <param-value>myapp.ear.context</param-value>
        </context-param>
    
        <listener>
          <listener-class>
            org.springframework.web.context.ContextLoaderListener
          </listener-class>
        </listener>
    
        <servlet>
            <servlet-name>dispatcher</servlet-name>
            <servlet-class>
                org.springframework.web.servlet.DispatcherServlet
            </servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>
                    /WEB-INF/applicationContext.xml
                    /WEB-INF/dispatcher-servlet.xml
                </param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
    

    以下是CoreServices.jar中的beanRefContext.xml的样子(刚刚意识到它仍然指向Spring 2.5.6 xsd schema,这可能是一个问题?)

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
                            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    
        <!-- The context files listed here should contain beans that are used by all WARs, 
             for example Services, EJB's and DAO's etc. -->
        <bean id="com.myapp.services-context" class="org.springframework.context.support.ClassPathXmlApplicationContext">
            <constructor-arg>
              <list>
                <value>services-context.xml</value>
              </list>
            </constructor-arg>
    
    
       <!-- Define an alias to treat services-context as the enterprise wide "EAR" context.
            This context is the parent ApplicationContext for the WebApplicationContexts defined in the WARs. 
            If we ever want to change this parent we simply assign this alias to another context. -->
       <alias name="com.myapp.services-context" alias="myapp.ear.context"/>
    
    
    </beans>
    

    这个想法是允许CoreServices.jar中的服务可用于EAR中存在的所有WAR,这就是为什么我们不通过web.xml中的dispatcher-servlet初始化services-context.xml。

    请注意,我们还在dispatcher-servlet.xml中包含以下内容:

      <context:annotation-config/>
      <context:component-scan base-package="com.myapp"/>
    

    这是为了在FrontEnd.war中的控制器中启用注释。

    希望这可以更好地了解Spring的初始化方式。将理解为什么Spring AOP或基于注释的事务在CoreService.jar项目中不起作用的任何见解。他们再次使用Spring 2.5.6。

1 个答案:

答案 0 :(得分:0)

感谢Boris Treukhov的评论,从dispatcher-servlet.xml中删除额外的<context:annotation-config/>解决了这个问题!

现在配置如下:

dispatcher-servlet.xml :(无组件扫描)

 <context:annotation-config/>

服务-context.xml中:

<context:component-scan base-package="com.myapp, com.myapp.imports"/>

<jee:jndi-lookup id="dataSource" jndi-name="java:/myDB"/>

<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

  <aop:config>
    <aop:pointcut id="serviceOperation" expression="execution(* com.myapp.service..*Service.*(..))"/>
    <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/>
  </aop:config>

  <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="search*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

自动装配,注释和交易在应用程序的所有层中都有效!