#Transactional for any Exceptions in Spring - java

I am looking using #Transactional on one of the Service methods. However when an exception occurs, the transaction is not getting rolled back. I tried the same with #Transactional(rollbackFor=Exception.class). My code as follows:-
#Override
#Transactional(rollbackFor=Throwable.class)
public boolean addUser(User user) throws Exception{
boolean userAdded = userDao.addUser(user);
boolean userRegistrationRecorded = userDao.recordUserRegistraionDetails(user);
return true;
}
I read lot of posts and every one says that Spring handles only RuntimeExceptions and not checked Exceptions other than RmiException. I need a solution that works for any kind of Exception. Some one suggested me to write own annotation, where as others suggested of having a TransactionManager as part of applicationContext.xml file. A detailed solution will definitely help me.
By the way I am using Spring JdbcTemplate. The strange thing I observe is though the Exceptions raised by Spring are RuntimeExceptions the transaction is not getting rolled back. I am trying to raise an Exception by adding the same User in the above scenario.
My applicationContext.xml is as follows:-
<context:component-scan base-package="org.chaperone.services.security.*" />
<context:annotation-config />
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="searchSystemEnvironment" value="true" />
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="${DATABASE_URL}" />
<property name="username" value="${DATABASE_USER_NAME}" />
<property name="password" value="${DATABASE_PASSWORD}" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>

The ease-of-use afforded by the use of the #Transactional annotation is best illustrated in this link
you have to add :
<tx:annotation-driven transaction-manager="transactionManager" />

Related

Want to know more about property "globalRollbackOnParticipationFailure" of spring transaction management

<bean id="transactionManager"
class="org.springframework.jdbc.datasource.SpringTransactionManager">
<property name="dataSource" ref="dataSource" />
<property name="globalRollbackOnParticipationFailure" value="false" />
</bean>
Can anyone help me to figure out that how "globalRollbackOnParticipationFailure" property works in different scenarios??

Spring #Transactional - Rollback is not happening

I am new to spring and working on a sample program using Spring jdbc. this is to check how spring #Trsactional working and rolling back the changes to the Db if there is an exception.
But I am not able to achieve this. Through I am raising an exception in one of the DB update, still it's inserting the data to DB and not rolling back for that batch. For ex. after inserting 5000 I am raising an exception, so ideally it should rollback all the changes(for the current batch) to all the tables and total rows in Db should be 4000.
I know somewhere I am making mistake but not able to figure it out. Not sure if this is a correct approach.
I tried all possible ways available in the Internet, but still no luck. please help me to resolve this issue.
Here is my sample application https://github.com/rajarshp/JavaSample
Code Snippet
#Transactional(rollbackFor={Exception.class})
public void executeDB(int count) throws Exception
{
CreateAccount newacc = new CreateAccount(jdbcTemplate);
CreateUser newusr = new CreateUser(jdbcTemplate);
//BalanceUpdate newbal = new BalanceUpdate(jdbcTemplate);
newacc.addList(acclist);
newusr.addToList(usrlist);
//newbal.addList(ballist);
newusr.execute(); // insert data to db
newacc.addAccount(); // insert data to db
//newbal.addBalance(); // insert data to db
newacc.getAccList().clear();
newusr.getUserList().clear();
//newbal.getBalanceList().clear();
if(count == 5000)
{
Thread.sleep(1000);
throw new Exception("Rollback");
}
count += 1000;
//throw new Exception();
}
XML:
<context:component-scan base-package="com.example"></context:component-scan>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#localhost:1521:xe" />
<property name="username" value="system" />
<property name="password" value="root" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="startit" class="com.example.springtransaction.GlobalClass">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="dbupdate" class="com.example.springtransaction.DbUpdate">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
Create bean for all Db operation classes like createUser or Cretae account in the xml file. remove the initialization on these classes from db operation class and use setter method to inject it from the xml. Post that call your db operation method. It should work.
<bean id="newaccount" class="com.example.springtransaction.CreateAccount">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="newuser" class="com.example.springtransaction.CreateUser">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="dbupdate" class="com.example.springtransaction.DbUpdate">
<property name="newaccount" ref="newacc"></property>
<property name="newuser" ref="newusr"></property>
</bean>

Mybatis with Spring NullPointerException Autowired field

I'm trying to integrate Mybatis with spring. Here you can see my application context of Spring
<context:annotation-config />
<context:component-scan base-package="com" />
<tx:annotation-driven />
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/DB" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:/com/mybatis/mybatis-config.xml" />
<!--<property name="transactionFactory" ref="springManagedTransactionFactory" />-->
</bean>
<!--
<bean id="springManagedTransactionFactory" class="org.mybatis.spring.transaction.SpringManagedTransactionFactory">
<constructor-arg index="0" ref="dataSource" />
</bean> -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="registroClimaMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.mybatis.dao.RegistroClimaMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
First of all I have commented springManagedTransactionFactory in XML because it's giving me an exception
Error creating bean with name 'springManagedTransactionFactory' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)
Here is my interface RegistroClimaMapper. I do not have any annotation here because I have defined the context:component scan.
public interface RegistroClimaMapper {
void insertarRegistroClima(RegistroClima registro) throws SQLException;
List<RegistroClima> getRegistrosClima() throws SQLException;
List<RegistroClima> getRegistrosClima(#Param("Validado") boolean Validado) throws SQLException;
}
I try to use this interface in a ManagedBean and when I'm going to use registroClimaPersistence inside a method of TablaRegistroClimaBean I get a NullPointerException
#ManagedBean(name = "tablaRegClimaBean")
#ViewScoped
public class TablaRegistroClimaBean implements Serializable {
#Autowired
private RegistroClimaMapper registroClimaPersistence;
public void setRegistroClimaPersistence(RegistroClimaMapper registroClimaPersistence) {
this.registroClimaPersistence = registroClimaPersistence;
}
}
As seen here: http://javadox.com/org.mybatis/mybatis-spring/1.1.1/org/mybatis/spring/transaction/SpringManagedTransactionFactory.html#SpringManagedTransactionFactory%28%29, SpringManagedTransactionFactory has a default constructor, but none that takes a datasource. You need to pass the datasource to the newTransaction() method, but not to the constructor: http://javadox.com/org.mybatis/mybatis-spring/1.1.1/org/mybatis/spring/transaction/SpringManagedTransactionFactory.html#newTransaction(javax.sql.DataSource,%20org.apache.ibatis.session.TransactionIsolationLevel,%20boolean)
Change the piece of code to
<bean id="springManagedTransactionFactory" class="org.mybatis.spring.transaction.SpringManagedTransactionFactory">
<!--<constructor-arg index="0" ref="dataSource" />-->
</bean>
or completely remove the constructor-arg.
What NullPointerException do you get? It might come from you just defining the RegistroClimaMapper interface, but not implementing this interface in any bean. Please add a Bean that implements this interface and the Autowired annotation should work.

SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization

I've got an app running on Tomcat 7, using Spring, Mybatis, and .. Mybatis-spring.
Here's the setup for the DB and transactions in servlet-context.xml:
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/MyDS" />
<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath*:maps/*.xml" />
<property name="transactionFactory">
<bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" />
</property>
</bean>
<mybatis:scan base-package="com.domain.dao.mappers" />
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
The error I'm getting when I run a method that uses the SqlSession is the following:
org.springframework.dao.TransientDataAccessResourceException: SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization
at org.mybatis.spring.SqlSessionUtils.getSqlSession(SqlSessionUtils.java:136)
What am I doing wrong?
My goal, really, is just to use transactions with this setup. I don't think I necessarilly need JTA. But if that's easy enough to setup on Tomcat, I'm willing to take a swing at it.
And I solved the problem myself. Really simple solution. In case anyone runs into the same issue, all I needed to do is remove the following from the sqlSessionFactory bean:
<property name="transactionFactory">
<bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" />
</property>
I must have somehow entered it thinking I needed it, but apparently it's only needed if you're not using CMT (Container Managed Transactions).
You can solve this by changing the transaction factory to
<property name="transactionFactory">
<bean class="org.mybatis.spring.transaction.SpringManagedTransactionFactory" />
</property>

Spring global transaction committed after getting an element

I am using Spring and Hibernate with Jta Transactions, I have 2 databases, and I have a problem in a transactional method.
In this method I insert a lot of objects but I throws an exception to rollback the insertions, here the code works as I expected because the objects dont appear into the database.
But if I add a line in the method that get the objects of the same table, the objects are committed into the database.
I think that when I make a SELECT the objects are auto-committed, because the exception its thrown again and the objects persists into the database.
My xml and code:
dao.xml
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:configuracion_dao.properties" />
</bean>
<bean name="productosDAO" class="practica1.hibernate.HibernateProductosDAOImpl"
parent="abstractPracticaBean">
<property name="sessionFactory" ref="hibernateSessionFactory" />
</bean>
<bean name="tercerosDAO" class="${tercerosDAO.classname}" parent="abstractPracticaBean">
<property name="dataSource" ref="dataSourceDatos" />
</bean>
<bean name="auditoriaDAO" class="practica1.hibernate.HibernateAuditoriaDAOImpl" parent="abstractPracticaBean">
<property name="sessionFactory" ref="hibernateSessionFactory2" />
</bean>
<bean id="hibernateSessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSourceDatos" />
<property name="mappingResources">
<list>
<value>hibernate-mappings.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.HSQLDialect
</value>
</property>
</bean>
<bean id="hibernateSessionFactory2"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSourceAuditoria" />
<property name="mappingResources">
<list>
<value>hibernate-mappings-auditoria.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.HSQLDialect
</value>
</property>
</bean>
<bean name="dataSourceDatos" class="org.enhydra.jdbc.standard.StandardXADataSource">
<property name="driverName" value="org.apache.derby.jdbc.EmbeddedDriver" />
<property name="url" value="jdbc:derby:/tmp/datos.db;create=true" />
<property name="transactionManager" value="#{txManager.transactionManager}" />
</bean>
<jdbc:initialize-database data-source="dataSourceDatos"
ignore-failures="ALL">
<jdbc:script location="classpath:practica1/sql/creacion_derby.sql" />
<jdbc:script location="classpath:practica1/sql/datos.sql" />
</jdbc:initialize-database>
<bean name="dataSourceAuditoria" class="org.enhydra.jdbc.standard.StandardXADataSource">
<property name="driverName" value="org.apache.derby.jdbc.EmbeddedDriver" />
<property name="url" value="jdbc:derby:/tmp/auditoria.db;create=true" />
<property name="transactionManager" value="#{txManager.transactionManager}" />
</bean>
<jdbc:initialize-database data-source="dataSourceAuditoria"
ignore-failures="ALL">
<jdbc:script location="classpath:practica1/sql/creacion_auditoria_derby.sql" />
</jdbc:initialize-database>
<bean id="txManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" value="#{jotm.transactionManager}" />
<property name="userTransaction" value="#{jotm.userTransaction}" />
</bean>
<bean id="jotm" class="org.objectweb.jotm.Jotm" destroy-method="stop">
<constructor-arg value="true" />
<constructor-arg value="false" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
bo.xml
<bean name="tercerosBO" class="practica1.impl.TercerosBOImpl"
parent="abstractPracticaBean" autowire="constructor">
</bean>
<bean name="productosBO" class="practica1.impl.ProductosBOImpl"
parent="abstractPracticaBean">
<property name="productosDAO" ref="productosDAO" />
<property name="auditoriaDAO" ref="auditoriaDAO" />
</bean>
aplicacion.xml
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames" value="mensajes" />
</bean>
<bean id="abstractPracticaBean" class="practica1.impl.AbstractPracticaBean" abstract="true">
<property name="messageSource" ref="messageSource"></property>
</bean>
<import resource="bo.xml" />
<import resource="dao.xml" />
Transactional method:
#Transactional
#Override
public void actualizaPrecio(double porcentaje) {
internalActualizaPrecio(porcentaje);
}
private void internalActualizaPrecio(double porcentaje) {
auditoriaDAO.insertAuditoria(getMessageSource().getMessage(
"mensaje.actualizar_productos", new Object[] { porcentaje },
null));
int i = 0;
auditoriaDAO.getAuditorias(); // Without this line its works like I expected
List<Producto> productos = productosDAO.getProductos();
for (Producto producto : productos) {
i++;
if (i > 3)
throw new UnsupportedOperationException(
"Error para que veamos las transacciones");
producto.setPrecio(producto.getPrecio().multiply(
new BigDecimal(porcentaje).divide(new BigDecimal(100))));
productosDAO.updateProducto(producto);
}
}
I realised that if I use auditoriaDAO.getAuditorias() the rollback only affects to Producto but if I use productoDAO.getProductos() the rollback only affects to Auditoria...
You may be mixing up flush and commit here: a SELECT statement usually flushes all previous SQL statements, in order to fetch up-to-date data (regarding the previous changes you made in the tx). It may be possible that before such a SELECT statement is done (the following DAO calls are made to the 2nd sessionFactory if I'm not mistaken), the exception exits the method without a flush. Hence no modification in database.
So the question is: are you sure you're rollbacking the tx effectively? I see you've annotated a private method: the proxy-based mechanism of Spring AOP don't handle that! You must annotate a public method and call it from outside the annotated method's class, due to this very proxy-based mechanism. See the "Method visibility and #Transactional" block in the documentation.
Another lead: you have 2 sessionFactories, so I assume you're using XA transactions/datasources: are you sure this part of the conf is OK?
Please check auditoriaDAO and productosDAO and search for other transactional annotation. I think a new transaction is created somewhere and the UnsupportedException rollbacks only the last transaction, and the parent transaction is committed. Hope I helped!
I have found two example. Please check it.
JOTM transactions in spring and hibernate
Access Multiple Database Using Spring 3, Hibernate 3 and Atomikos

Categories

Resources