112 votes

Annotation @Transactional. Comment revenir en arrière ?

J'ai utilisé cette annotation avec succès pour une classe Dao. Et le rollback fonctionne pour les tests.

Mais maintenant, j'ai besoin de revenir sur du vrai code, pas seulement sur des tests. Il existe des annotations spéciales à utiliser dans les tests. Mais quelles annotations sont destinées au code non testé ? C'est une grande question pour moi. J'y ai déjà consacré une journée. La documentation officielle ne répondait pas à mes besoins.

class MyClass { // this does not make rollback! And record appears in DB.
        EmployeeDaoInterface employeeDao;

        public MyClass() {
            ApplicationContext context = new ClassPathXmlApplicationContext(
                    new String[] { "HibernateDaoBeans.xml" });
            employeeDao = (IEmployeeDao) context.getBean("employeeDao");
         }

        @Transactional(rollbackFor={Exception.class})
    public void doInsert( Employee newEmp ) throws Exception {
        employeeDao.insertEmployee(newEmp);
        throw new RuntimeException();
    }
}

employeeDao est

@Transactional
public class EmployeeDao implements IEmployeeDao {
    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public void insertEmployee(Employee emp) {
        sessionFactory.getCurrentSession().save(emp);
    }
}

Et voici un test pour lequel les annotations fonctionnent bien :

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/HibernateDaoBeans.xml" })
@TransactionConfiguration(transactionManager = "txManager", defaultRollback = true)
@Transactional
public class EmployeeDaoTest {

    @Autowired
    EmployeeDaoInterface empDao;

    @Test
    public void insert_record() {
       ...
       assertTrue(empDao.insertEmployee(newEmp));
    }

HibernateDaoBeans.xml

   ...
<bean id="employeeDao" class="Hibernate.EmployeeDao">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
    <tx:annotation-driven transaction-manager="txManager"/>

<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
   ...



**Oui, j'ai annulé la transaction. J'ai juste ajouté BEAN pour le service... et alors l'annotation @Transactional commence à fonctionner :-) **

<bean id="service" class="main.MyClass">
    <property name="employeeDao" ref="employeeDao" />
</bean>

Merci à tous, la Russie ne vous oubliera pas !

184voto

Tomasz Nurkiewicz Points 140462

Il suffit de jeter n'importe quel RuntimeException à partir d'une méthode marquée comme @Transactional .

Par défaut, tous les RuntimeException alors que les exceptions vérifiées ne le sont pas. Il s'agit d'un héritage des EJB. Vous pouvez configurer ceci en utilisant rollbackFor() y noRollbackFor() paramètres d'annotation :

@Transactional(rollbackFor=Exception.class)

Cette opération annulera la transaction après avoir lancé cualquier exception.

112voto

Stefan K. Points 1264

Ou par programme

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

5voto

Alex Points 4759

Vous pouvez lancer une exception non contrôlée à partir de la méthode que vous souhaitez annuler. Cette exception sera détectée par Spring et votre transaction sera marquée comme étant uniquement de type rollback.

Je suppose que vous utilisez Spring ici. Et je suppose que les annotations auxquelles vous faites référence dans vos tests sont les annotations basées sur les tests Spring.

La manière recommandée d'indiquer à l'infrastructure de transaction de Spring Framework que le travail d'une transaction doit être annulé est de lancer une exception à partir du code qui s'exécute actuellement dans le contexte d'une transaction.

et de noter que :

veuillez noter que le code de l'infrastructure de transaction du Spring Framework ne marquera, par défaut, une transaction pour un retour en arrière que dans le cas d'exceptions d'exécution non vérifiées, c'est-à-dire lorsque l'exception lancée est une instance ou une sous-classe de RuntimeException.

2voto

Roberto Rodriguez Points 1119

Pour moi, rollbackFor n'était pas suffisant, j'ai donc dû mettre ceci et cela fonctionne comme prévu :

@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)

J'espère que cela vous aidera :-)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X