Java Spring@Transaction无法使用LocalContainerEntityManager FactoryBean
我正在开发一个应用程序,它接收各种XML文件,这些文件包含我存储在数据库中的不同“单个”对象。 目标是读取和处理所有文件。如果文件处理正确,它将移动到“已处理”文件夹。如果抛出异常,它将移动到错误文件夹 所需的行为是,如果其中一个文件中发生错误,所有文件都将回滚,数据库中不保存任何内容,所有文件都将复制到错误文件夹(以及已处理的文件) 文件夹的复制可能无法使用事务来完成,所以我手动执行 我的项目结构如下: 技术:Java Spring@Transaction无法使用LocalContainerEntityManager FactoryBean,java,sql-server,spring,hibernate,transactions,Java,Sql Server,Spring,Hibernate,Transactions,我正在开发一个应用程序,它接收各种XML文件,这些文件包含我存储在数据库中的不同“单个”对象。 目标是读取和处理所有文件。如果文件处理正确,它将移动到“已处理”文件夹。如果抛出异常,它将移动到错误文件夹 所需的行为是,如果其中一个文件中发生错误,所有文件都将回滚,数据库中不保存任何内容,所有文件都将复制到错误文件夹(以及已处理的文件) 文件夹的复制可能无法使用事务来完成,所以我手动执行 我的项目结构如下: 技术: Hibernate:3.5.0-Final 弹簧:3.1.1.释放 服务器:T
- Hibernate:3.5.0-Final
- 弹簧:3.1.1.释放
- 服务器:Tomcat7
- 数据库:SQL Server
@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是一个服务,它执行文件的实际读取和对数据库的写入
编辑
这里是我在EntityService中调用add方法的IndividualReader的代码:
@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();
}
}
@Override
@Transactional
public void add(Individual individual) {
individualDao.addIndividual(individual);
}
这个类除了调用DAO之外什么都不做,我用@Transactional注释了它。由于默认值为Propagation.REQUIRED,因此它不会启动新的物理事务,但会加入服务的事务
最后是道:
@Transactional
public void addIndividual(Individual individual) {
em.persist(individual);
}
我还使用事务性注释这个方法,原因与上面相同。
实体管理器使用Spring在DAO中自动连接:
@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>
org.hibernate.dialogue.sqlserverdialogue
现在,所有的编译和部署都很好,并且工作正常。但是,当我使其中一个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>
类路径:/be/healthconnect/pwg/core/properties/pwg.properties
org.hibernate.dialogue.sqlserverdialogue
我犯的错误如下:@Transactional注释无效,因为它注释了一个私有方法。代理生成器忽略了它
我在以下文件中找到了解决方案:
方法可见性和@Transactional
使用代理时,应仅将@Transactional注释应用于具有公共可见性的方法。
如果使用@Transactional注释对受保护的、私有的或包可见的方法进行注释,则不会引发错误,但注释的方法不会显示已配置的事务设置。如果需要注释非公开方法,请考虑使用AspectJ(见下文)。
您是否有
?是的,我已经在application-context.xml中指定了它,包括正确的命名空间定义请发布整个上下文。某些配置可能已关闭。已完成,我已将其添加到我的初始帖子中。我看不到您正在调用的add
。造成这种情况的最可能原因是无意中跨越了某个事务边界。