TS-
TS-

Reputation: 4411

Spring @Transaction does not rollback on Exception thrown

I've searched around for this question, there's quite a few of them here on StackOverflow and Google but I can't seem to get anything working for me.

here are my codes Spring config: (I dont use any pointcut - I think I dont need to?)

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
...
</bean>

<bean id="hibernateSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
 <property name="dataSource" ref="dataSource" />
 ...
</bean>

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="hibernateSessionFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>

I have a Service class:

@Service
public class ServiceImpl implements ServiceInterface 
{
    /**
     * Injected session factory
     */
    @Autowired(required=true)
    private SessionFactory sessionFactory;

    @Autowired(required=true)
    private Dao myDao;

    /**
     * {@inheritDoc}
     */
    @Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRED)
    public void scheduleBlast(BlastParameters blastParameters) throws ServiceException 
    {
        ... do bunch of stuff ..
        myDao.persist(entity)

        if(true)
            throw new ServiceException("random error")
    }

    .. setter methods and other stuff ..
}

and a Dao class:

public class DaoImpl implements DaoInterface
{
    @Autowired(required=true)
    private SessionFactory sessionFactory

    /**
     * {@inheritDoc}
     */
    @Transactional(propagation=Propagation.MANDATORY)
    public void persist(Entity e) throws DaoException
    {
        try
        {
            sessionFactory.getCurrentSession().persist(e);
        }
        catch(Exception ex)
        {
            throw new DaoException(ex);
        }
    }


    .. setter methods and other stuff ..
}

Some unnecessary details are eliminated (eg. missing setter, etc), assume that code works perfectly fine.

My problem with the above is that when I added the throw random exception line, it does not rollback, the object being persisted through the DAO stays in the db.

I am using Spring 3.1 and Hibernate 3.6 (because there was a bug with Hibernate 4.0 on Spring 3.1)

Thoughts?

Thank you

Upvotes: 7

Views: 40516

Answers (5)

Yauheni Leaniuk
Yauheni Leaniuk

Reputation: 456

I have the same issue when open connections manually in my Spring Repository. After I start using JdbcTemplate error disappeared

I used

@Autowired 
private JdbcTemplate jdbc;

in my Repository

and then jdbc.query(sql, new RowMapper()); or jdbc.update(sql); or jdbc.queryForObject(sql, new RowMapper()); dependent on how many objects you return by your query.

Also I defined RowMapper():

public class RowMapper implements RowMapper<YourDTOObject> {
    @Override
    public YourDTOObject mapRow(ResultSet resultSet, int i) throws SQLException {
        YourDTOObject dto = new YourDTOObject();
        dto.setId(resultSet.getInt("id")); // here you can change to your column names
        dto.setName(resultSet.getString("name"));
        dto.setAmount(resultSet.getBigDecimal("amount"));
        return a;
    }

Upvotes: 0

TS-
TS-

Reputation: 4411

I found the cause of my problem and why the transaction (seemingly) not managed properly.

Somewhere in my code

/**
 * {@inheritDoc}
 */
@Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRED)
public void doWork(Parameters param) throws ServiceException 
{
    ... do bunch of stuff ..
    myDao1.persist(entity)

    -- Some logic here --

    ... do bunch of stuff ..
    myDao2.persist(entity2)

    if(true)
        throw new ServiceException("random error")
}

The part where it says "-- Some logic here --", there was some logic done that uses raw SQL and call on execute update:

Query query = sessionFactory.getCurrentSession().createSQLQuery(queryText);
query.executeUpdate();

And because it's not using Hibernate query, and instead using raw SQL execution, it caused a flush to be called and thus any work done prior to the call will be committed along with it.

I re-work the flow of logic to account for this logic to make sure transaction is managed properly. While using raw SQL might be an indication that there's something wrong - it was something necessary to be done due to the things that the service try to accomplish and to improve the performance of the service.

Upvotes: 13

Ravi Sanwal
Ravi Sanwal

Reputation: 634

That is the intended behavior of transaction management. The default behavior for @Transactional is to rollback only for runtime exceptions. If you want your stuff to rollback after throwing DaoException then add it to the rollback exception list. Don't forget to also include RuntimeException also. Try the following on the Dao class @Transactional(propagation=Propagation.Mandatory, rollbackFor={RuntimeException.class, DaoException.class})

Upvotes: 16

Michael Wiles
Michael Wiles

Reputation: 21186

Try remove the @Transactional annotation from the DaoImpl class. I suspect what might be happening is that the transaction is being committed when it crosses back over that transaction boundary (DaoImpl). I've had mixed success with this setup. You can try some different transaction approaches to the "inner" transaction.

The other thing you can do is turn on spring transaction logging. It think its category org.springframework.transaction or something. That way you will see exactly what it is doing w.r.t to roll back and commit of transactions...

Upvotes: 1

gpeche
gpeche

Reputation: 22504

you don't have one of those JDBC drivers that are in AUTOCOMMIT mode by default, do you?

Upvotes: 0

Related Questions