0

i'm trying to learn how to use AOP, and i'm trying to set a read-only transaction in the application context of Spring but it doesn't work, it stills committing data to the DB.

I really don't know what i'm doing wrong, if you can help me, i'll be really glad.

applicationcontext.xml

<?xml version="1.0" encoding="windows-1252"?>
<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:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/tx`enter code here`
    http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

<aop:aspectj-autoproxy proxy-target-class="true" />

<!--advice-->
    <tx:advice  id="txAdvice" transaction-manager="txManager">
       <tx:attributes>            
            <tx:method name="eliminar*" read-only="true"  />
        </tx:attributes>
    </tx:advice>

<!-- Aspect -->
    <bean id="Aop" class="com.all.mymavenii.dao.TiposSolicitudDAO"  />
    <aop:config>
        <aop:pointcut id="point" 
                      expression="execution(* com.all.mymavenii.dao.TiposSolicitudDAO.eliminar(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="point"/>
    </aop:config>

<!-- Data Base configuration  -->
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="/WEB-INF/config/basedatos/jdbc.properties" />
    </bean>

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="${sifa.jdbc.driverClassName}" />
        <property name="jdbcUrl" value="${sifa.jdbc.url}" />
        <property name="user" value="${sifa.jdbc.username}" />
        <property name="password" value="${sifa.jdbc.password}" />
        <property name="acquireIncrement" value="${sifa.c3p0.acquireIncrement}" />
        <property name="minPoolSize" value="${sifa.c3p0.minPoolSize}" />
        <property name="maxPoolSize" value="${sifa.c3p0.maxPoolSize}" />
        <property name="maxIdleTime" value="${sifa.c3p0.maxIdleTime}" />
    </bean>

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

    <context:component-scan base-package="com.all.mymavenii.servicio">
        <context:include-filter expression="org.springframework.stereotype.Service" type="annotation" />
    </context:component-scan>

    <context:component-scan base-package="com.all.mymavenii.dao">
        <context:include-filter expression="org.springframework.stereotype.Repository"    type="annotation" />
    </context:component-scan>

</beans>

IndexController

The code of the request that execute the methot:

@Controller
@RequestMapping("/")
public class IndexControlador {

    @Autowired
    private Servicio service;
/*
. . .
*/
   @RequestMapping(value = "/vista2", method = RequestMethod.POST)
    public String tipoSolicitudEliminar(@RequestParam("clv") String claveTipo) {
        service.eliminar(claveTipo);
        return "redirect:/vista2";
    }      
}

Service

@Service
public class Servicio {
    @Autowired 
    private TiposSolicitudDAO tiposSolicitudDAO;

    public void eliminar(String clave)
    {
        tiposSolicitudDAO.eliminar(clave);        
    }
}

DAO

@Repository("tiposSolicitudDAO")
public class TiposSolicitudDAO {

    private JdbcTemplate jdbcTemplate;
    private final String DELETE_SQL;

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public TiposSolicitudDAO() {
        this.DELETE_SQL = "DELETE FROM sifa_tipos_solicitud WHERE  tiso_clave =? ";
    }

 public void eliminar(String claveTiposSolicitud) {
        jdbcTemplate.update(DELETE_SQL, new Object[]{claveTiposSolicitud});
    }
}

Dependences

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>4.0.5.RELEASE</version>
</dependency>        
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.1</version>
</dependency>        
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.1</version>
</dependency>
Alex
  • 1
  • Can you narrow down where the problem is occurring? Giant walls of code are less likely to get answered due to the effort required by someone else to figure them out. – Dan Is Fiddling By Firelight Jul 03 '14 at 22:15
  • Read-only is just an indication it doesn't enforce, generally , anything. Especially with plain jdbc you are basically at the mercy of the JDBC provider if this has been implemented, most vendors don't bother with it. – M. Deinum Jul 04 '14 at 05:54

1 Answers1

0

As can be seen here (https://jira.spring.io/browse/SPR-8959), the read-only property doesn't result in an automatic rollback at the end of the transaction with JPATransactionManager and MySQL as is expected by many (including me). It seems that the Spring developers don't think this is a bug and there's no plan to fix it.

If you want to always rollback transaction(thus make it read-only) at the end of some methods, my way of doing this is to extend spring's TransactionInterceptor.

The method commitTransactionAfterReturning takes charge of transaction commit.

protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
    if (txInfo != null && txInfo.hasTransaction()) {
        if (logger.isTraceEnabled()) {
            logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
        }
        txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
    }
}

You can see from the last line that by default it just commits the transaction. So what we need to do is to replace it with an if statement.

if (txInfo.getTransactionAttribute().isReadOnly()) {
    txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
} else {            
    txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}

In this way, of course you cannot use <tx:advice> anymore. We also need to config the bean ourselves. Below is an example:

<bean id="txAdvice" class="yourTransactionInterceptor">
    <property name="transactionManager" ref="transactionManager" />
    <property name="transactionAttributes">
        <props>
            <prop key="read*">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
            </prop>
            <prop key="*">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop>
        </props>
    </property>
</bean>
Harper
  • 1,794
  • 14
  • 31