duda SimpleJdbctemplate con transction Manager
Hola a todos, mi duda es la sigueinte tengo una operacion de negocio que inserta en una cabecera y 5 detalles, la cuestion es qu si falla la insercion de detalles debo hacer rollback a todas las insercciones anteriores, he tratado de configurar el tranasaction manager de spring y al parecer todo esta bien pero cuando obligo a la operacion a lanzar una excepcion en uno de los detalles no le hacer rollback a nada, anexo mi codigo de configuracion para saber si estoy haciendo algo mal o si me falta algo de antemano gracias por su valiosa atencion
APP Context
<property name="jndiName">
<value>jdbc/SimaEspecial</value>
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<aop:config>
<aop:advisor pointcut="execution(* *..com.easywest.sima.inventario.DAO+.*(..))" advice-ref="txAdvice"/>
</aop:config>
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="insert*" rollback-for="java.lang.RuntimeException"/>
</tx:attributes>
</tx:advice>
<bean id="simpleJdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
<constructor-arg><ref bean="dataSource"/></constructor-arg>
</bean>
.
DAO
@Transactional
public boolean insertConfigC(InvCconfiDTO insrtCconfiDTO){
SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd" );
boolean flag=false;
String sSql="INSERT INTO ABASTO_DB.INV_C_CONFIG_CAB (ID_FOLIO_C,"
+ " ID_MOTIVO,"
+ "B_LUNES,"
+ "B_MARTES,"
+ "B_MIERCOLES,"
+ "B_JUEVES,"
+ "B_VIERNES,"
+ "B_SABADO,"
+ "B_DOMINGO,"
+ " FRECUENCIA,"
+ " FH_CREACION,"
+ "SEMANA_PEDIR,"
+ "F_INICIO_VIGENCIA, "
+" F_FIN_VIGENCIA,"
+ " DIAS_VIGENCIA,"
+ " MAX_ARTICULO,"
+ " ESTATUS )"
+ " VALUES (?,"
+ "?,"
+ "?,"
+ "?,"
+ "?,"
+ "?,"
+ "?,"
+ "?,"
+ "?,"
+ "?,"
+ "SYSDATE,"
+ "?,"
+ "?,"
+ "?,"
+ "?,"
+ "?,"
+ "?)";
try {
simpleJdbcTemplate.update(sSql,
insrtCconfiDTO.getIdFolio(),
insrtCconfiDTO.getIdMotivo(),
valdiaBoolean(insrtCconfiDTO.isB_Lunes()),
valdiaBoolean(insrtCconfiDTO.isB_Martes()),
valdiaBoolean(insrtCconfiDTO.isB_Miercoles()),
valdiaBoolean(insrtCconfiDTO.isB_Jueves()),
valdiaBoolean(insrtCconfiDTO.isB_Viernes()),
valdiaBoolean(insrtCconfiDTO.isB_Sabado()),
valdiaBoolean(insrtCconfiDTO.isB_Domingo()),
insrtCconfiDTO.getFrecuencia(),
insrtCconfiDTO.getSemanaPedir(),
sdf.parse(insrtCconfiDTO.getFchInicioV()),
sdf.parse(insrtCconfiDTO.getFchFinV()),
insrtCconfiDTO.getDiasVigencia(),
insrtCconfiDTO.getMax_art(),
1);
flag=true;
} catch (Exception e) {
log.error("Error: " + e.getMessage());
}
return flag;
}
.
esta es la inserccion del detalle como se ve le estoy mandando un null al id para que lance la excepcion y pro lo consiguiente deberia de ahcer rollback a la cabecera. si alguien me puede ilustrar en que estoy haciendo mal de antemano se los agradezco
public boolean insertArt(InsertDTO insertDTO){
boolean flag=false;
StringBuilder sb=new StringBuilder();
sb.append("INSERT INTO ");
sb.append(OWNER);
sb.append(".INV_C_DET_ARTICULO(");
sb.append("ID_FOLIO_C,");
sb.append("CODIGO_BARRAS) ");
sb.append("VALUES");
sb.append("(");
sb.append("?,");
sb.append("?)");
try {
simpleJdbcTemplate.update(sb.toString(),
null,
insertDTO.getIdArt());
flag=true;
} catch (DataAccessException e) {
log.error("Error: "+e.getMessage());
}
return flag;
}
.
- Inicie sesión o regístrese para enviar comentarios
@Transactional
Si usas
@Transactional
, no veo por qué necesitas lo de aop:config.Todo lo veo bien... excepto que estás cachando DataAccessException.
Para que eso funcione necesitas lo siguiente:
Configurar lo de annotation-driven, indicando una de las dos opciones:
1. Configurar Spring para que tus proxies los haga con CGLIB, creando subclases para envolver TODOS los métodos
2. (más fácil, y la opción default) crear interfaces con los métodos que van a ser transaccionales, y que tus componentes implementen esas interfaces.
Ejemplo:
public void insertar5datos();
}
public class Componente implements Transaccional {
@Transactional
public void insertar5datos() {
//blabla
}
}
Eso lo tienes (casi) bien. Y en tu XML va algo así:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans <a href="http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
" title="http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
">http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
</a> <a href="http://www.springframework.org/schema/context" title="http://www.springframework.org/schema/context">http://www.springframework.org/schema/context</a> <a href="http://www.springframework.org/schema/context/spring-context-3.0.xsd
" title="http://www.springframework.org/schema/context/spring-context-3.0.xsd
">http://www.springframework.org/schema/context/spring-context-3.0.xsd
</a> <a href="http://www.springframework.org/schema/tx" title="http://www.springframework.org/schema/tx">http://www.springframework.org/schema/tx</a> <a href="http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<context:annotation-config" title="http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<context:annotation-config">http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<context:a...</a> />
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="blabla, esto ya lo tienes definido" />
<bean id="componente" class="Componente" />
</beans>
Ahí nomás vi que te sobra lo de aop:config pero tal vez es para otra cosa, no sé.
Lo más importante: NO CACHES las excepciones de tiempo de ejecución! si lo haces pues el interceptor no se puede dar cuenta y por eso no da rollback. Si vas a manejar las excepciones entonces TU tienes que manejar tus transacciones. Necesitas más bien algo así:
public boolean metodo() {
boolean flag = false;
try {
//todo tu codigo
flag = true;
} finally {
//liberar cualquier recurso que se necesite (aunque no veo nada en tu código)
}
return flag;
La documentación de todo este rollo de
@Transactional
y annotation-driven dice que la manera en que funciona es que se abren transacciones para los métodos anotados; si terminan bien su ejecución se hace commit, pero si se arroja cualquier RuntimeException entonces se da rollback. No hay rollback para excepciones declaradas (tu método las debe manejar todas, o arrojar RuntimeException que envuelva una excepción declarada). A fin de cuentas lo que hace es crear un proxy dinámico que envuelva tu componente y hace algo así:TransactionStatus tx = //crear transaccion con el transactionManager
try {
tuobjeto.tumetodo();
} catch (RuntimeException ex) {
tx.setRollbackOnly();
}
transactionManager.commit(tx);
}
Por lo tanto, obviamente si cachas internamente DataAccessException, pues el método externo nunca se da cuenta y se da commit.
GRacias!!!
ok Pues si hare lo del proxy por default, entoces solo necesito configurar mis clases y en ellas debo de poner un throws SQL Exception?? o solo lo debo dejar sin ningu throws?, si en mi Buissne Object primero ejecuto la inserccion de la cabecera y despues las demas inserciones si alguna de las otras inserciones falla se hara el rollback incluso de la cabecera?
y lo de AOP
dado q nunca he usado el transaction manager fui viendo implementaciones y me encontre una muy parecida a la q yo necesitaba pero no funcionaba asi q pro eso esta eso de AOP pero no lo necesito solo fue un ejemplo fallido
SQLException
SQLException es una excepción declarada, no de tiempo de ejecución. Tus métodos anotados con @Transactional deben manejar ese caso (pero si estás usando JdbcTemplate no veo por qué vas a declarar que arrojas eso).
gracias
Ok, ya cambie mi implementaciion pero no le da rollback a la cabecera, si falla algo en la inserccion de los detalles
metodo del DAO
public boolean insertArt(InsertDTO insertDTO){
boolean flag=false;
StringBuilder sb=new StringBuilder();
sb.append("INSERT INTO ");
sb.append(OWNER);
sb.append(".INV_C_DET_ARTICULO(");
sb.append("ID_FOLIO_C,");
sb.append("CODIGO_INTERNO,");
sb.append("CODIGO_BARRAS) ");
sb.append("VALUES");
sb.append("(");
sb.append("?,");
sb.append("?,");
sb.append("?)");
simpleJdbcTemplate.update(sb.toString(),
// insertDTO.getIdFolio(),
null,
insertDTO.getIdCodInt(),
insertDTO.getIdArt());
flag=true;
return flag;
appcontext
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- SECCION PARA LA DECLARACION DEL JNDI -->
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>jdbc/SimaEspecial</value>
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="insert*" rollback-for="java.lang.RuntimeException"/>
</tx:attributes>
</tx:advice>
<bean id="simpleJdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
<constructor-arg><ref bean="dataSource"/></constructor-arg>
</bean>
<!--#lectura del fichero administrable -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders">
<value>true</value>
</property>
<property name="locations">
<list>
<value>classpath:/Mail.properties</value>
</list>
</property>
</bean>
<!--#Configuración del servicio de Spring: MailSession JNDI -->
<!-- <bean id="mailSession"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="mail/Session" />
<property name="resourceRef" value="true" />
</bean>-->
<!--#Configuración del servicio de Spring: MailSernder -->
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="${host}"/>
<property name="port" value="${port}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
<property name="protocol" value="${protocol}"/>
<property name="defaultEncoding" value="UTF-8"/>
<property name="javaMailProperties">
<props>
<!-- Use SMTP transport protocol -->
<prop key="mail.transport.protocol">${protocol}</prop>
<!-- Use SMTP-AUTH to authenticate to SMTP server -->
<prop key="mail.smtp.auth">${mail.smtp.auth}</prop>
<!-- Use TLS to encrypt communication with SMTP server -->
<prop key="mail.smtp.starttls.enable">${mail.smtp.starttls.enable}</prop>
<prop key="mail.debug">true</prop>
</props>
</property>
<!--<property name="session" ref="mailSession" />-->
</bean>
<!-- Seccion de DAO -->
<bean id="UsuarioDAO" class="com.easywest.sima.seguridad.DAO.UsuarioDAO">
<property name="simpleJdbcTemplate" ref="simpleJdbcTemplate"/>
</bean>
<bean id="MenuDAO" class="com.easywest.sima.seguridad.DAO.MenuDAO">
<property name="simpleJdbcTemplate" ref="simpleJdbcTemplate"/>
</bean>
<bean id="PerfilDAO" class="com.easywest.sima.seguridad.DAO.PerfilDAO">
<property name="simpleJdbcTemplate" ref="simpleJdbcTemplate"/>
</bean>
<bean id="MenuPerfilDAO" class="com.easywest.sima.seguridad.DAO.MenuPerfilDAO">
<property name="simpleJdbcTemplate" ref="simpleJdbcTemplate"/>
</bean>
<bean id="IinvConfigDAO" class="com.easywest.sima.inventario.DAO.InvCconfigDAO">
<property name="simpleJdbcTemplate" ref="simpleJdbcTemplate"/>
</bean>
<bean id="FormatoTiendaDAO" class="com.easywest.sima.inventario.DAO.FormatoTiendaDAO">
<property name="simpleJdbcTemplate" ref="simpleJdbcTemplate"/>
</bean>
<bean id="MotivoDAO" class="com.easywest.sima.inventario.DAO.MotivoDAO">
<property name="simpleJdbcTemplate" ref="simpleJdbcTemplate"/>
</bean>
<!-- SECCION DE BO -->
<bean id="loginBO" class="com.easywest.sima.seguridad.BO.LoginBO">
<property name="usuarioDAO" ref="UsuarioDAO"/>
<property name="menuDAO" ref="MenuDAO"/>
</bean>
<bean id="menuBO" class="com.easywest.sima.seguridad.BO.MenuBO">
<property name="menuDAO" ref="MenuDAO"/>
<property name="perfilDAO" ref="PerfilDAO"/>
<property name="menuPerfilDAO" ref="MenuPerfilDAO"/>
</bean>
<bean id="securityBO" class="com.easywest.sima.seguridad.BO.SecurityBO">
<property name="menuPerfilDAO" ref="MenuPerfilDAO"/>
</bean>
<bean id="mailBO" class="com.easywest.sima.seguridad.BO.MailBO">
<property name="usuarioDAO" ref="UsuarioDAO"/>
<property name="mailSender" ref="mailSender"/>
</bean>
<bean id="invCconfigBO" class="com.easywest.sima.inventario.BO.InvCconfigBO">
<property name="InvCconfigDAO" ref="IinvConfigDAO"/>
<property name="formatoTiendaDAO" ref="FormatoTiendaDAO"/>
</bean>
<bean id="MotivoBO" class="com.easywest.sima.inventario.BO.MotivoBO">
<property name="motivoDAO" ref="MotivoDAO"/>
</bean>
<bean id="configEcBO" class="com.easywest.sima.inventario.BO.ConfigEcBO">
</bean>
<!-- SECCION DE ACTION -->
<bean id="login" class="com.easywest.sima.seguridad.Action.LoginAction" scope="prototype">
<property name="loginBO" ref="loginBO"/>
<property name="menuBO" ref="menuBO"/>
</bean>
<bean id="closeSession" class="com.easywest.sima.seguridad.Action.CloseSession" scope="prototype" />
<bean id="menuAction" class="com.easywest.sima.seguridad.Action.MenuAction" scope="prototype" >
<property name="menuBO" ref="menuBO"/>
<property name="perfilBO" ref="perfilBO"/>
<property name="securityBO" ref="securityBO"/>
</bean>
<bean id="mailAction" class="com.easywest.sima.seguridad.Action.MailAction" scope="prototype">
<property name="mailBO" ref="mailBO"/>
</bean>
<bean id="configuracionAction" class="com.easywest.sima.inventario.Action.ConfiguracionAction" scope="prototype">
<property name="invCconfigBO" ref="invCconfigBO"/>
<property name="securityBO" ref="securityBO"/>
<property name="configEcBO" ref="configEcBO"/>
</bean>
<bean id="cfgArtAction" class="com.easywest.sima.inventario.Action.ConfArtAction" scope="prototype">
<property name="invCconfigBO" ref="invCconfigBO"/>
<property name="securityBO" ref="securityBO"/>
</bean>
<bean id="cfgSucAction" class="com.easywest.sima.inventario.Action.ConfSucAction" scope="prototype">
<property name="invCconfigBO" ref="invCconfigBO"/>
<property name="securityBO" ref="securityBO"/>
</bean>
<bean id="cfgGralAction" class="com.easywest.sima.inventario.Action.ConfGralAction" scope="prototype">
<property name="invCconfigBO" ref="invCconfigBO"/>
<property name="securityBO" ref="securityBO"/>
<property name="motivoBO" ref="MotivoBO" />
</bean>
</beans>
BO
if(invCconfigBO.insertConf(configDTO)){
if(lstArticuloDTO!=null){
log.info("insertando configuracion por articulo");
for (ArticuloDTO tmp : lstArticuloDTO) {
InsertDTO insertTmp= new InsertDTO();
insertTmp.setIdFolio(configDTO.getIdFolio());
insertTmp.setIdArt(Long.valueOf(tmp.getCodBarras()));
insertTmp.setIdCodInt(tmp.getCodInterno());
invCconfigBO.insertArt(insertTmp);
}
}else{
log.info("Insertando configuracion por EC");
if(lstProveedorConfigDTO!=null){
for (ProveedorConfigDTO tmp : lstProveedorConfigDTO) {
InsertDTO insertTmp= new InsertDTO();
insertTmp.setIdFolio(configDTO.getIdFolio());
insertTmp.setId(tmp.getIdProveedor());
if(!String.valueOf(tmp.getB_NoResurtible()).equals("0")){
insertTmp.setIdP(0);
}else if(!String.valueOf(tmp.getB_Resurtible()).equals("0")){
insertTmp.setIdP(tmp.getB_Resurtible());
}else if(!String.valueOf(tmp.getB_TodosArt()).equals("0")){
insertTmp.setIdP(tmp.getB_TodosArt());
}
invCconfigBO.insertProv(insertTmp);
}
}
if(lstSeccionDTO!=null){
for (SeccionDTO tmp : lstSeccionDTO) {
InsertDTO insertTmp= new InsertDTO();
insertTmp.setIdFolio(configDTO.getIdFolio());
insertTmp.setId(tmp.getIdSeccion());
invCconfigBO.insertSecc(insertTmp);
}
}
if(lstCategoriaDTO!=null){
for (CategoriaDTO tmp : lstCategoriaDTO) {
InsertDTO insertTmp= new InsertDTO();
insertTmp.setIdFolio(configDTO.getIdFolio());
insertTmp.setId(tmp.getIdCategoria());
invCconfigBO.insertCat(insertTmp);
}
}
}
if(lstSucursalDTO!=null){
for (SucursalDTO tmp : lstSucursalDTO) {
InsertDTO insertTmp= new InsertDTO();
insertTmp.setIdFolio(configDTO.getIdFolio());
insertTmp.setId(tmp.getIdSucursal());
invCconfigBO.insertSuc(insertTmp);
}
}else if(lstFromat!=null){
for (SucursalDTO tmp : lstFromat) {
InsertDTO insertTmp= new InsertDTO();
insertTmp.setIdFolio(configDTO.getIdFolio());
insertTmp.setId(tmp.getIdFormato());
invCconfigBO.insertFormat(insertTmp);
}
}else if(lstSubDir!=null){
for (SucursalDTO tmp : lstSubDir) {
InsertDTO insertTmp= new InsertDTO();
insertTmp.setIdFolio(configDTO.getIdFolio());
insertTmp.setId(tmp.getIdSubdirector());
invCconfigBO.insertSubDir(insertTmp);
}
}
}
mmmm
pues por como se ve, debería funcionar... lee bien la documentación porque tal vez sea un tema de que estás poniéndole un DataSourceTransactionManager a un DataSource del contenedor (porque lo obtienes por JNDI) y creo que en esos casos debes usar también un transactionManager del contenedor (generalmente si el contenedor maneja dataSources, tiene transactionManagers de JTA).
Ok
Lo revisare esto corre bajo un Glasfish 3.1, pero no tengo configurado ningun JTA en el contenedor aun asi tendria que ponerle un transarciton Manager de JTA?
no
El JtaTransactionManager de Spring es una especie de proxy para usar el manejador de transacciones de JTA del contenedor.
Para probar si es el DataSource, sustitúyelo por uno propio (puedes usar un BasicDataSource de apache DBCP, solamente necesitas agregar dos dependencias a tu proyecto, commons-pool y commons-dbcp, y son en runtime, no hay que recompilar nada). Si con un DataSource propio (creado dentro de tu appcontext) todo funciona OK, entonces la bronca es con el dataSource JNDI.
Que tú no hayas configurado JTA en el contenedor no quiere decir que ya venga por default... otra cosa, el driver JDBC que usas para tu base de datos de JDBC4? Porque creo que tiene que ser JDBC4 o cuando menos JDBC3 (los docs de Spring indican todo eso).
ammm
oks, uso oracle y es ojdbc6.jar
revisa
No sé qué sea eso de ojdbc6.jar pero no existe JDBC tipo 6, solamente hay tipos 1 a 4.
okas
Pues segun ORACLE es la ultima version de su JAR De JDBC
Es la version del driver para Oracle 11.
Es un driver tipo 4 y soporta JDBC 4.0. Este driver de Oracle incluye un DataSource (oracle.jdbc.pool.OracleDataSource).