@Transactional y @Rollback en Spring JDBC

Estoy en un proyecto en el que he abandonado Hibernate. Prefiero tener el control de las queries. Realmente no van a ser unas queries poderosas, pero tambien es cierto que realmente no entiendo la ventaja de Hibernate. Y para colmo no tengo acceso a ningun curso, con el cual pueda abrir los ojos.

He decidido probar Spring JDBC. Uso NamedParameterJdbcTemplate para todas las operaciones. Esto implica no tener que estar inyectando el DataSource por todos los lados. Simplificación y legibilidad.

Con el paso de los días, y en progresión a la experiencia. Decidí hacer test, pero sin grabar en bbdd. Haciendo rollback. Así me queda un proyecto tan completo como los que he realizado con Hibernate. Añadí a mi fichero de configuración de Spring:


    <tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" />

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

y en los métodos en los que se hace insert, update ó delete, indico la anotación @Transactional

	@Transactional
	protected void update(String query, T bean) {
	      // ...
	}

Ahora bien, en los test. He estado probando y realizando pruebas para saber concretamente el uso que tiene Transactional y Rollback.

  • Si pones solo Transacctional , hara rollback, cuando se lance alguna excepción no cacheadas (derivadas de RuntimeException). Dentro de la anotación puedes indicarle otras opciones.
  • Si acompañas la anotacion @Transacctional con @Rollback (por defecto es true), estas indicando que siempre se va a hacer Rollback. Mediante la anotacion Rollback, estas sobreescribiendo el comportamiento de Transacctional, referente a los rollback.
  • 	@Test
    	@Transactional
    	@Rollback
    	public void testSave() {
    	      // ...
    	}
    

    Indicar @Transacctional en el metodo de test, es sobreescribir el comportamiento de @Transacctional en el metodo a testear.

El Javadoc de Rollback dice:

Test annotation to indicate whether or not the transaction for the annotated
test method should be rolled back after the test method has
completed. If {@code true}, the transaction will be rolled back;
otherwise, the transaction will be committed.

@Transactional y @Rollback en Spring JDBC

@Transactional

¿Porque escribir un post sobre esta anotación?, porque me ha tenido 16h bloqueado. En un principio parece que hay que usarla a nivel de clase.Posteriormente, leyendo en stackoverflow, te dicen que a nivel de método. Oki. Tampoco va. Porque tiene que ser a nivel de clase y de método. Olé.¿Porqué?
Ejemplo en el manual de Spring. ¿Alguien ve anotaciones a nivel de método?

// the service class that we want to make transactional

@Transactional
public class DefaultFooService implements FooService {

 Foo getFoo(String fooName);
 Foo getFoo(String fooName, String barName);
 void insertFoo(Foo foo);
 void updateFoo(Foo foo);

}

Su applicationContext.xml


<!-- from the file 'context.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: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-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/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

<!-- this is the service object that we want to make transactional -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>

<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="txManager"/>

<!-- a PlatformTransactionManager is still required -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- (this dependency is defined somewhere else) -->
<property name="dataSource" ref="dataSource"/>
</bean>

<!-- other <bean/> definitions here -->

</beans>

Del manual de Spring:

Si  <bean id=»txManager»> se llamase transactionManager, entonces no haría falta ponerlo en: <tx:annotation-driven transaction-manager=»txManager»/>. Se quedaria: <tx:annotation-driven />.

«You can place the @Transactional annotation before an interface definition, a method on an interface, a class definition, or a public method on a class. However, the mere presence of the @Transactional annotation is not enough to activate the transactional behavior.» Es decir se puede poner conjuntamente, pero no aclara que debe ser asi.

«Spring recommends that you only annotate concrete classes (and methods of concrete classes) with the @Transactional annotation, as opposed to annotating interfaces.» Si no es una interfaz… si es una clase… la anotación va en la clase y en el método… puff.

¿Porque no usarlo en interfaces?. Porque si utilizas <tx:annotation-driven mode=»aspectj»/> ó <tx:annotation-driven proxy-target-class=»true»/>. Spring no va a poder hacer su magia.

Cuidadin con poner @Transactional y @Service juntas…

«The most derived location takes precedence when evaluating the transactional settings for a method». Es decir si tienes la anotacion a nivel de clase y de metodo, prevalece la del metodo. La configuracion que introduzcas en el metodo tienen prioridad sobre la configuracion de la anotacion en la clase. Ej: si en la clase, la transaccion es de solo lectura, y en el metodo de escritura, ese metodo podra escribir.

@Transactional(readOnly = true)
public class DefaultFooService implements FooService {

  public Foo getFoo(String fooName) {
    // do something
  }

  // these settings have precedence for this method
  @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
  public void updateFoo(Foo foo) {
    // do something
  }
}

Cuando usas proxy, la anotación solo es viable en métodos públicos. No se lanzara ningún error, pero el proxy no los podrá ver. Si por alguna razón, necesitas métodos de otro tipo, protected ó private, utiliza mejor aspectos.

Bien, esto ya ha terminado, ¿donde pone que es obligatorio que el método y la clase deban tener la anotacion @Transaction?. Si no lo haces así, no funciona.

@Transactional