Transações com Spring e MySQL

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[code]<?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>[/code]

struts-config.xml[code]<?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>

&lt;plug-in className=&quot;org.springframework.web.struts.ContextLoaderPlugIn&quot;&gt;
	&lt;set-property property=&quot;contextConfigLocation&quot; value=&quot;/WEB-INF/spring-config.xml&quot; /&gt;
&lt;/plug-in&gt;

</struts-config>[/code]

spring-config.xml[code]<?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">

&lt;context:component-scan base-package=&quot;poc&quot; /&gt;

&lt;context:annotation-config /&gt;

&lt;tx:annotation-driven transaction-manager=&quot;txManager&quot;/&gt;

&lt;jee:jndi-lookup id=&quot;dataSourceMySQLLocal&quot; jndi-name=&quot;jdbc/mysqlLocal&quot; /&gt;

&lt;bean id=&quot;txManager&quot; class=&quot;org.springframework.jdbc.datasource.DataSourceTransactionManager&quot;&gt;
	&lt;property name=&quot;dataSource&quot; ref=&quot;dataSourceMySQLLocal&quot; /&gt;
&lt;/bean&gt;

&lt;bean name=&quot;/insercaoTestes&quot; class=&quot;poc.web.action.InsercaoTestesAction&quot; /&gt;

</beans>[/code]

ProdutoDAOJdbc.java[code]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=&quot;dataSourceMySQLLocal&quot;)
private DataSource dataSource;

private JdbcTemplate jdbcTemplate;

public List&lt;Produto&gt; buscarProdutos( String desc ) throws SQLException {
	final String sql = &quot;SELECT ...&quot;;
	desc = &quot;%&quot;+desc.trim().toUpperCase()+&quot;%&quot;;
	// 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 = &quot;INSERT INTO TESTE (NOME,VALOR) VALUES (?,?)&quot;;
	// 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;
}

}
[/code]

CatalogoProdutos.java[code]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&lt;Produto&gt; buscarProdutos( String descricao ) {
	try {
		return dao.buscarProdutos(descricao);
	} catch( Exception e ) {
		throw new RuntimeException(e);
	}
}

public void salvarDadosTesteTransacao() {
	try {
		for( int i=0; i&lt; 10; i++ ) {
			dao.salvarTeste(&quot;Nome &quot;+(++idx),&quot;Valor &quot;+(idx));
			if( i==6 ) {
				throw new RuntimeException(&quot;Erro gerado propositalmente na criação dos dados de teste&quot;);
			}
		}
	} catch( Exception e ) {
		throw new RuntimeException(e);
	}
}

public ProdutoDAO getDao() {
	return dao;
}

public void setDao(ProdutoDAO dao) {
	this.dao = dao;
}

}[/code]

InsercaoTestesAction.java[code]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(&quot;errors&quot;, new ActionMessage(&quot;msg.error&quot;, e.getMessage()));
		saveErrors(request, errors);
	}

	return mapping.getInputForward();
}

}[/code]

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 é?

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.

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:

[code]
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);
}
}

//…

}[/code]

e não no método “salvarTeste” do seu “DAO”…
testa ai pra ver…

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.

[quote=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.[/quote]

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.