Note that there are some explanatory texts on larger screens.

plurals
  1. POSpring @Transaction not working with with LocalContainerEntityManagerFactoryBean
    primarykey
    data
    text
    <p>I'm developing an application that receives various XML files containing different 'Individual' objects that I store in a database. The goal is that all the files are read and treated. If a file is processed correctly it moves to the "processed" folder. If an exception is thrown it moves to an error folder.</p> <p>The desired behaviour is that if an error occurs in one of the files, everything is rollbacked , nothing is saved in the database and all the files are copied to the error folder (also the already processed ones)</p> <p>The copying of the folders probably can't be done using transactions so I do them manually...</p> <p>The structure of my project is the following:</p> <p><img src="https://i.stack.imgur.com/8NNf0.png" alt="project structure"></p> <p>Technologies:</p> <ul> <li>Hibernate : 3.5.0-Final</li> <li>Spring : 3.1.1.RELEASE</li> <li>Server : Tomcat 7</li> <li>Database : SQL Server</li> </ul> <p>I start from the idea that the best location to put the transaction is the service. I don't add the propagation property, since I want the default Property.REQUIRED behaviour:</p> <pre><code>@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()); .... </code></pre> <p>Here I start the transaction. The individualReader is a service that performs the actual reading of the file and the writing to the DB.</p> <p><strong>EDIT</strong> Here the code of the IndividualReader where I call the add method in the EntityService:</p> <pre><code> @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&lt;CDM.Individual&gt; jaxbIndividual = unmarshaller.unmarshal(xmlr, CDM.Individual.class); CDM.Individual individual = jaxbIndividual.getValue(); Individual individualDO = individualBuilder.build(individual); Set&lt;Diploma&gt; diplomas = diplomaBuilder.build(individual.getDiplomas(), individualDO); Set&lt;HealthCareProfessional&gt; 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(); } } </code></pre> <p>The lower level is the EntityService:</p> <pre><code>@Override @Transactional public void add(Individual individual) { individualDao.addIndividual(individual); } </code></pre> <p>This class doesn't do anything more than calling the DAO, I annotated it with the @Transactional annotation. Since the default is Propagation.REQUIRED it won't start a new physical transaction, but it will join the transaction of the service.</p> <p>Finally we have the DAO:</p> <pre><code>@Transactional public void addIndividual(Individual individual) { em.persist(individual); } </code></pre> <p>I also annotate this method with Transactional, with the same reason as above. The Entity manager is autowired in the DAO using Spring:</p> <pre><code>@PersistenceContext private EntityManager em; </code></pre> <p>The Entity Manager is defined in the applicationContext as follows:</p> <pre><code>&lt;bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"&gt; &lt;property name="persistenceUnitName" value="individual"/&gt; &lt;property name="jpaVendorAdapter"&gt; &lt;bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"&gt; &lt;property name="databasePlatform" value="org.hibernate.dialect.SQLServerDialect"/&gt; &lt;property name="generateDdl" value="true"/&gt; &lt;property name="showSql" value="false"/&gt; &lt;/bean&gt; &lt;/property&gt; &lt;property name="jpaProperties"&gt; &lt;props&gt; &lt;prop key="hibernate.dialect"&gt;org.hibernate.dialect.SQLServerDialect&lt;/prop&gt; &lt;/props&gt; &lt;/property&gt; &lt;property name="dataSource" ref="dataSource"/&gt; &lt;/bean&gt; </code></pre> <p>Now everything compiles and deploys fine and works even as expected. But when I make one of the XML files corrupt all the files before the corrupt file end up in the DB and the transaction is not rollbacked.</p> <p>I guess I must be missing something and probably my mistake is in the wrong usage of the combination @Transaction and the Spring EntityManager. I never use the explicit em.flush() to push the data to the DB. Maybe the em.persist is wrong and stores the data to the database and I can't recover from it...</p> <p>Anyone has an idea of what I'm doing wrong? Help would be highly appreciated.</p> <p><strong>EDIT</strong> Hereby the complete context:</p> <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;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"&gt; &lt;context:component-scan base-package="be.healthconnect.pwg" /&gt; &lt;task:annotation-driven /&gt; &lt;tx:annotation-driven /&gt; &lt;bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"&gt; &lt;property name="location"&gt; &lt;value&gt;classpath:/be/healthconnect/pwg/core/properties/pwg.properties&lt;/value&gt; &lt;/property&gt; &lt;/bean&gt; &lt;bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"&gt; &lt;property name="driverClassName" value="${datasource.driver.class.name}" /&gt; &lt;property name="url" value="${datasource.url}" /&gt; &lt;property name="username" value="${datasource.username}" /&gt; &lt;property name="password" value="${datasource.password}" /&gt; &lt;/bean&gt; &lt;bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"&gt; &lt;property name="persistenceUnitName" value="individual"/&gt; &lt;property name="jpaVendorAdapter"&gt; &lt;bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"&gt; &lt;property name="databasePlatform" value="org.hibernate.dialect.SQLServerDialect"/&gt; &lt;property name="generateDdl" value="true"/&gt; &lt;property name="showSql" value="false"/&gt; &lt;/bean&gt; &lt;/property&gt; &lt;property name="jpaProperties"&gt; &lt;props&gt; &lt;prop key="hibernate.dialect"&gt;org.hibernate.dialect.SQLServerDialect&lt;/prop&gt; &lt;/props&gt; &lt;/property&gt; &lt;property name="dataSource" ref="dataSource"/&gt; &lt;/bean&gt; &lt;bean id="jpaVendorAdaptor" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" /&gt; &lt;bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"&gt; &lt;property name="entityManagerFactory" ref="emf" /&gt; &lt;/bean&gt; &lt;/beans&gt; </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload