Transactions com JPA + SPRING

3 respostas
R

Pessoal, como eu faço para garantir que haja rollback caso uma das funções(f3, por exemplo) falhe em uma estrutura do tipo:
f1() {
f2() { chama um metodo save do entity manager }
f3() { chama um metodo save do entity manager }
f4() { chama um metodo save do entity manager }
}

Minha primeira tentativa foi colocar um annotation @Transactional na f1 mas não resolveu, ele chega a salvar o primeiro objeto(salvo por f2) no banco.
Como é que se faz isso?

Meu appContext:
<?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:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

	<bean
		class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="jpaVendorAdapter">
			<bean
				class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
				<property name="database" value="MYSQL" />
				<property name="showSql" value="true" />
			</bean>
		</property>
	</bean>
		
	<bean id="sharedEntityManagerBean"
		class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>

	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost/2aag" />
		<property name="username" value="" />
		<property name="password" value="" />
	</bean>

	<bean id="transactionManager"
		class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>
	
	<tx:annotation-driven transaction-manager="transactionManager" />
	
	<!-- ********************** -->
    <!-- Injecao de Dependência -->
	<!-- ********************** -->
	
	<bean id="usuarioDao" class="br.com.dr.sipo.dao.impl.UsuarioDaoImpl">  
		<property name="entityManager" ref="sharedEntityManagerBean" />  
	</bean>
	 
</beans>

Agradeço desde já qualquer ajuda!
Obrigado!

3 Respostas

R

Up!

marciocamurati

E ae, não sei como está a sua implementação de BO onde você fez essa anotação para que ocorre-se transaction mas o que pode estar ocorrendo é que o MySQL suporta transaction apenas em um dos seus tipos de tabela que no caso é o INNODB se você criou elas utilizando MyISAM ela não sera transaction safe ou seja não ocorrera nenhum controle de transação.

Se você quiser testar para verificar se o problema é esse você tem duas opções verificar no MySQL o tipo de tabela criado, ou alterar o seu acesso para uma outra base que possua como default transaction como por exemplo HSQL que é uma boa opção para você fazer o teste e verificar se é problema de implementação ou database.

[]s

R
Obrigado Marcio! Estou usando INNODB sim, eu acredito que o problema seja devido ao fato de cada DAO estar realizando uma transaction inteira e comitando a mesma. Vou passar o código que estou usando: DAO's:
package br.com.dreamsolutions.sipo.dao.impl;

import java.util.List;

import javax.persistence.EntityManager;

import org.springframework.dao.DataAccessException;
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
import org.springframework.transaction.annotation.Transactional;

import br.com.dreamsolutions.sipo.dao.GenericDao;

/**
 * @author User
 * 
 */
@Transactional
public class GenericDaoImpl<T> extends AbstractTransactionalDataSourceSpringContextTests implements GenericDao<T> {

	private EntityManager entityManager;
	
	private Class<T> classRef;
	
	//TODO remover este metodo
	public GenericDaoImpl() {
		throw new RuntimeException("Faltou passar a classe de referencia!");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * Contrutor do GenericDao para passar qual a classe
	 */
	public GenericDaoImpl(Class<T> classRef) throws DataAccessException {
		this.classRef = classRef;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.com.dreamsolutions.dao.GenericDao#delete(java.lang.Object)
	 */
	public void delete(T obj) throws DataAccessException {
		entityManager.remove(obj);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.com.dreamsolutions.dao.GenericDao#loadAll()
	 */
	public List<T> loadAll() throws DataAccessException {
		// TODO Auto-generated method stub
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.com.dreamsolutions.dao.GenericDao#loadById()
	 */
	public T loadById(int id) throws DataAccessException {
		return entityManager.find(classRef, (new Integer(id)).toString());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.com.dreamsolutions.dao.GenericDao#save(java.lang.Object)
	 */
	public void save(T obj) throws DataAccessException {
		entityManager.persist(obj);
	}
	/*
	 * 
	 * 
	 */

	/**
	 * @param sharedEntityManagerBean the sharedEntityManagerBean to set
	 */
	public void setEntityManager(
			EntityManager entityManager) {
		this.entityManager = entityManager;
	}

}
E a funcao que chama os mesmos:
package br.com.dreamsolutions.sipo.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import br.com.dreamsolutions.sipo.dao.UsuarioDao;
import br.com.dreamsolutions.sipo.model.Empresa;
import br.com.dreamsolutions.sipo.model.Usuario;

@Transactional
public class TestJPATransactionControl  extends AbstractTransactionalDataSourceSpringContextTests {

	/**
	 * @param args
	 */
	@Test
	@Transactional( propagation = Propagation.REQUIRED)
	public void testJPATransaction1() {
		
		ApplicationContext ctx = new ClassPathXmlApplicationContext("file:WebContent/WEB-INF/applicationContext.xml");
		
		//Criando o objeto 1
		Usuario joao1 = new Usuario();
		joao1.setIdentificacao("1");
		joao1.setNome("joao1");
		joao1.setLogin("joaozinho");
		joao1.setEmpresa(new Empresa());
		joao1.setSenha("123");
		
		//Criando o objeto 2
		Usuario joao2 = new Usuario();
		joao2.setIdentificacao("1");
		joao2.setNome("joao2");
		joao2.setLogin("joaozinho");
		joao2.setEmpresa(new Empresa());
		joao2.setSenha("123");
		
		//Criando o objeto 3
		Usuario joao3 = new Usuario();
		joao3.setIdentificacao("3");
		joao3.setNome("joao3");
		joao3.setLogin("joaozinho");
		joao3.setEmpresa(new Empresa());
		joao3.setSenha("123");
		
		
		
		//Salvando no banco os objetos
		UsuarioDao userDao = (UsuarioDao)ctx.getBean("usuarioDao");
		userDao.save(joao1);
		userDao.save(joao2);
		userDao.save(joao3);
		
		//Testando resultado
		assertEquals(joao1.getLogin(), userDao.loadById(1).getLogin());
		assertEquals(joao2.getLogin(), userDao.loadById(2).getLogin());
		assertEquals(joao3.getLogin(), userDao.loadById(3).getLogin());
		
		
	}
	
}

Note que o segundo save vai gerar Exception, mas o primeiro está sendo salvo.
Caso eu conserte o segundo objeto a funcao salva os 3 no BD normalmente.
Obrigado pela ajuda!
[]'s

Criado 11 de junho de 2008
Ultima resposta 13 de jun. de 2008
Respostas 3
Participantes 2