我试图在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