Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring managed transactions without @Transactional annotation

I'm using Spring annotations to manage my transactions like so:

@Transactional(readOnly = true)
public class AlertServiceImpl implements AlertService {

     private AlertDAO alertDAO;

     public List<Alert> getAlerts(){
         List<Alert> alerts = alertDAO.getAlerts();
         return alerts;
     }

}

I'm wondering what happens if I forget the annotation:

// Oops! Forgot to use transactional annotation 
public class AlertServiceImpl implements AlertService {

    private AlertDAO alertDAO;

    public List<Alert> getAlerts(){
         List<Alert> alerts = alertDAO.getAlerts();
         return alerts;
    }

}

When the alertDAO implementation is following:

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

// no annotation here either
public class HibernateAlertDAO extends HibernateDaoSupport implements AlertDAO {

    public List<Alert> getAlerts(){
         // some implementation details that define queryString

         Query query = getSession().createQuery(queryString);
         List<Alert> alerts = query.list();

         return alerts;
    }

}

It seems like Hibernate allows me to fetch data from database even without the annotation.

What are the consequences of this kind of carelessness and what are the worst case scenarios that could happen?

like image 499
Jukka Hämäläinen Avatar asked Jan 23 '14 15:01

Jukka Hämäläinen


People also ask

Is @transactional mandatory?

@Transactional(MANDATORY) : fails if no transaction was started ; works within the existing transaction otherwise. @Transactional(SUPPORTS) : if a transaction was started, joins it ; otherwise works with no transaction.

What happens if we don't use @transactional?

Hard to use the database to troubleshoot test failures Another problem with using @Transactional is that you can't use the database to investigate the failure reasons, as the data is never actually saved into the database because transactions are always rollback'd at the end of the test method execution.

Is it sufficient to annotate the classes with the @transactional annotation?

It is not sufficient to tell you simply to annotate your classes with the @Transactional annotation, add the line ( <tx:annotation-driven/> ) to your configuration, and then expect you to understand how it all works.

When should we use @transactional annotation?

The @Transactional annotation is the metadata that specifies the semantics of the transactions on a method. We have two ways to rollback a transaction: declarative and programmatic. In the declarative approach, we annotate the methods with the @Transactional annotation.


2 Answers

According to the documentation (Spring docs) it's just metadata to give an indication that the method or interface can be configured by something that is 'transactionally aware' (i.e. <tx:annotation-driven/>).

With just tx:annotation-driven and no @Transactional attribute I believe you get the "default" transactionality applied:

  • Propagation setting is REQUIRED.
  • Isolation level is DEFAULT.
  • Transaction is read/write.
  • Transaction timeout defaults to the default timeout of the underlying transaction system, or none if timeouts are not supported.
  • any RuntimeException triggers rollback, and any checked Exception does not.

Assuming you're using the <tx:annotation-driven /> to drive it via a transaction manager then missing out the @Transactional attribute means you can't apply such properties as readOnly, isolation, propagation, rollbackFor, noRollbackFor etc.

I believe that MVC is slightly different - the Hibernate session is tied directly to the MVC request - i.e. when the request is received the transaction starts.

Back to your example, the code for getSession() in HibernateDAOSupport is as follows:

protected final Session getSession()     throws DataAccessResourceFailureException, IllegalStateException  {     return getSession(this.hibernateTemplate.isAllowCreate()); } 

Which in turn calls to:

/**  * Obtain a Hibernate Session, either from the current transaction or  * a new one. The latter is only allowed if "allowCreate" is true.  *.......  */ protected final Session getSession()     throws DataAccessResourceFailureException, IllegalStateException {     return getSession(this.hibernateTemplate.isAllowCreate()); } 

which ultimately calls to :

/**   * ....  * @param allowCreate whether a non-transactional Session should be created  * when no transactional Session can be found for the current thread  * ....  */ private static Session doGetSession(     SessionFactory sessionFactory, Interceptor entityInterceptor, SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate) 

Fundamentally, a Transaction:Session is tied 1:1 AFAIK, and the only way to run without a transaction is by using say JBoss which has a 'baked in' persistence layer which provides the transactionality for you (under the covers). Even if you call getQuery() after getSession() you still effectively have a transaction occurring as it's a JDBC/Hibernate connection.

like image 107
Al Sweetman Avatar answered Sep 22 '22 06:09

Al Sweetman


In your scenario your DAO will execute without transaction, most probably with auto-commit.

If you want to avoid this kind of mistake in future and require all your services to run in transaction you can protect DAO layer with following @Transactional annotation:

@Transactional(propagation = MANDATORY) public class HibernateAlertDAO extends HibernateDaoSupport implements AlertDAO {    ... } 

This annotation will require service layer to declare transaction and will throw exception otherwise.

like image 22
hoaz Avatar answered Sep 23 '22 06:09

hoaz