Anmerkung @Transactional. Wie kann ich ein Rollback durchführen?

90

Ich habe diese Anmerkung erfolgreich für eine Dao-Klasse verwendet. Und Rollback funktioniert für Tests.

Aber jetzt muss ich echten Code zurücksetzen, nicht nur Tests. Es gibt spezielle Anmerkungen zur Verwendung in Tests. Aber welche Anmerkungen gelten für Nicht-Testcode? Das ist eine große Frage für mich. Dafür habe ich schon einen Tag verbracht. Die offizielle Dokumentation entsprach nicht meinen Bedürfnissen.

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 ist

@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);
    }
}

Und hier ist ein Test, für den die Anmerkungen gut funktionieren:

@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>
   ...



** JA, ich habe die Transaktion zurückgesetzt. Ich habe gerade BEAN für den Service hinzugefügt ... und dann beginnt die Annotation @Transactional zu funktionieren :-) **

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

Vielen Dank, Russland wird dich nicht vergessen!

Moskauer Junge
quelle

Antworten:

159

Wirf einfach eine RuntimeExceptionvon einer Methode, die als markiert ist @Transactional.

Standardmäßig alle RuntimeExceptions Rollback-Transaktionen, während aktivierte Ausnahmen dies nicht tun. Dies ist ein EJB-Erbe. Sie können dies mithilfe von rollbackFor()und noRollbackFor()Annotationsparametern konfigurieren :

@Transactional(rollbackFor=Exception.class)

Dadurch wird die Transaktion zurückgesetzt, nachdem eine Ausnahme ausgelöst wurde .

Tomasz Nurkiewicz
quelle
1
Ich versuchte es. Es funktioniert nicht! Ich erzähle von Rollback-Operationen nicht direkt im Dao-Objekt, das könnte funktionieren. Ich erzähle von einem anderen Objekt, das eine Aufrufsequenz der Dao-Methode verwendet, die @Transactional verwendet. Und ich versuche, die gleiche Anmerkung für meine Klasse hinzuzufügen, die dieses Dao nennt. Aber hier funktioniert es nicht.
Moscow Boy
5
Das funktioniert wirklich so :-). Wenn Sie einen Dienst haben, der mehrere DAOs anruft, muss der Dienst auch eine @TransactionalAnmerkung enthalten. Andernfalls wird jeder DAO-Aufruf gestartet und eine neue Transaktion festgeschrieben, bevor Sie eine Ausnahme im Dienst auslösen. Die Quintessenz lautet: Die Ausnahme muss eine als @Transactional.
Tomasz Nurkiewicz
Schauen Sie sich den hinzugefügten Code im ersten Beitrag an. Es gibt einen Teil des Codes, den ich habe. Und nach der Ausführung habe ich den Datensatz in der DB. => Rollback funktioniert noch nicht.
Moscow Boy
Wird durch das Auslösen einer Ausnahme von DAO die Transaktion nicht ebenfalls zurückgesetzt? Haben Sie org.springframework.orm.hibernate3.HibernateTransactionManagerin Ihrem Spring-Kontext konfiguriert? Wenn Sie den org.springframework.transactionLogger aktivieren , zeigt er etwas Interessantes an?
Tomasz Nurkiewicz
1
Wenn Sie Spring Boot verwenden und die DataSource automatisch konfigurieren lassen, wird HikariDataSource verwendet, für die das automatische Festschreiben standardmäßig auf true festgelegt ist. Sie müssen es auf false setzen: hikariDataSource.setAutoCommit (false); Ich habe diese Änderung in der @ Configuration-Klasse vorgenommen, als ich die DataSourceTransactionManager-Bean konfiguriert habe.
Alex
82

oder programmatisch

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
Stefan K.
quelle
12
Ja. Denn manchmal müssen Sie ein Rollback durchführen, ohne einen Fehler auszulösen.
Erica Kane
2
Du bist ein Lebensretter.
Wände
Dies ist eine geeignetere Methode, um ein Rollback-Imo aufzurufen. Idealerweise möchten Sie keine Ausnahmen für bekannte oder kontrollierte Bedingungen auslösen.
Jalpino
2
Manchmal funktioniert es nicht. In meinem Fall habe ich beispielsweise einen Transaktionsdienst und ein nicht-transaktionales Repository. Ich rufe das Repository vom Dienst aus auf, daher sollte es an einer vom Dienst geöffneten Transaktion teilnehmen. Wenn ich Ihren Code jedoch aus meinem nicht-transaktionalen Repository aufrufe, wird NoTransactionException ausgelöst, obwohl eine Transaktion vorhanden ist.
Victor Dombrovsky
3
Welche Transaktion möchten Sie in Ihrem "nicht-transaktionalen Repository" zurücksetzen?
Stefan K.
5

Sie können eine ungeprüfte Ausnahme von der Methode auslösen, die Sie zurücksetzen möchten. Dies wird bis zum Frühjahr erkannt und Ihre Transaktion wird nur als Rollback markiert.

Ich gehe davon aus, dass Sie Spring hier verwenden. Und ich gehe davon aus, dass die Anmerkungen, auf die Sie sich in Ihren Tests beziehen, auf Federtests basierende Anmerkungen sind.

Die empfohlene Methode, um der Transaktionsinfrastruktur des Spring Framework anzuzeigen, dass die Arbeit einer Transaktion zurückgesetzt werden soll, besteht darin, eine Ausnahme von Code auszulösen, der derzeit im Kontext einer Transaktion ausgeführt wird.

und beachten Sie, dass:

Bitte beachten Sie, dass der Transaktionsinfrastrukturcode des Spring Framework standardmäßig nur eine Transaktion für das Rollback markiert, wenn zur Laufzeit ungeprüfte Ausnahmen vorliegen. Das heißt, wenn die ausgelöste Ausnahme eine Instanz oder Unterklasse von RuntimeException ist.

Alex Barnes
quelle
1

Für mich war rollbackFor nicht genug, also musste ich das setzen und es funktioniert wie erwartet:

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

Ich hoffe, es hilft :-)

Roberto Rodriguez
quelle
1
nein , es hilft nicht bei allen TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();unter Ihnen geholfen
Enerccio