# 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.) ``` | 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 org.springframework.shell spring-shell-starter ``` 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 : ...") 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 org.springframework.boot spring-boot-starter-data-jpa ``` `application.properties`: ```properties spring.datasource.url=jdbc:mysql://localhost:3306/dbname spring.datasource.username= spring.datasource.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 repository = new SimpleJpaRepository<>(Book.class, em); List books = repository.findAll(); ``` ### CrudRepository / ListCrudRepository / JpaRepository ```java public interface BookJpaRepository extends JpaRepository { // Methoden werden automatisch generiert } ``` Kein `@Autowired` nötig – Spring erkennt das Interface und erstellt automatisch eine Implementierung. --- ## Messaging Starter (ActiveMQ): ```xml org.springframework.boot spring-boot-starter-activemq ``` 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> typeIdMappings = new HashMap<>(); typeIdMappings.put("buch", Buch.class); converter.setTypeIdMappings(typeIdMappings); return converter; } ``` --- ## Spring Web MVC – RESTful Web Services Starter: ```xml org.springframework.boot spring-boot-starter-web ``` ### 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) |