Transações com Spring e MySQL

5 respostas
danieldestro

Caros, apesar dos quase 10 anos de estrada em Java, só agora me aventurei no Spring Framework. Antes tarde do que nunca.

Estou fazendo uma aplicação simples (prova de conceito) para um cliente, usando Java 5, Tomcat, Struts 1.x, Spring e MySQL.

Fiz uma busca simples e funcionou, a inserção de dados (no caminho feliz) funcionou, mas quando lanço uma "RuntimeException", ele faz o commit até o ponto atual e descarta o resto. Eu preciso que ele faça, neste último caso, o rollback de toda a transação.

Estou achando que as configurações utilizadas não atribuem o status de auto commit = false para a conexão com o MySQL. Alguém pode ajudar?

poc.xml
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/poc" reloadable="false">
  <Resource name="jdbc/mysqlLocal" 
            auth="Container" 
            driverClassName="com.mysql.jdbc.Driver" 
            maxActive="20" 
            maxIdle="10" 
            type="javax.sql.DataSource" 
            url="jdbc:mysql://localhost:3306/distesdp?autoCommit=false" 
            username="root"  
            password=""/>
            
  <ResourceLink global="jdbc/mysqlLocal" 
                name="jdbc/mysqlLocal" 
                type="javax.sql.DataSource"/>

</Context>
struts-config.xml
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
          "http://struts.apache.org/dtds/struts-config_1_3.dtd">
<struts-config>
  <!-- outras configs aqui -->
	<action-mappings>
		<action path="/insercaoTestes"
			type="org.springframework.web.struts.DelegatingActionProxy"
			input="/insercaoTestes.jsp" validate="false" name="searchForm" />
	</action-mappings>

	<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
		<set-property property="contextConfigLocation" value="/WEB-INF/spring-config.xml" />
	</plug-in>
</struts-config>
spring-config.xml
<?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:jee="http://www.springframework.org/schema/jee"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd 
     http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

	<context:component-scan base-package="poc" />

	<context:annotation-config />
	
	<tx:annotation-driven transaction-manager="txManager"/>
	
	<jee:jndi-lookup id="dataSourceMySQLLocal" jndi-name="jdbc/mysqlLocal" />

	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSourceMySQLLocal" />
	</bean>

	<bean name="/insercaoTestes" class="poc.web.action.InsercaoTestesAction" />
</beans>
ProdutoDAOJdbc.java
package poc.dao;

import java.sql.*;
import java.util.*;
import javax.annotation.Resource;
import org.springframework.jdbc.core.*;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.*;
import poc.modelo.Produto;

@Repository
@Transactional(readOnly=true)
public class ProdutoDAOJdbc implements ProdutoDAO {

    @Resource(name="dataSourceMySQLLocal")
    private DataSource dataSource;
    
	private JdbcTemplate jdbcTemplate;

    public List<Produto> buscarProdutos( String desc ) throws SQLException {
    	final String sql = "SELECT ...";
    	desc = "%"+desc.trim().toUpperCase()+"%";
    	// exemplo usando JDBC Template do Spring
        return getJdbcTemplate().query( sql, new Object[]{desc}, new ProdutoMapper() );

    }
    
    @Transactional(readOnly=false, propagation = Propagation.REQUIRED)
    public void salvarTeste( String nome, String valor ) throws SQLException {
    	final String sql = "INSERT INTO TESTE (NOME,VALOR) VALUES (?,?)";
    	// exemplo usando JDBC Template do Spring
    	getJdbcTemplate().update(sql, new Object[] {nome, valor});
    }

	private JdbcTemplate getJdbcTemplate() {
		if( jdbcTemplate == null ) {
			setJdbcTemplate(new JdbcTemplate(dataSource));
		}
		return jdbcTemplate;
	}

	private void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}
}
CatalogoProdutos.java
package poc.business;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import poc.dao.ProdutoDAO;
import poc.modelo.Produto;

@Service
public class CatalogoProdutos {
	
	private static int idx = 0;
	
	@Autowired
	private ProdutoDAO dao;

	public List<Produto> buscarProdutos( String descricao ) {
		try {
			return dao.buscarProdutos(descricao);
		} catch( Exception e ) {
			throw new RuntimeException(e);
		}
	}
	
	public void salvarDadosTesteTransacao() {
		try {
			for( int i=0; i< 10; i++ ) {
				dao.salvarTeste("Nome "+(++idx),"Valor "+(idx));
				if( i==6 ) {
					throw new RuntimeException("Erro gerado propositalmente na criação dos dados de teste");
				}
			}
		} catch( Exception e ) {
			throw new RuntimeException(e);
		}
	}
	
	public ProdutoDAO getDao() {
		return dao;
	}

	public void setDao(ProdutoDAO dao) {
		this.dao = dao;
	}
}
InsercaoTestesAction.java
package poc.web.action;

import javax.servlet.http.*;
import org.apache.struts.action.*;
import org.springframework.beans.factory.annotation.Autowired;
import poc.dao.*;

public class InsercaoTestesAction extends Action {

	@Autowired
	private CatalogoProdutos catalogo;

	public CatalogoProdutos getCatalogo() {
		return catalogo;
	}

	public void setCatalogo(CatalogoProdutos catalogo) {
		this.catalogo = catalogo;
	}

	public ActionForward execute(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response) throws Exception {

		try {
			getCatalogo().salvarDadosTesteTransacao();
			
		} catch (Exception e) {
			ActionErrors errors = new ActionErrors();
			errors.add("errors", new ActionMessage("msg.error", e.getMessage()));
			saveErrors(request, errors);
		}

		return mapping.getInputForward();
	}
}

5 Respostas

B

Pelo que eu me lembre, o throw faz com que a execução do código caia fora do try, e consequentemente do seu for, não é?

danieldestro

Bom, o código não estava como eu havia feito. Arrumei (editei) o post original para ser ter uma referência correta do programa.

Javabuntu

pelo que entendi do seu propósito, você deve delegar ao spring controlar a transação no seu método da sua classe onde executa a regra do seu negócio:

public class CatalogoProdutos { 

//...
 
  @Transactional(readOnly=false, propagation = Propagation.REQUIRED)
  public void salvarDadosTesteTransacao() {  
        try {  
             for( int i=0; i< 10; i++ ) {  
                 dao.salvarTeste("Nome "+(++idx),"Valor "+(idx));  
                 if( i==6 ) {  
                     throw new RuntimeException("Erro gerado propositalmente na criação dos dados de teste");  
                 }  
             }  
         } catch( Exception e ) {  
             throw new RuntimeException(e);  
         }  
     } 

//...


}

e não no método "salvarTeste" do seu "DAO"...
testa ai pra ver...

danieldestro

Noooooosssa, fui muito amador mesmo. Sabe quando o problema está na sua frente e você não se dá conta? rsrsrs
Era isso mesmo. Obrigado!

Eu mudei a configuração do transacional do DAO para o CatalogoProduto.

Javabuntu

danieldestro:
Noooooosssa, fui muito amador mesmo. Sabe quando o problema está na sua frente e você não se dá conta? rsrsrs
Era isso mesmo. Obrigado!

Eu mudei a configuração do transacional do DAO para o CatalogoProduto.

rsrsrsrs isso acontece até com os mais experientes… :lol:

eu utilizo Spring para transações e gerenciar o hibernate, sempre nos meus DAOs não tenho nenhuma referência ao spring, somente no negócio o qual você já alterou…

e no controller no seu caso você também já está injetando via @Autowired

partindo desta “arquitetura” é fuçar nos recursos que o spring oferece para atender sua necessidade…

flw Hewerton.

Criado 8 de abril de 2009
Ultima resposta 8 de abr. de 2009
Respostas 5
Participantes 3