Spring @Transaction não reverte na exceção lançada
Pergunta
Eu pesquisei por essa questão, há alguns deles aqui no StackOverflow e no Google, mas não consigo fazer nada funcionando para mim.
aqui estão meus códigos Configuração do Spring: (eu não uso nenhum pointcut - acho que não preciso?)
<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"/>
Eu tenho uma aula de serviço:
@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 ..
}
e uma classe Dao:
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 ..
}
Alguns detalhes desnecessários são eliminados (por exemplo, setter ausente, etc), assuma que o código funciona perfeitamente.
Meu problema com o acima é que quando adicionei a linha de exceção aleatória, ela não faz rollback, o objeto que está sendo persistido por meio do DAO permanece no banco de dados.
Estou usando Spring 3.1 e Hibernate 3.6 (porque havia um bug com Hibernate 4.0 no Spring 3.1)
Pensamentos?
Obrigado
Solução 2
Encontrei a causa do meu problema e por que a transação (aparentemente) não foi gerenciada corretamente.
Em algum lugar no meu código
/**
* {@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")
}
A parte onde diz "- Alguma lógica aqui -", foi feita alguma lógica que usa SQL bruto e chama na execução de atualização:
Query query = sessionFactory.getCurrentSession().createSQLQuery(queryText);
query.executeUpdate();
E como não está usando a consulta do Hibernate e, em vez disso, está usando a execução de SQL bruta, causou a chamada de um flush e, portanto, qualquer trabalho feito antes da chamada será confirmado junto com isto.
Eu refaço o fluxo da lógica para levar em conta essa lógica para garantir que a transação seja gerenciada corretamente.Embora o uso de SQL bruto possa ser uma indicação de que há algo errado - era algo necessário a ser feito devido às coisas que o serviço tenta realizar e para melhorar o desempenho do serviço.
Outras dicas
Esse é o comportamento pretendido do gerenciamento de transações. O comportamento padrão para @Transactional é reverter apenas para exceções de tempo de execução. Se você quiser que seu material seja revertido após lançar DaoException, adicione-o à lista de exceções de reversão.Não se esqueça de incluir também RuntimeException. Experimente o seguinte na classe Dao @Transactional (propagation= Propagation.Mandatory, rollbackFor= {RuntimeException.class, DaoException.class})
Tente remover a anotação @Transactional da classe DaoImpl.Eu suspeito que o que pode estar acontecendo é que a transação está sendo confirmada quando cruza de volta o limite da transação (DaoImpl).Tive sucesso misto com esta configuração.Você pode tentar algumas abordagens de transação diferentes para a transação "interna".
A outra coisa que você pode fazer é ativar o registro de transações do spring.Acho que sua categoria org.springframework.transaction ou algo assim.Dessa forma, você verá exatamente o que ele está fazendo para reverter e confirmar as transações ...
você não tem um daqueles drivers JDBC que estão no modo AUTOCOMMIT por padrão, tem?