事务未在Tomcat中使用Spring和JPA启动



我试图在tomcat中使用带有Spring的JPA,但在事务没有按预期启动的情况下遇到了问题。

我使用的是Hibernate 3.6.9.Final、Spring 3.0.5.RELEASE,并在Tomcat 6.0.30中运行。

我有一个单独的数据访问jar,其中包含我的实体和数据访问对象。这个jar包含一个META-INF/persistence.xml文件,其中包含以下内容。

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
   <persistence-unit name="osPU">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
   </persistence-unit>
</persistence>

正如您所看到的,这非常简单,实际上只是JPA知道这是包含JPA实体的jar的一个标记。

根据我的springbeans文件中的以下配置,可以发现这些实体是正常的。

<tx:annotation-driven />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
    <property name="jpaProperties"> 
        <props>
            <prop key="hibernate.dialect">${database.dialect}</prop>
            <prop key="hibernate.show_sql">${database.show.sql}</prop>
            <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
        </props>
    </property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" >
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

我已经用标记了我的DAO

@Transactional(propagation=Propagation.MANDATORY)

因为我想确保DAO方法的所有调用方都能正确地创建事务。

我添加了

@Transactional

到从中调用DAO的所有方法。

这将导致一个异常。

org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'

如果我从DAO@Transactional注释中删除propagation.MANDORY,当entityManager尝试刷新更改时,我会收到一个错误。

任何想法都将不胜感激。

EDIT我在堆栈的所有级别上都使用泛型。控制器、服务和DAO。不确定这是否会导致Spring能够拦截方法调用并将其封装在事务中的问题。

更多详细信息这是控制器代码:

public abstract class AbstractEavDefinitionController<DTO> implements EAVDefinitionOperations<DTO> {
    private EavDefinitionService<DTO> eavDefinitionService;
    private Validator<DTO> validator;
    @RequestMapping(method = RequestMethod.POST)
    @ResponseBody
    public ServiceResult<Long> save(@RequestBody DTO dto) {
        ValidationResult<DTO> validationResult = validator.validate(dto);
        if (validationResult.isValid()) {
            Long result = eavDefinitionService.save(dto);
            return new SimpleServiceResult<Long>(result);
        }
        return new SimpleServiceResult<Long>(validationResult);
    }
}

验证器和服务都使用必须有活动事务的DAO。验证器调用按预期工作,我可以看到它正在进行DB调用。对服务的调用抛出

javax.persistence.TransactionRequiredException: no transaction is in progress

我不明白的是,对DAO的一次调用是如何成功的,而下一次调用却失败了。使用Transactional时,控制器调用中可以打开的事务数量是否有限制?

我猜,DAO调用程序不是基于另一个基于spring的组件(@Controller?,JAX-RS resources?,simple classbean,..)调用的接口所以这个类,即使它很好地管理了@Autowired字段,@Transactional操作调用也不会被Spring拦截。解决问题的配置(假设您在上下文中使用LTW):

<?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:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util" xmlns:repository="http://www.springframework.org/schema/data/repository"
    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-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/util http://www.springframework.org/schema/util/spring-util-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/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
            http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
            http://www.springframework.org/schema/data/repository
            http://www.springframework.org/schema/data/repository/spring-repository.xsd
            http://www.springframework.org/schema/data/jpa
            http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"
    default-autowire="byName">
    <context:load-time-weaver />
    <context:spring-configured />
    <context:annotation-config />
    <aop:aspectj-autoproxy />
    <bean name="loadTimeWeaver" class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
    <context:component-scan base-package="com.mycompany.mypackage" />
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
        <property name="jpaProperties"> 
            <props>
                <prop key="hibernate.dialect">${database.dialect}</prop>
                <prop key="hibernate.show_sql">${database.show.sql}</prop>
                <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
            </props>
        </property>
    </bean>
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
        p:entityManagerFactory-ref="entityManagerFactory" />
    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
</beans>

James,我过去采用的方法如下。非常基本的persistence.xml:

<persistence-unit name="audit">
<!-- dont need props here as configured in Spring -->
<properties>
</properties>
</persistence-unit>

然后我的spring上下文连接如下(省略了数据源定义):

<tx:annotation-driven/>
<context:annotation-config/>

<bean id="jpaAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <property name="databasePlatform" value="${db.dialect}"/>
    <property name="showSql" value="true"/>
    <property name="generateDdl" value="false"/>
</bean>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="audit"/>
    <property name="dataSource" ref="datasource"/>
<property name="jpaVendorAdapter" ref="jpaAdapter"/>
<property name="jpaProperties">
    <props>
            <prop key="hibernate.generate_statistics">true</prop>
        </props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<bean id="auditDAO" class="com.gridfreak.core.audit.persistence.JPAAuditDAO"/>

在我实际的dao类中,只需注入持久性上下文并将方法标记为transactional:

public class JPAAuditDAO implements AuditDAO {
@PersistenceContext(unitName="audit")
private EntityManager entityManager = null;

@Transactional(propagation = Propagation.REQUIRED)
public List<VarAudit> findByPiid(long piid) {
        //...
    }
    //....
}

这是一个微不足道的例子,并没有将事务发起的责任委派给包含服务。您是否尝试将事务标记为REQUIRED而不是强制性的,然后查看它们是否已创建,在这种情况下,可能是服务bean本身的事务注释没有被拾取的问题?

Adam

最新更新