gpt4 book ai didi

java - Spring @Transaction 不与 LocalContainerEntityManagerFactoryBean 一起使用

转载 作者:太空宇宙 更新时间:2023-11-04 15:20:36 26 4
gpt4 key购买 nike

我正在开发一个应用程序,它接收各种 XML 文件,其中包含我存储在数据库中的不同“单独”对象。目标是读取和处理所有文件。如果文件处理正确,它将移动到“已处理”文件夹。如果抛出异常,它将移动到错误文件夹。

理想的行为是,如果其中一个文件发生错误,则所有内容都会回滚,数据库中不会保存任何内容,并且所有文件都将复制到错误文件夹(也包括已处理的文件)

文件夹的复制可能无法使用事务来完成,所以我手动进行......

我的项目结构如下:

project structure

技术:

  • hibernate :3.5.0-最终版
  • Spring :3.1.1.RELEASE
  • 服务器:Tomcat 7
  • 数据库:SQL Server

我的想法是,进行交易的最佳位置是服务。我不添加传播属性,因为我想要默认的 Property.REQUIRED 行为:

@Transactional(rollbackFor = Exception.class)
private Feedback readIndividuals(File fileLocation) throws Exception {
System.out.println("Start reading individuals");
//Set the status of all database entries to DELETED
individualEntityService.setAllStatussesToDeleted();
}
final File individualsProcessedFolder = new File(individualsProcessedFolderLocation);
for (final File fileEntry : fileLocation.listFiles()) {
if (fileEntry.isDirectory()) {
readIndividuals(fileEntry, feedback);
} else {
individualReader.read(fileEntry.getAbsolutePath());
....

在这里我开始交易。 individualReader 是一个执行实际读取文件和写入数据库的服务。

编辑这里是 individualReader 的代码,我在 EntityService 中调用 add 方法:

 @Override
@Transactional
public void read(String fileLocation) throws Exception {

JAXBContext jaxbContext = JAXBContext.newInstance(CDM.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();

XMLInputFactory factory = XMLInputFactory.newInstance();
FileInputStream fileInputStream = new FileInputStream(fileLocation);
XMLStreamReader xmlr = factory.createXMLStreamReader(fileInputStream, ENCODING);

try {
xmlr.nextTag();
xmlr.require(XMLStreamConstants.START_ELEMENT, null, "CDM");

xmlr.nextTag();
while (xmlr.getEventType() == XMLStreamConstants.START_ELEMENT) {

JAXBElement<CDM.Individual> jaxbIndividual = unmarshaller.unmarshal(xmlr,
CDM.Individual.class);
CDM.Individual individual = jaxbIndividual.getValue();
Individual individualDO = individualBuilder.build(individual);
Set<Diploma> diplomas = diplomaBuilder.build(individual.getDiplomas(), individualDO);
Set<HealthCareProfessional> healthCareProfessionals = healthCareProfessionalBuilder.build(individual.getHCProfessionals());
individualDO.addHealthCareProfessionals(healthCareProfessionals);
individualDO.addDiplomas(diplomas);
LOG.debug("Adding individual with SSIN [" + individualDO.getSsin() + "] into DB");
Individual retrievedIndividual = individualEntityService.read(individualDO.getSsin());
if (retrievedIndividual != null) {
individualEntityService.remove(retrievedIndividual);
individualDO.setStatus(EntryStatus.UPDATED);
}
individualEntityService.add(individualDO);
LOG.debug("Individual with SSIN [" + individualDO.getSsin() + "] successfully added to DB");
LOG.debug(getIndividualXMLAsString(individualDO));

if (xmlr.getEventType() == XMLStreamConstants.CHARACTERS) {
xmlr.next();
}
}
} finally {
xmlr.close();
fileInputStream.close();
}
}

下层是EntityService:

@Override
@Transactional
public void add(Individual individual) {
individualDao.addIndividual(individual);
}

这个类除了调用 DAO 之外没有做任何事情,我用 @Transactional 注释对其进行了注释。由于默认为Propagation.REQUIRED,因此不会启动新的物理事务,但会加入服务的事务。

最后我们有了 DAO:

@Transactional
public void addIndividual(Individual individual) {
em.persist(individual);
}

我也用Transactional来注释这个方法,原因和上面一样。实体管理器使用 Spring 在 DAO 中 Autowiring :

@PersistenceContext
private EntityManager em;

实体管理器在 applicationContext 中定义如下:

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="individual"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.SQLServerDialect"/>
<property name="generateDdl" value="true"/>
<property name="showSql" value="false"/>
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
</props>
</property>
<property name="dataSource" ref="dataSource"/>
</bean>

现在一切都可以正常编译和部署,并且可以按预期工作。但是,当我使其中一个 XML 文件损坏所有文件时,该文件最终会进入数据库并且事务不会回滚。

我想我一定错过了一些东西,可能我的错误是错误地使用了@Transaction和Spring EntityManager的组合。我从不使用显式 em.flush() 将数据推送到数据库。也许 em.persist 是错误的,并将数据存储到数据库中,但我无法从中恢复......

有人知道我做错了什么吗?我们将非常感谢您的帮助。

编辑以下是完整的上下文:

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

<context:component-scan base-package="be.healthconnect.pwg" />
<task:annotation-driven />
<tx:annotation-driven />

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:/be/healthconnect/pwg/core/properties/pwg.properties</value>
</property>
</bean>

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

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="individual"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.SQLServerDialect"/>
<property name="generateDdl" value="true"/>
<property name="showSql" value="false"/>
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
</props>
</property>
<property name="dataSource" ref="dataSource"/>
</bean>

<bean id="jpaVendorAdaptor"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf" />
</bean>
</beans>

最佳答案

我犯的错误如下:@Transactional 注释没有效果,因为它注释了私有(private)方法。代理生成器忽略它。

我在 Spring Manual chapter 10.5.6 中找到了解决方案:

Method visibility and @Transactional

When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.

关于java - Spring @Transaction 不与 LocalContainerEntityManagerFactoryBean 一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20434798/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com