This commit is contained in:
2026-06-11 19:31:22 +02:00
parent e7d80a1fbc
commit a4a4913958
24 changed files with 1636 additions and 0 deletions
@@ -0,0 +1,578 @@
# Componentware Transaktionen, Packaging, Architektur und Spring Boot
## Inhalt
**Teil 1: Transaktionen & Packaging**
- Transaktionen: Einführung, CMT, BMT, Client Managed
- Packaging
- Architektur
- Abschließendes Beispiel
**Teil 2: Spring Boot**
- Überblick über Spring
- Starter
- Anlegen eines Spring-Boot Projekts
- Spring Shell
- Komponenten, CDI
- Zugriff auf Datenbanken / Spring Data
- Messaging
- Spring Web MVC / RESTful Web Services
- Transaktionen
---
## Einführung
- Eine der wichtigsten Aufgaben des Entwicklers von Enterprise Systemen stellt die Aufrechterhaltung der Systemkonsistenz dar.
- Falls von einem Geschäftsprozess mehr als eine Aktion durchgeführt wird, muss sichergestellt sein, dass entweder alle Einzelschritte oder gar keiner der Schritte durchgeführt wird.
- Beispiel: Bestellung → Bestand reduzieren + Spediteur benachrichtigen. Beide Schritte müssen zusammen ablaufen.
- Das Jakarta Transactions API (JTA) stellt diverse Möglichkeiten der Transaktionsverwaltung bereit.
### Ablauf einer Transaktion
1. Vor der ersten Ausführung wird eine Transaktion eröffnet.
2. Der Zustand vor dem Start wird zwischengespeichert.
3. Falls ein Fehler auftritt, markiert der Server die Transaktion als fehlerhaft.
4. Falls alle Aktionen fehlerfrei durchlaufen → **commit** (zwischengespeicherte Zustände werden gelöscht).
5. Falls die Transaktion als ungültig markiert wurde → **rollback** (Zustand vor der Transaktion wird wiederhergestellt).
Der Transaktionskontext wird entlang der Aufrufkette propagiert (bei Lookups, Injections oder Proxy-Objekten).
### ACID-Prinzip
| Eigenschaft | Bedeutung |
|-------------|-----------|
| **Atomicity** | Alle Teilschritte oder keiner wird ausgeführt. |
| **Consistency** | Nach der Ausführung ist der Datenbestand konsistent. |
| **Isolation** | Mehrere Transaktionen beeinflussen sich nicht gegenseitig. |
| **Durability** | Ergebnisse sind dauerhaft (überstehen z.B. Server-Neustart). |
### 2-Phase-Commit
Bei verteilten Systemen erfolgt die Transaktion per 2-Phase-Commit:
1. **Phase 1**: Transaktionskoordinator veranlasst Ausführung bei allen beteiligten Systemen.
2. **Phase 2**: Bei Erfolg → finales Commit; bei Fehler → Rollback.
### Transaktionsarten
- **CMT (Container Managed Transactions)**: Voreinstellung bei EJBs der Container übernimmt die Steuerung.
- **BMT (Bean Managed Transactions)**: Der Entwickler steuert die Transaktionen selbst.
- **Client Managed Transactions**: Der Client startet die Transaktion.
---
## Container Managed Transactions (CMT)
- Annotierung der Klasse mit `@TransactionManagement(TransactionManagementType.CONTAINER)` (ist Voreinstellung).
- Transaktionen starten beim Client-Aufruf einer Methode und enden i.d.R. mit der Methode.
- JMS-Nachrichten oder Datenbankstatements werden erst am Ende der Transaktion versendet.
### Rollback-Auslöser
1. **System-Exception** (von `RuntimeException` oder `java.rmi.RemoteException` abgeleitet):
- Wird vom Server in eine Application-Exception verpackt und an den Client geleitet.
- Rollback kann verhindert werden mit `@ApplicationException` an der Exception-Klasse.
- Nach einer System-Exception in einer Stateful/Stateless Session Bean wird die Bean zerstört.
2. **Application-Exception mit `@ApplicationException(rollback=true)`**:
- Application-Exceptions sind checked Exceptions (von `Exception`, aber nicht von `RuntimeException` abgeleitet).
- Werden direkt an den Client weitergeleitet (kein Verpacken).
- Checked Exceptions führen standardmäßig **nicht** zum Rollback.
3. **Manuell**: `sessionContext.setRollbackOnly()` (SessionContext per `@Resource` injizieren).
---
## CMT `@TransactionAttribute`
```java
@TransactionAttribute(TransactionAttributeType.<VALUE>)
```
| Wert | Bedeutung |
|------|-----------|
| **REQUIRED** | (Standard) Vorhandenen Kontext verwenden oder neuen anlegen. |
| **REQUIRES_NEW** | Immer neuen Kontext anlegen; vorhandener wird pausiert. |
| **MANDATORY** | Vorhandener Kontext erforderlich; sonst `TransactionRequiredException`. |
| **SUPPORTS** | Kontext wird übernommen, wenn vorhanden; sonst ohne Transaktion. |
| **NOT_SUPPORTED** | Keinen Kontext verwenden; vorhandener wird unterbrochen. |
| **NEVER** | Keinen Kontext akzeptieren; bei vorhandenem Kontext → `EJBException`. |
> **Hinweis**: Bei Message Driven Beans sind nur `REQUIRED`, `REQUIRES_NEW` und `SUPPORTS` möglich.
### `this`-Aufrufproblem
Ruft eine EJB-Methode eine andere Methode der **gleichen Klasse** über `this` auf, registriert der Container diesen Aufruf nicht → das Transaktionsattribut der aufgerufenen Methode wird ignoriert.
**Lösung**: Aufruf über eine andere Bean (per `@EJB` injiziert) oder über `sessionContext.getBusinessObject(KlassenName.class)`.
---
## CMT Container Callback Methoden (Session Synchronisierung)
Für Stateful Session Beans, die länger als eine Transaktion leben. Interface: `SessionSynchronization` (oder Annotationen):
| Methode / Annotation | Zeitpunkt |
|----------------------|-----------|
| `afterBegin()` / `@AfterBegin` | Nach Beginn der Transaktion |
| `beforeCompletion()` / `@BeforeCompletion` | In Phase 1 des 2-Phase-Commits |
| `afterCompletion(boolean commit)` / `@AfterCompletion` | In Phase 2 (`true` = commit, `false` = rollback) |
---
## Bean Managed Transactions (BMT)
```java
@TransactionManagement(TransactionManagementType.BEAN)
```
Zugriff auf `UserTransaction` über `sessionContext.getUserTransaction()`.
### UserTransaction-Methoden
| Methode | Beschreibung |
|---------|-------------|
| `begin()` | Startet eine neue Transaktion. BMT kann nicht an propagierten Transaktionen teilnehmen. |
| `commit()` | Beendet und bestätigt die Transaktion. |
| `rollback()` | Beendet und verwirft die Transaktion. |
> **Achtung**: In Stateful Session Beans können Transaktionen über mehrere Methoden geöffnet bleiben Transaktion muss in jedem Fall beendet werden.
---
## Client Managed Transactions
```java
InitialContext ctx = new InitialContext(..);
UserTransaction tx = (UserTransaction) ctx.lookup("UserTransaction");
```
Steuerung über dieselben Methoden wie bei BMT: `begin()`, `commit()`, `rollback()`.
> Der Jakarta EE Standard empfiehlt Serverherstellern, Client-Transaktionen zu unterstützen es ist jedoch keine Pflicht.
---
## Packaging
| Archivtyp | Inhalt | Verwendung |
|-----------|--------|------------|
| `.jar` | EJB-Projekt | EJB Application Archive |
| `.war` | Web-Projekt | Web Application Archive |
| `.ear` | jar + war + Deployment-Deskriptor | Enterprise Archive |
### EAR mit IntelliJ erstellen
1. Projekt gemäß Anleitung anlegen (ejb- und war-Module im Temp-Ordner belassen).
2. Unter Artifacts → „Java EE Application: Archive" erstellen:
- Output Directory = WildFly Deploy-Verzeichnis
- „Include in project build" anhaken
- EJB- und WAR-Artifact per Drag & Drop hinzufügen
---
## Architektur
### Klassische 3-Schichten-Architektur
```
Bedieneroberfläche → Fachkonzept → Datenhaltung
```
Mit Web-Controller (MVC):
```
Bedieneroberfläche → Controller → Fachkonzept → Datenhaltung
```
Mit Jakarta EE Middleware:
```
Bedieneroberfläche → Controller → Middleware (EJBs / DAOs) → Datenhaltung
```
### Data Transfer Objects (DTOs)
- **Ohne DTOs**: Entities werden direkt zum Client transportiert.
- **Mit DTOs**: Auf den Client angepasste Klassen → bessere Sicherheit und Performance.
### Monolithische Applikationen vs. Microservices
#### Monolithen
| Vorteile | Nachteile |
|----------|-----------|
| Einfache Entwicklung, IDE-Unterstützung | Schwer verständlich bei großer Codebasis |
| Einfaches Deployment (eine Datei) | Skalierung nur für das Gesamtsystem |
| Einfache End-to-End-Tests | Hohe Compilezeit |
| Leicht mit Load Balancer skalierbar | Lange Start- und Testzeiten |
#### Microservices
| Vorteile | Nachteile |
|----------|-----------|
| Kleinere, besser verständliche Codebasis | Schwierige Trennungsfindung |
| Einzeln deploybar und skalierbar | Netzwerkkomplexität |
| Teams können autonom arbeiten | Übergreifende Transaktionen schwierig |
| Verschiedene Technologien möglich | Höherer Administrationsaufwand |
| Schnellere Tests pro Service | Manche Aufgaben je Service implementieren |
---
## Das Spring Boot Framework
### Überblick
- Spring ist ein Entwicklungsframework für Enterprise Applikationen auf Java-Basis (Alternative zu Jakarta EE).
- Erste Version: 2004; seit 2009 durch VMware entwickelt.
- **Spring Boot** (ab 2014): deutlich vereinfachte Konfiguration durch Autokonfiguration.
- Projekte anlegen über [https://start.spring.io](https://start.spring.io) oder IntelliJ.
### Spring vs. Spring Boot
| Spring | Spring Boot |
|--------|-------------|
| Container muss selbst konfiguriert werden | Sinnvolle Voreinstellungen |
| Kein embedded Server | Embedded Apache Tomcat |
### Typische Module
- **Spring Data** Datenbankzugriff
- **Spring Web** Webapplikationen inkl. RESTful Web Services
- **Spring Security** Authentifizierung und Autorisierung
- **Spring Messaging** Messaging
- **Spring Cloud** Cloud-Technologien (AWS, Azure, ...)
### Starter
Ein Starter ist eine Maven-Dependency, die alle für eine Technologie notwendigen Libraries lädt und eine Autokonfiguration vornimmt.
---
## Anlegen eines Spring-Boot-Projekts
### Hauptklasse
```java
@SpringBootApplication
public class SpringDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringDemoApplication.class, args);
}
}
```
---
## Interaktive Anwendungen mit der Spring Shell
Starter:
```xml
<dependency>
<groupId>org.springframework.shell</groupId>
<artifactId>spring-shell-starter</artifactId>
</dependency>
```
Shell-Methode erstellen:
```java
@Component
public class HelloWorld {
@Command(description = "hello: Says Hello World!")
public String hello() {
return "Hello World!";
}
}
```
Aufruf im Prompt (Methodenname in kebab-case):
```
$> hello
Hello World!
```
Mit Parameter:
```java
@Command(description="hello-to --name <name>: ...")
public String helloTo(@Option(required = true) String name) {
return "Hello " + name;
}
```
```
$> hello-to --name Petra
Hello Petra
```
> **Hinweis**: In neueren IntelliJ-Versionen muss die App über Maven gestartet werden (`./mvnw spring-boot:run`).
---
## Komponenten
- POJOs werden mit `@Component` zu Spring-managed Beans.
- Müssen in einem Unterpaket der Klasse mit `@SpringBootApplication` liegen.
### Dependency Injection
```java
@Autowired
private SomeManagedBean bean; // Attribut-Injection
```
Besser: **Konstruktor-Injection** (final-Attribute möglich, testbar ohne Spring-Kontext):
```java
@Autowired
public HelloWorld(HelloWorldComponent helloWorldComponent) {
this.helloWorldComponent = helloWorldComponent;
}
```
### Semantische Annotationen
| Annotation | Verwendung |
|------------|-----------|
| `@Component` | Allgemeine Spring-managed Bean |
| `@Service` | Service-Klassen (Fachlogik) |
| `@Repository` | Data Access Objects |
| `@Controller` / `@RestController` | Web-Controller |
---
## CDI in Spring
- **Component Scanning**: Spring sucht nach `@Component`, `@Repository`, `@Service`, `@Controller` im Paket der Hauptklasse und Unterpaketen.
- **`@Bean`**: Entspricht CDI-Producern macht Werte aus Methoden injizierbar.
```java
@Bean
public String name() {
return "Max Mustermann";
}
```
- **`@ComponentScan`**: Erweitert den Suchpfad.
- **Interceptoren**: `@PostConstruct` etc. wie in Jakarta EE. Alternativ AOP.
---
## Zugriff auf Datenbanken
### Spring Data JPA
Starter:
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
```
`application.properties`:
```properties
spring.datasource.url=jdbc:mysql://localhost:3306/dbname
spring.datasource.username=<username>
spring.datasource.password=<password>
spring.jpa.hibernate.ddl-auto=create-drop
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
```
> In Spring laufen Methoden **nicht automatisch transaktional** → `@Transactional` an Methoden oder Klassen notwendig.
### SimpleJpaRepository
```java
SimpleJpaRepository<Book, Long> repository = new SimpleJpaRepository<>(Book.class, em);
List<Book> books = repository.findAll();
```
### CrudRepository / ListCrudRepository / JpaRepository
```java
public interface BookJpaRepository extends JpaRepository<Book, Long> {
// Methoden werden automatisch generiert
}
```
Kein `@Autowired` nötig Spring erkennt das Interface und erstellt automatisch eine Implementierung.
---
## Messaging
Starter (ActiveMQ):
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
```
ActiveMQ als Docker Container starten:
```bash
docker run -d --name activemq -p 61616:61616 -p 8161:8161 rmohr/activemq
```
`application.properties`:
```properties
spring.activemq.broker-url=tcp://localhost:61616
spring.activemq.user=admin
spring.activemq.password=admin
app.queue.name=buch.queue
spring.jms.pub-sub-domain=false
```
Hauptklasse:
```java
@SpringBootApplication
@EnableJms
public class MyApp { ... }
```
### Receiver
```java
@Component
public class MessageReceiver {
@JmsListener(destination = "springQueue")
public void receiveMessage(Book receivedBook, Message message) {
// Verarbeitung
}
}
```
### Sender
```java
JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class);
jmsTemplate.convertAndSend("queueName", new Book("Buch", 42.99));
```
### MessageConverter (für benutzerdefinierte Typen via JSON/Jackson)
```java
@Bean
public MessageConverter jacksonJmsMessageConverter() {
JacksonJsonMessageConverter converter = new JacksonJsonMessageConverter();
converter.setTypeIdPropertyName("_type");
Map<String, Class<?>> typeIdMappings = new HashMap<>();
typeIdMappings.put("buch", Buch.class);
converter.setTypeIdMappings(typeIdMappings);
return converter;
}
```
---
## Spring Web MVC RESTful Web Services
Starter:
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
```
### Controller
```java
@RestController
@RequestMapping("/hello")
public class HelloWebService {
@GetMapping
public String sayHello() {
return "Hello from Web Service";
}
}
```
### Mapping-Annotationen
| Annotation | HTTP-Methode |
|------------|-------------|
| `@GetMapping` | GET |
| `@PostMapping` | POST |
| `@PutMapping` | PUT |
| `@DeleteMapping` | DELETE |
### Parameter
**Query-Parameter**:
```java
@GetMapping("/to")
public String sayHelloTo(@RequestParam(value = "name", defaultValue = "World") String name) {
return "Hello to " + name;
}
// Aufruf: http://localhost:8080/hello/to?name=Peter
```
**Path-Parameter**:
```java
@GetMapping("/to/{name}")
public String sayHelloToParam(@PathVariable String name) {
return "Hello to " + name;
}
```
### Rückgabe von Objekten
```java
@GetMapping("/book/{id}")
public ResponseEntity<?> getBookById(@PathVariable long id) {
Book book = bookJpaRepository.getReferenceById(id);
return ResponseEntity.ok(book); // automatisch via JSON
}
```
### Empfang von Objekten
```java
@PostMapping(path = "/book")
public ResponseEntity<?> saveBook(@RequestBody Book book) {
Book b = bookJpaRepository.save(book);
return ResponseEntity.ok(b);
}
```
---
## Transaktionen in Spring
### Deklarativ (`@Transactional`)
```java
@Transactional
public void doSomething() {
// läuft in einem Transaktionskontext ab
}
```
- Bei `RuntimeException` → automatischer Rollback.
- Konfigurierbar: `readOnly`, Isolation (`REQUIRED`, `REQUIRES_NEW`), welche Exceptions zum Rollback führen.
- Neuer Thread erhält keinen propagierten Transaktionskontext.
### Programmgesteuert
```java
@Service
public class DoSomethingService {
@Autowired
private PlatformTransactionManager transactionManager;
public void doSomething() {
TransactionDefinition definition = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(definition);
try {
// ... Datenbankoperationen ...
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
}
}
}
```
#### Kernklassen
| Klasse | Rolle |
|--------|-------|
| `PlatformTransactionManager` | Führt Transaktionen technisch aus (begin, commit, rollback) |
| `TransactionDefinition` | Beschreibt Eigenschaften (Propagation, Timeout, ReadOnly, Name) |
| `TransactionStatus` | Repräsentiert die laufende Transaktion (Status abfragbar) |