Transaktionen sind ein wesentlicher Bestandteil jeder Anwendung, die
mit einer Datenbank arbeitet, insbesondere in RESTful
Webservices. Eine Transaktion stellt sicher, dass alle
Operationen innerhalb einer Datenbanktransaktion entweder vollständig
abgeschlossen oder vollständig zurückgesetzt werden, falls ein Fehler
auftritt. In Spring Boot können Transaktionen automatisch mit der
Annotation @Transactional verwaltet
werden.
In diesem Kapitel erläutern wir die Funktionsweise von Transaktionen
in Spring Boot, die Verwendung der
@Transactional-Annotation und wie man mit gängigen
Problemen wie Rollback und Isolation
Levels umgeht.
Eine Transaktion ist eine Sequenz von Operationen, die als eine Einheit von Arbeit ausgeführt werden. Eine Transaktion hat die folgenden grundlegenden Eigenschaften, die oft als ACID-Prinzipien bezeichnet werden:
In Spring Boot werden Transaktionen durch das Spring Framework verwaltet, das sich um die Integration mit der darunter liegenden Datenbank kümmert.
@TransactionalIn Spring Boot ist die häufigste Möglichkeit, Transaktionen zu
verwalten, die Verwendung der Annotation @Transactional.
Diese Annotation kann auf Methoden oder Klassen angewendet werden und
sorgt dafür, dass alle Datenbankoperationen innerhalb einer Transaktion
ausgeführt werden.
@Transactional auf einer Service-Methodeimport org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ProductService {
private final ProductRepository productRepository;
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
@Transactional
public Product createProduct(Product product) {
// Speichert das Produkt in der Datenbank
return productRepository.save(product);
}
}In diesem Beispiel sorgt die Annotation @Transactional
dafür, dass die Methode createProduct() innerhalb einer
Transaktion ausgeführt wird. Sollte während der Ausführung ein Fehler
auftreten, wird die Transaktion zurückgerollt und die Änderungen an der
Datenbank werden verworfen.
Spring Boot führt automatisch ein Rollback durch,
wenn eine unchecked Exception (z.B.
RuntimeException) innerhalb einer
@Transactional-Methode auftritt. Das bedeutet, dass alle
Änderungen, die in der Transaktion gemacht wurden, rückgängig gemacht
werden, wenn ein Fehler auftritt.
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ProductService {
private final ProductRepository productRepository;
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
@Transactional
public Product createProduct(Product product) {
productRepository.save(product);
if (product.getPrice() < 0) {
throw new IllegalArgumentException("Der Preis darf nicht negativ sein.");
}
return product;
}
}In diesem Beispiel wird das Produkt nur dann gespeichert, wenn der
Preis positiv ist. Wenn der Preis negativ ist, wird eine
IllegalArgumentException geworfen und die Transaktion wird
zurückgerollt.
Standardmäßig führt Spring Boot kein Rollback durch, wenn eine
checked Exception (z.B. IOException)
geworfen wird. Sie können jedoch explizit festlegen, dass auch bei
checked Exceptions ein Rollback durchgeführt wird, indem Sie den
Parameter rollbackFor in der
@Transactional-Annotation verwenden.
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
@Service
public class ProductService {
@Transactional(rollbackFor = IOException.class)
public Product createProduct(Product product) throws IOException {
productRepository.save(product);
if (product.getPrice() < 0) {
throw new IOException("Ungültiger Preis");
}
return product;
}
}In diesem Beispiel wird ein Rollback auch dann durchgeführt, wenn
eine IOException auftritt.
Spring Boot bietet verschiedene Propagationsstufen für Transaktionen. Diese steuern, wie Transaktionen zwischen Methoden weitergereicht werden, insbesondere wenn eine Methode mit einer Transaktion eine andere Methode aufruft, die ebenfalls in einer Transaktion läuft.
Propagation.REQUIRES_NEWimport org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void processOrder(Order order) {
// Neue Transaktion wird gestartet
orderRepository.save(order);
}
}In diesem Beispiel wird die Methode processOrder() immer
in einer neuen Transaktion ausgeführt, unabhängig davon, ob eine
bestehende Transaktion vorhanden ist.
Das Isolation Level einer Transaktion bestimmt, wie isoliert eine Transaktion von anderen parallelen Transaktionen ist. Spring Boot unterstützt die Standard-Isolationsebenen von SQL, die festlegen, wie Daten während paralleler Transaktionen konsistent bleiben.
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ProductService {
@Transactional(isolation = Isolation.SERIALIZABLE)
public void updateProductStock(Long productId, int quantity) {
// Stock aktualisieren mit hoher Isolation
Product product = productRepository.findById(productId).orElseThrow();
product.setStock(product.getStock() - quantity);
productRepository.save(product);
}
}In diesem Beispiel verwendet die Methode
updateProductStock() das
SERIALIZABLE-Isolation-Level, um sicherzustellen, dass die
Transaktion vollständig isoliert von anderen Transaktionen ist.
Sie können @Transactional nicht nur auf Methodenebene,
sondern auch auf Klassenebene verwenden. In diesem Fall gilt die
Transaktion für alle öffentlichen Methoden der Klasse.
@Transactional auf Klassenebeneimport org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class ProductService {
public Product createProduct(Product product) {
return productRepository.save(product);
}
public void deleteProduct(Long productId) {
productRepository.deleteById(productId);
}
}Alle öffentlichen Methoden in der ProductService-Klasse
laufen in einer Transaktion.
Transaktionen sind ein grundlegendes Element jeder
datenbankgestützten Anwendung, und Spring Boot macht es einfach,
Transaktionen durch die Verwendung der Annotation
@Transactional zu verwalten. Ob es darum geht, einfache
CRUD-Operationen sicherzustellen, Rollbacks bei Fehlern durchzuführen
oder mit fortgeschrittenen Themen wie Propagation und Isolation Levels
umzugehen, Spring Boot bietet eine robuste Unterstützung für die
Verwaltung von Transaktionen in RESTful Webservices.