{"id":36799,"date":"2023-10-04T21:35:26","date_gmt":"2023-10-04T19:35:26","guid":{"rendered":"https:\/\/www.happycoders.eu\/?p=36799"},"modified":"2025-06-12T08:52:32","modified_gmt":"2025-06-12T06:52:32","slug":"ports-and-adapters-java-tutorial-db","status":"publish","type":"post","link":"https:\/\/www.happycoders.eu\/de\/software-craftsmanship\/ports-and-adapters-java-tutorial-db\/","title":{"rendered":"Ports and Adapters Java-Tutorial: Datenbankadapter hinzuf\u00fcgen"},"content":{"rendered":"\n<p>Im vorherigen, <a href=\"\/de\/software-craftsmanship\/hexagonale-architektur-java\/\">zweiten Teil dieser Serie \u00fcber die Hexagonale Architektur<\/a> (deren offizieller Name \u201ePorts and Adapters\u201c lautet), habe ich demonstriert, wie man eine Java-Anwendung mit eben dieser Architektur implementiert \u2013 zun\u00e4chst ohne Application Framework und Datenbank. Stattdessen haben wir einen simplen Dependency-Injection-Mechanismus implementiert und ein In-Memory-Repository als Persistenz-Adapter eingesetzt.<\/p>\n\n\n\n<p>In diesem dritten Teil werden wir einen Adapter implementieren, der die Daten \u2013 Warenk\u00f6rbe und Produkte eines Online-Shops \u2013 in einer Datenbank speichert. Und um die Flexibilit\u00e4t unserer Architektur zu unterstreichen, werden wir den Adapter nicht einfach ersetzen, sondern ihn <em>hinzuf\u00fcgen<\/em> und den gew\u00fcnschten Adapter (In-Memory oder Datenbank) \u00fcber eine System Property ausw\u00e4hlbar machen.<\/p>\n\n\n\n<p>Die Architektur unserer Anwendung sieht aktuell wie folgt aus (Port und Adapter, um die es heute geht, sind hervorgehoben):<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"588\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-800x588.png\" alt=\"Ports-and-Adapters-Architektur der Beispielanwendung\" class=\"wp-image-37462\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-800x588.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-224x165.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-336x247.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-504x370.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-672x494.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-400x294.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-600x441.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-944x694.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-1200x882.png 1200w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example.png 1600w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Ports-and-Adapters-Architektur der Beispielanwendung<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Die anf\u00e4ngliche Implementierung ohne Datenbank erlaubt es uns nun \u2013 ganz im Sinne der Ports-and-Adapters-Architektur \u2013 die Entscheidung \u00fcber die einzusetzende Datenbank und das Datenbankschema auf die Erfahrungen zu st\u00fctzen, die wir bei der Entwicklung des Anwendungskerns gesammelt haben.<\/p>\n\n\n\n<p>Warenk\u00f6rbe und Produkte lassen sich sehr gut in einer relationalen Datenbank ablegen. Daher werden wir f\u00fcr die Persistierung MySQL verwenden und f\u00fcr das O\/R-Mapping JPA. Die folgende Grafik zeigt unsere Ziel-Architektur:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"588\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-with-db-800x588.png\" alt=\"Ports-and-Adapters-Architektur mit zwei Persistenz-Adaptern\" class=\"wp-image-37463\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-with-db-800x588.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-with-db-224x165.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-with-db-336x247.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-with-db-504x370.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-with-db-672x494.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-with-db-400x294.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-with-db-600x441.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-with-db-944x694.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-with-db-1200x882.png 1200w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-architecture-example-with-db.png 1600w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Ports-and-Adapters-Architektur mit zwei Persistenz-Adaptern<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Und all das werden wir \u2013 wiederum im Sinne der Ports-and-Adapters-Architektur \u2013 umsetzen, ohne eine einzige Zeile Code im Anwendungskern \u00e4ndern zu m\u00fcssen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"eingesetzte-technologien\">Eingesetzte Technologien<\/h3>\n\n\n\n<p>Wir werden die folgenden zus\u00e4tzlichen Technologien f\u00fcr die Erweiterung unserer Anwendung einsetzen:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a rel=\"noopener\" href=\"https:\/\/hibernate.org\/\" target=\"_blank\">Hibernate<\/a> als OR-Mapper (Implementierung der Jakarta Persistence API \u2013 JPA),<\/li>\n\n\n\n<li><a rel=\"noopener\" href=\"https:\/\/testcontainers.com\/\" target=\"_blank\">Testcontainers<\/a>, ein Framework, das es uns erlaubt, aus Tests heraus eine MySQL-Datenbank als Docker-Container zu starten.<\/li>\n<\/ul>\n\n\n\n<p>Mit Hilfe von Testcontainers werden wir Integration-Tests f\u00fcr die Hibernate-Adapter implementieren.<\/p>\n\n\n\n<p>Den Quellcode zum Artikel findest du in <a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\" target=\"_blank\" rel=\"noopener\">diesem GitHub-Repository<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"sekundaere-ports-und-adapter\">Sekund\u00e4re Ports und Adapter<\/h2>\n\n\n\n<p>Zur Auffrischung zeige ich dir in der folgenden Grafik noch einmal den aktuellen Stand der sekund\u00e4ren Ports und Adapter (an den prim\u00e4ren Ports und Adaptern werden wir in diesem Teil der Serie nichts \u00e4ndern  \u2013 diese habe ich daher im oberen Teil des Klassendiagramms ausgeblendet).<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"371\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-800x371.png\" alt=\"Sekund\u00e4re Ports und Adapter der Beispielanwendung\" class=\"wp-image-37466\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-800x371.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-224x104.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-336x156.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-504x234.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-672x312.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-400x186.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-600x278.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-944x438.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-1200x557.png 1200w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram.png 1600w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Sekund\u00e4re Ports und Adapter der Beispielanwendung<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Wir werden heute das Paket <em>eu.happycoders.shop.adapter.out.persistence.jpa<\/em> hinzuf\u00fcgen. Das folgende Klassendiagramm zeigt dieses Paket mit zwei neuen Adaptern \u2013 <code>JpaCartRepository<\/code> und <code>JpaProductRepository<\/code> \u2013 im rechten unteren Bereich:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"371\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-with-db-800x371.png\" alt=\"Sekund\u00e4re Ports und Adapter mit zwei Persistenz-Adaptern\" class=\"wp-image-37467\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-with-db-800x371.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-with-db-224x104.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-with-db-336x156.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-with-db-504x234.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-with-db-672x312.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-with-db-400x186.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-with-db-600x278.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-with-db-944x438.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-with-db-1200x557.png 1200w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-secondary-ports-class-diagram-with-db.png 1600w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Sekund\u00e4re Ports und Adapter mit zwei Persistenz-Adaptern<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Bevor wir mit der Implementierung beginnen, m\u00f6chte ich auf das Thema Mapping zu sprechen kommen, das ich <a href=\"\/de\/software-craftsmanship\/hexagonale-architektur\/#mapping\">hier im ersten Teil der Serie<\/a> behandelt habe.<\/p>\n\n\n\n<p>Wir werden hier das von mir favorisierte Zwei-Wege-Mapping einsetzen: Wir werden f\u00fcr jede Modell-Entity eine entsprechende JPA-Entity anlegen und dann sowohl beim Persistieren einer Entity als auch beim Laden aus der Datenbank auf die jeweils andere Entity-Klasse mappen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"implementierung-der-adapter-klassen\">Implementierung der Adapter-Klassen<\/h2>\n\n\n\n<p>Der folgenden Screenshot zeigt das um alle ben\u00f6tigten Klassen erweiterte <em>adapter<\/em>-Modul in der IDE (das Paket mit den prim\u00e4ren Adaptern, <em>in.rest<\/em> habe ich zugeklappt):<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-half_400\"><img decoding=\"async\" width=\"400\" height=\"454\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-java-adapter-module.v3-400x454.png\" alt=\"Ports and Adapters mit Java - Adapter-Klassen\" class=\"wp-image-37670\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-java-adapter-module.v3-400x454.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-java-adapter-module.v3-224x254.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-java-adapter-module.v3-336x381.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-java-adapter-module.v3-504x572.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-java-adapter-module.v3-672x763.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-java-adapter-module.v3-600x681.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-java-adapter-module.v3.png 800w\" sizes=\"(max-width: 400px) 100vw, 400px\" \/><\/figure>\n<\/div>\n\n\n<p>Beginnen wir mit der Implementierung der JPA-Entities\u2026<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"implementierung-der-jpa-entity-klassen\">Implementierung der JPA-Entity-Klassen<\/h3>\n\n\n\n<p>Um die JPA-Klassen implementieren zu k\u00f6nnen, ben\u00f6tigen wir zun\u00e4chst eine Abh\u00e4ngigkeit zu JPA. Die f\u00fcgen wir mit folgendem Eintrag in der <em>pom.xml<\/em> des <em>adapter<\/em>-Moduls hinzu:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>jakarta.persistence<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>jakarta.persistence-api<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>3.1.0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Beachte, dass wir an dieser Stelle noch keine Dependency zu Hibernate ben\u00f6tigen. Hibernate ist eine <em>Implementierung<\/em> von JPA, daher ben\u00f6tigen wir es erst zur Laufzeit. Wir werden Hibernate (und einige zus\u00e4tzlich ben\u00f6tigte Abh\u00e4ngigkeiten, wie den MySQL-JDBC-Treiber) im weiteren Verlauf im Test-Scope hinzuf\u00fcgen \u2013 und sp\u00e4ter noch einmal im <em>bootstrap<\/em>-Modul.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">ProductJpaEntity<\/h4>\n\n\n\n<p>Beginnen wir mit der JPA-Entity f\u00fcr das Produkt. Hier zur Erinnerung die entsprechende Entity-Klasse, <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/model\/src\/main\/java\/eu\/happycoders\/shop\/model\/product\/Product.java\" target=\"_blank\">Product<\/a> (der Link f\u00fchrt ins GitHub-Repository), aus dem <em>model<\/em>-Modul:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@Data<\/span>\n<span class=\"hljs-meta\">@Accessors<\/span>(fluent = <span class=\"hljs-keyword\">true<\/span>)\n<span class=\"hljs-meta\">@AllArgsConstructor<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Product<\/span> <\/span>{\n\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> ProductId id;\n  <span class=\"hljs-keyword\">private<\/span> String name;\n  <span class=\"hljs-keyword\">private<\/span> String description;\n  <span class=\"hljs-keyword\">private<\/span> Money price;\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">int<\/span> itemsInStock;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/model\/src\/main\/java\/eu\/happycoders\/shop\/model\/product\/ProductId.java\" target=\"_blank\" rel=\"noopener\">ProductId<\/a> ist ein Record, der lediglich einen String enth\u00e4lt. Die Verwendung solch eines Value Objects hat zwei Vorteile: Zum einen bringt es Typsicherheit mit sich, zum anderen k\u00f6nnen wir die G\u00fcltigkeit einer Produkt-ID im Konstruktor des Value Objects sicherstellen.<\/p>\n\n\n\n<p>Wir erstellen nun im Paket <em>eu.happycoders.shop.adapter.out.persistence.jpa<\/em> eine entsprechende JPA-Entity-Klasse, <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/adapter\/src\/main\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/jpa\/ProductJpaEntity.java\" target=\"_blank\">ProductJpaEntity<\/a>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@Entity<\/span>\n<span class=\"hljs-meta\">@Table<\/span>(name = <span class=\"hljs-string\">\"Product\"<\/span>)\n<span class=\"hljs-meta\">@Getter<\/span>\n<span class=\"hljs-meta\">@Setter<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ProductJpaEntity<\/span> <\/span>{\n\n  <span class=\"hljs-meta\">@Id<\/span> <span class=\"hljs-keyword\">private<\/span> String id;\n\n  <span class=\"hljs-meta\">@Column<\/span>(nullable = <span class=\"hljs-keyword\">false<\/span>)\n  <span class=\"hljs-keyword\">private<\/span> String name;\n\n  <span class=\"hljs-meta\">@Column<\/span>(nullable = <span class=\"hljs-keyword\">false<\/span>)\n  <span class=\"hljs-keyword\">private<\/span> String description;\n\n  <span class=\"hljs-meta\">@Column<\/span>(nullable = <span class=\"hljs-keyword\">false<\/span>)\n  <span class=\"hljs-keyword\">private<\/span> String priceCurrency;\n\n  <span class=\"hljs-meta\">@Column<\/span>(nullable = <span class=\"hljs-keyword\">false<\/span>)\n  <span class=\"hljs-keyword\">private<\/span> BigDecimal priceAmount;\n\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">int<\/span> itemsInStock;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Die JPA-Entity unterscheidet sich in einigen Punkten von der Modell-Entity:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Die technischen JPA-Annotationen <code>@Entity<\/code> und <code>@Table<\/code> machen die Klasse zu einer JPA-Entity, die auf die Datenbank-Tabelle \u201eProduct\u201c abgebildet wird.<\/li>\n\n\n\n<li>Die mit <code>@Id<\/code> markierte ID ist ein String, kein <code>ProductId<\/code>-Record.<\/li>\n\n\n\n<li>Alle nicht-primitiven Pflichtfelder sind mit <code>@Column(nullable = false)<\/code> annotiert, damit sich nicht irrt\u00fcmlich <code>null<\/code>-Werte in die Datenbank einschleichen.<\/li>\n\n\n\n<li>Der Preis, der in der Modell-Klasse vom Typ <code>Money<\/code> war, ist \u2013 in Ermangelung eines entsprechenden SQL-Typs \u2013 aufgeteilt auf zwei Felder: <code>priceCurrency<\/code> und <code>priceAmount<\/code>.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">CartLineItemJpaEntity<\/h4>\n\n\n\n<p>Hier zur Erinnerung das <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/model\/src\/main\/java\/eu\/happycoders\/shop\/model\/cart\/CartLineItem.java\" target=\"_blank\">CartLineItem<\/a> (ein Eintrag im Warenkorb) aus dem <em>model<\/em>-Modul:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@Getter<\/span>\n<span class=\"hljs-meta\">@Accessors<\/span>(fluent = <span class=\"hljs-keyword\">true<\/span>)\n<span class=\"hljs-meta\">@RequiredArgsConstructor<\/span>\n<span class=\"hljs-meta\">@AllArgsConstructor<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">CartLineItem<\/span> <\/span>{\n\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> Product product;\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">int<\/span> quantity;\n\n  <span class=\"hljs-comment\">\/\/ ... <\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Die Modell-Klasse enth\u00e4lt zudem Methoden, um die Anzahl eines Produkts zu erh\u00f6hen und um den Gesamtpreis einer Warenkorbposition zu berechnen.<\/p>\n\n\n\n<p>Hier ist die zugeh\u00f6rige JPA-Klasse, <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/adapter\/src\/main\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/jpa\/CartLineItemJpaEntity.java\" target=\"_blank\">CartLineItemJpaEntity<\/a>, ebenfalls im Paket <em>eu.happycoders.shop.adapter.out.persistence.jpa<\/em>. Die JPA-Entity ben\u00f6tigt keine entsprechenden Methoden, da ihr einziger Zweck die Persistierung ist.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@Entity<\/span>\n<span class=\"hljs-meta\">@Table<\/span>(name = <span class=\"hljs-string\">\"CartLineItem\"<\/span>)\n<span class=\"hljs-meta\">@Getter<\/span>\n<span class=\"hljs-meta\">@Setter<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">CartLineItemJpaEntity<\/span> <\/span>{\n\n  <span class=\"hljs-meta\">@Id<\/span> <span class=\"hljs-meta\">@GeneratedValue<\/span> <span class=\"hljs-keyword\">private<\/span> Integer id;\n\n  <span class=\"hljs-meta\">@ManyToOne<\/span> <span class=\"hljs-keyword\">private<\/span> CartJpaEntity cart;\n\n  <span class=\"hljs-meta\">@ManyToOne<\/span> <span class=\"hljs-keyword\">private<\/span> ProductJpaEntity product;\n\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">int<\/span> quantity;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Wir sehen hier einige weitere JPA-Annotationen:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>@GeneratedValue<\/code> am Prim\u00e4rschl\u00fcssel <code>id<\/code> sorgt daf\u00fcr, dass dieses Feld automatisch von MySQL durch aufsteigende Zahlen gef\u00fcllt wird. Da dieser Prim\u00e4rschl\u00fcssel lediglich f\u00fcr die Datenbank von Relevanz ist, gibt es in der Modellklasse kein entsprechendes Feld.<\/li>\n\n\n\n<li><code>@ManyToOne<\/code> an <code>cart<\/code> definiert eine N:1-Beziehung zum Warenkorb und f\u00fchrt letztendlich dazu, dass in der Datenbanktabelle <code>CartLineItem<\/code> eine Spalte mit dem Namen <code>cart_id<\/code> erzeugt wird sowie eine Fremdschl\u00fcsselbeziehung auf die <code>Cart<\/code>-Tabelle.<\/li>\n\n\n\n<li><code>@ManyToOne<\/code> an <code>product<\/code> definiert entsprechend eine N:1-Beziehung zum Produkt.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">CartJpaEntity<\/h4>\n\n\n\n<p>Und zuletzt, wieder zur Erinnerung, die <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/model\/src\/main\/java\/eu\/happycoders\/shop\/model\/cart\/Cart.java\" target=\"_blank\">Cart<\/a>-Klasse aus dem <em>model<\/em>-Modul:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@Accessors<\/span>(fluent = <span class=\"hljs-keyword\">true<\/span>)\n<span class=\"hljs-meta\">@RequiredArgsConstructor<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Cart<\/span> <\/span>{\n\n  <span class=\"hljs-meta\">@Getter<\/span> <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> CustomerId id; <span class=\"hljs-comment\">\/\/ cart ID = customer ID<\/span>\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> Map&lt;ProductId, CartLineItem&gt; lineItems = <span class=\"hljs-keyword\">new<\/span> LinkedHashMap&lt;&gt;();\n\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Die Modellklasse enth\u00e4lt au\u00dferdem Methoden, um Produkte hinzuzuf\u00fcgen, die Warenkorbeintr\u00e4ge abzurufen und um den Gesamtwert des Warenkorbs zu berechnen.<\/p>\n\n\n\n<p>Hier ist die entsprechende JPA-Klasse, <a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/adapter\/src\/main\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/jpa\/CartJpaEntity.java\" target=\"_blank\" rel=\"noopener\">CartJpaEntity<\/a>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@Entity<\/span>\n<span class=\"hljs-meta\">@Table<\/span>(name = <span class=\"hljs-string\">\"Cart\"<\/span>)\n<span class=\"hljs-meta\">@Getter<\/span>\n<span class=\"hljs-meta\">@Setter<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">CartJpaEntity<\/span> <\/span>{\n\n  <span class=\"hljs-meta\">@Id<\/span> <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">int<\/span> customerId;\n\n  <span class=\"hljs-meta\">@OneToMany<\/span>(mappedBy = <span class=\"hljs-string\">\"cart\"<\/span>, cascade = CascadeType.ALL, orphanRemoval = <span class=\"hljs-keyword\">true<\/span>)\n  <span class=\"hljs-keyword\">private<\/span> List&lt;CartLineItemJpaEntity&gt; lineItems;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Hier habe ich eine weitere JPA-Annotation verwendet:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>@OneToMany<\/code> an <code>lineItems<\/code> f\u00fchrt dazu, dass ein Zugriff auf dieses Feld alle auf diesen Warenkorb referenzierenden Eintr\u00e4ge der <code>CartLineItem<\/code>-Tabelle ausliest und in dieser Liste speichert. Und andersherum werden beim Speichern eines Warenkorbs alle in dieser Liste befindlichen Eintr\u00e4ge in die <code>CartLineItem<\/code>-Tabelle geschrieben.<\/li>\n<\/ul>\n\n\n\n<p>Damit haben wir alle ben\u00f6tigten JPA-Klassen implementiert. Kommen wir nun zu den Mappern.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"implementierung-der-modell-jpa-mapper-klassen\">Implementierung der Modell-JPA-Mapper-Klassen<\/h3>\n\n\n\n<p>Beginnen wir wieder mit dem Produkt\u2026<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">ProductMapper<\/h4>\n\n\n\n<p>Der <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/adapter\/src\/main\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/jpa\/ProductMapper.java\" target=\"_blank\">ProductMapper<\/a> wandelt ein <code>Product<\/code> in eine <code>ProductJpaEntity<\/code> um und umgekehrt. Der Mapper liegt im gleichen Paket wie die JPA-Entities, <em>eu.happycoders.shop.adapter.out.persistence.jpa<\/em>. Da hier auch die Adapter liegen werden, brauchen der Mapper und seine Methoden nicht <code>public<\/code> zu sein.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ProductMapper<\/span> <\/span>{\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-title\">ProductMapper<\/span><span class=\"hljs-params\">()<\/span> <\/span>{}\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">static<\/span> ProductJpaEntity <span class=\"hljs-title\">toJpaEntity<\/span><span class=\"hljs-params\">(Product product)<\/span> <\/span>{\n    ProductJpaEntity jpaEntity = <span class=\"hljs-keyword\">new<\/span> ProductJpaEntity();\n\n    jpaEntity.setId(product.id().value());\n    jpaEntity.setName(product.name());\n    jpaEntity.setDescription(product.description());\n    jpaEntity.setPriceCurrency(product.price().currency().getCurrencyCode());\n    jpaEntity.setPriceAmount(product.price().amount());\n    jpaEntity.setItemsInStock(product.itemsInStock());\n\n    <span class=\"hljs-keyword\">return<\/span> jpaEntity;\n  }\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">static<\/span> Optional&lt;Product&gt; <span class=\"hljs-title\">toModelEntityOptional<\/span><span class=\"hljs-params\">(ProductJpaEntity jpaEntity)<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> Optional.ofNullable(jpaEntity).map(ProductMapper::toModelEntity);\n  }\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">static<\/span> Product <span class=\"hljs-title\">toModelEntity<\/span><span class=\"hljs-params\">(ProductJpaEntity jpaEntity)<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> Product(\n        <span class=\"hljs-keyword\">new<\/span> ProductId(jpaEntity.getId()),\n        jpaEntity.getName(),\n        jpaEntity.getDescription(),\n        <span class=\"hljs-keyword\">new<\/span> Money(\n            Currency.getInstance(jpaEntity.getPriceCurrency()), \n            jpaEntity.getPriceAmount()),\n        jpaEntity.getItemsInStock());\n  }\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">static<\/span> List&lt;Product&gt; <span class=\"hljs-title\">toModelEntities<\/span><span class=\"hljs-params\">(List&lt;ProductJpaEntity&gt; jpaEntities)<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> jpaEntities.stream().map(ProductMapper::toModelEntity).toList();\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Das <code>final<\/code>-Keyword an der Klasse und der private Konstruktor haben zudem den Sinn, dass diese Klasse nicht instantiiert wird. Hier folge ich einer Empfehlung von statischen Codeanalysetools. Ansonsten d\u00fcrfte die Mapper-Klasse keiner weiteren Erkl\u00e4rung bed\u00fcrfen.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">CartMapper<\/h4>\n\n\n\n<p>Zur Erinnerung: Als sekund\u00e4ren Port f\u00fcr Warenk\u00f6rbe haben wir nur ein <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/application\/src\/main\/java\/eu\/happycoders\/shop\/application\/port\/out\/persistence\/CartRepository.java\" target=\"_blank\">CartRepository<\/a>, welches Warenk\u00f6rbe samt ihrer Eintr\u00e4ge persistiert. Wir haben kein separates <code>CartLineItemRepository<\/code>. <\/p>\n\n\n\n<p>Dementsprechend werden wir sowohl <code>Cart<\/code> als auch <code>CartLineItem<\/code> in einer gemeinsamen Mapper-Klasse, dem <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/adapter\/src\/main\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/jpa\/CartMapper.java\" target=\"_blank\">CartMapper<\/a>, von und nach <code>CartJpaEntity<\/code> und <code>CartLineItemJpaEntity<\/code> umwandeln. Hier zun\u00e4chst der Code f\u00fcr die Umwandlung von Modell-Klassen in JPA-Klassen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">CartMapper<\/span> <\/span>{\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-title\">CartMapper<\/span><span class=\"hljs-params\">()<\/span> <\/span>{}\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">static<\/span> CartJpaEntity <span class=\"hljs-title\">toJpaEntity<\/span><span class=\"hljs-params\">(Cart cart)<\/span> <\/span>{\n    CartJpaEntity cartJpaEntity = <span class=\"hljs-keyword\">new<\/span> CartJpaEntity();\n    cartJpaEntity.setCustomerId(cart.id().value());\n\n    cartJpaEntity.setLineItems(\n        cart.lineItems().stream()\n            .map(lineItem -&gt; toJpaEntity(cartJpaEntity, lineItem))\n            .toList());\n\n    <span class=\"hljs-keyword\">return<\/span> cartJpaEntity;\n  }\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">static<\/span> CartLineItemJpaEntity <span class=\"hljs-title\">toJpaEntity<\/span><span class=\"hljs-params\">(\n      CartJpaEntity cartJpaEntity, CartLineItem lineItem)<\/span> <\/span>{\n    ProductJpaEntity productJpaEntity = <span class=\"hljs-keyword\">new<\/span> ProductJpaEntity();\n    productJpaEntity.setId(lineItem.product().id().value());\n\n    CartLineItemJpaEntity entity = <span class=\"hljs-keyword\">new<\/span> CartLineItemJpaEntity();\n    entity.setCart(cartJpaEntity);\n    entity.setProduct(productJpaEntity);\n    entity.setQuantity(lineItem.quantity());\n\n    <span class=\"hljs-keyword\">return<\/span> entity;\n  }\n\n  <span class=\"hljs-comment\">\/\/ method toModelEntityOptional(\u2026), see below<\/span>\n\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Den Prim\u00e4rschl\u00fcssel <code>CartLineItemJpaEntity.id<\/code> brauchen wir nicht zu setzen; dies geschieht, wie oben erw\u00e4hnt, automatisch durch MySQL.<\/p>\n\n\n\n<p>Bei der Umwandlung in die andere Richtung, also von JPA-Klassen in Modell-Klassen, m\u00fcssen wir eine Besonderheit beachten: Die Methode <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/model\/src\/main\/java\/eu\/happycoders\/shop\/model\/cart\/Cart.java#L27\" target=\"_blank\">Cart.addProduct(\u2026)<\/a> bzw. die von ihr aufgerufene Methode <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/model\/src\/main\/java\/eu\/happycoders\/shop\/model\/cart\/CartLineItem.java#L24\" target=\"_blank\">CartLineItem.increaseQuantityBy(\u2026)<\/a> wirft eine <code>NotEnoughItemsInStockException<\/code>, sollte ein Produkt nicht in ausreichender St\u00fcckzahl vorhanden sein. Das darf nat\u00fcrlich nicht dann passieren, wenn wir einen Warenkorb aus der Datenbank laden.<\/p>\n\n\n\n<p>Dementsprechend ben\u00f6tigen wir noch eine weitere Methode in <code>Cart<\/code>, die diese Pr\u00fcfung umgeht:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">putProductIgnoringNotEnoughItemsInStock<\/span><span class=\"hljs-params\">(Product product, <span class=\"hljs-keyword\">int<\/span> quantity)<\/span> <\/span>{\n  lineItems.put(product.id(), <span class=\"hljs-keyword\">new<\/span> CartLineItem(product, quantity));\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Diese Methode k\u00f6nnen wir dann in <code>CartMapper.toModelEntityOptional(\u2026)<\/code> einsetzen, um eine <code>CartJpaEntity<\/code> in ein <code>Cart<\/code> umzuwandeln:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">static<\/span> Optional&lt;Cart&gt; <span class=\"hljs-title\">toModelEntityOptional<\/span><span class=\"hljs-params\">(CartJpaEntity cartJpaEntity)<\/span> <\/span>{\n  <span class=\"hljs-keyword\">if<\/span> (cartJpaEntity == <span class=\"hljs-keyword\">null<\/span>) {\n    <span class=\"hljs-keyword\">return<\/span> Optional.empty();\n  }\n\n  CustomerId customerId = <span class=\"hljs-keyword\">new<\/span> CustomerId(cartJpaEntity.getCustomerId());\n  Cart cart = <span class=\"hljs-keyword\">new<\/span> Cart(customerId);\n\n  <span class=\"hljs-keyword\">for<\/span> (CartLineItemJpaEntity lineItemJpaEntity : cartJpaEntity.getLineItems()) {\n    cart.putProductIgnoringNotEnoughItemsInStock(\n        ProductMapper.toModelEntity(lineItemJpaEntity.getProduct()),\n        lineItemJpaEntity.getQuantity());\n  }\n\n  <span class=\"hljs-keyword\">return<\/span> Optional.of(cart);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Damit haben wir alle ben\u00f6tigten Mapper implementiert. Kommen wir nun zu den eigentlichen Adaptern.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"implementierung-der-jpa-adapter\">Implementierung der JPA-Adapter<\/h3>\n\n\n\n<p>Beginnen wir wieder mit dem Produkt\u2026<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">JpaProductRepository<\/h4>\n\n\n\n<p>Zur Erinnerung: Der f\u00fcr die Produkte zust\u00e4ndige Port, <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/application\/src\/main\/java\/eu\/happycoders\/shop\/application\/port\/out\/persistence\/ProductRepository.java\" target=\"_blank\">ProductRepository<\/a>, definiert drei Methoden:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">interface<\/span> <span class=\"hljs-title\">ProductRepository<\/span> <\/span>{\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">save<\/span><span class=\"hljs-params\">(Product product)<\/span><\/span>;\n\n  <span class=\"hljs-function\">Optional&lt;Product&gt; <span class=\"hljs-title\">findById<\/span><span class=\"hljs-params\">(ProductId productId)<\/span><\/span>;\n\n  <span class=\"hljs-function\">List&lt;Product&gt; <span class=\"hljs-title\">findByNameOrDescription<\/span><span class=\"hljs-params\">(String query)<\/span><\/span>;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Im letzten Teil der Serie haben wir dieses Interface im In-Memory-Adapter <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/adapter\/src\/main\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/inmemory\/InMemoryProductRepository.java\" target=\"_blank\">InMemoryProductRepository<\/a> implementiert. In diesem Teil f\u00fcgen wir den JPA-Adapter, <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/adapter\/src\/main\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/jpa\/JpaProductRepository.java\" target=\"_blank\">JpaProductRepository<\/a> im Paket <em>eu.happycoders.shop.adapter.out.persistence.jpa<\/em> (also im selben Paket wie die JPA-Entities und die Mapper) hinzu.<\/p>\n\n\n\n<p>Wir starten mit dem Grundger\u00fcst der Klasse. Zun\u00e4chst einmal ben\u00f6tigen wir f\u00fcr JPA eine <code>EntityManagerFactory<\/code>, die wir \u00fcber den Konstruktor injizieren. Im Konstruktor rufen wir zudem die Methode <code>createDemoProducts()<\/code> auf, die \u2013 genau wie im <code>InMemoryProductRepository<\/code> \u2013 einige Test-Produkte anlegt:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">JpaProductRepository<\/span> <span class=\"hljs-keyword\">implements<\/span> <span class=\"hljs-title\">ProductRepository<\/span> <\/span>{\n\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> EntityManagerFactory entityManagerFactory;\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-title\">JpaProductRepository<\/span><span class=\"hljs-params\">(EntityManagerFactory entityManagerFactory)<\/span> <\/span>{\n    <span class=\"hljs-keyword\">this<\/span>.entityManagerFactory = entityManagerFactory;\n    createDemoProducts();\n  }\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">createDemoProducts<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    DemoProducts.DEMO_PRODUCTS.forEach(<span class=\"hljs-keyword\">this<\/span>::save);\n  }\n\n  <span class=\"hljs-comment\">\/\/ ... interface methods ...<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Nun k\u00f6nnen wir die im Port-Interface definierten Methoden anlegen. Die <code>save(\u2026)<\/code>-Methode erzeugt zun\u00e4chst innerhalb eines <em>try-with-resources<\/em>-Blocks einen <code>EntityManager<\/code>, startet eine Transaktion, wandelt das <code>Product<\/code> in ein <code>ProductJpaEntity<\/code> um, speichert dieses \u00fcber <code>EntityManager.merge(\u2026)<\/code> in der Datenbank und committed schlie\u00dflich die Transaktion. Durch das Ende des <em>try-with-resources<\/em>-Blocks wird zuletzt der <code>EntityManager<\/code> geschlossen.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@Override<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">save<\/span><span class=\"hljs-params\">(Product product)<\/span> <\/span>{\n  <span class=\"hljs-keyword\">try<\/span> (EntityManager entityManager = entityManagerFactory.createEntityManager()) {\n    entityManager.getTransaction().begin();\n    entityManager.merge(ProductMapper.toJpaEntity(product));\n    entityManager.getTransaction().commit();\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Auch die zwei weiteren Methoden erzeugen zun\u00e4chst einen <code>EntityManager<\/code>. Die Methode <code>findById(\u2026)<\/code> l\u00e4dt via <code>EntityManager.find(\u2026)<\/code> ein <code>ProductJpaEntity<\/code> und wandelt dieses via <code>ProductMapper<\/code> in ein <code>Optional&lt;Product&gt;<\/code> um:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@Override<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> Optional&lt;Product&gt; <span class=\"hljs-title\">findById<\/span><span class=\"hljs-params\">(ProductId productId)<\/span> <\/span>{\n  <span class=\"hljs-keyword\">try<\/span> (EntityManager entityManager = entityManagerFactory.createEntityManager()) {\n    ProductJpaEntity jpaEntity = \n        entityManager.find(ProductJpaEntity<span class=\"hljs-class\">.<span class=\"hljs-keyword\">class<\/span>, <span class=\"hljs-title\">productId<\/span>.<span class=\"hljs-title\">value<\/span>())<\/span>;\n    <span class=\"hljs-keyword\">return<\/span> ProductMapper.toModelEntityOptional(jpaEntity);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><code>findByNameOrDescription(\u2026)<\/code> erzeugt via <code>EntityManager.createQuery(\u2026)<\/code> eine <code>TypedQuery<\/code>, l\u00e4dt dann via <code>TypedQuery.getResultList()<\/code> aus der Datenbank die der Suche entsprechende <code>ProductJpaEntity<\/code>-Liste und wandelt diese via <code>ProductMapper<\/code> in eine <code>Product<\/code>-Liste um:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@Override<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> List&lt;Product&gt; <span class=\"hljs-title\">findByNameOrDescription<\/span><span class=\"hljs-params\">(String queryString)<\/span> <\/span>{\n  <span class=\"hljs-keyword\">try<\/span> (EntityManager entityManager = entityManagerFactory.createEntityManager()) {\n    TypedQuery&lt;ProductJpaEntity&gt; query =\n        entityManager\n            .createQuery(\n                <span class=\"hljs-string\">\"from ProductJpaEntity \"<\/span>\n                        + <span class=\"hljs-string\">\"where name like :query or description like :query\"<\/span>,\n                ProductJpaEntity<span class=\"hljs-class\">.<span class=\"hljs-keyword\">class<\/span>)\n            .<span class=\"hljs-title\">setParameter<\/span>(\"<span class=\"hljs-title\">query<\/span>\", \"%\" + <span class=\"hljs-title\">queryString<\/span> + \"%\")<\/span>;\n\n    List&lt;ProductJpaEntity&gt; entities = query.getResultList();\n\n    <span class=\"hljs-keyword\">return<\/span> ProductMapper.toModelEntities(entities);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Wenn du bereits mit JPA gearbeitet hast, kommt dir das alles vermutlich verh\u00e4ltnism\u00e4\u00dfig umst\u00e4ndlich vor. Das liegt daran, dass die meisten Frameworks uns zum einen <code>EntityManager<\/code> und Transaktionsverwaltung zur Verf\u00fcgung stellen und zum anderen das Erstellen von Custom Queries, z. B. \u00fcber Annotationen, vereinfachen.<\/p>\n\n\n\n<p>In den zwei Folgeteilen dieses Tutorials wirst du sehen, wie die Repositories durch den Einsatz von Spring bzw. Quarkus deutlich weniger Boilerplate-Code enthalten werden.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">JpaCartRepository<\/h4>\n\n\n\n<p>Der f\u00fcr Warenk\u00f6rbe zust\u00e4ndige Port, <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/application\/src\/main\/java\/eu\/happycoders\/shop\/application\/port\/out\/persistence\/CartRepository.java\" target=\"_blank\">CartRepository<\/a>, definiert ebenfalls drei Methoden:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">interface<\/span> <span class=\"hljs-title\">CartRepository<\/span> <\/span>{\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">save<\/span><span class=\"hljs-params\">(Cart cart)<\/span><\/span>;\n\n  <span class=\"hljs-function\">Optional&lt;Cart&gt; <span class=\"hljs-title\">findByCustomerId<\/span><span class=\"hljs-params\">(CustomerId customerId)<\/span><\/span>;\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">deleteByCustomerId<\/span><span class=\"hljs-params\">(CustomerId customerId)<\/span><\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Den im vorherigen Teil der Serie implementierten Adapter <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/adapter\/src\/main\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/inmemory\/InMemoryCartRepository.java\" target=\"_blank\">InMemoryCartRepository<\/a> erweitern wir nun um einen JPA-Adapter, <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/adapter\/src\/main\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/jpa\/JpaCartRepository.java\" target=\"_blank\">JpaCartRepository<\/a>, wiederum im Paket <em>eu.happycoders.shop.adapter.out.persistence.jpa<\/em>.<\/p>\n\n\n\n<p>Wir injizieren wieder \u00fcber den Konstruktor eine <code>EntityManagerFactory<\/code> und implementieren <code>save(\u2026)<\/code> und <code>findByCustomerId(\u2026)<\/code> analog zu den entsprechenden Methoden im <code>ProductJpaRepository<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">JpaCartRepository<\/span> <span class=\"hljs-keyword\">implements<\/span> <span class=\"hljs-title\">CartRepository<\/span> <\/span>{\n\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> EntityManagerFactory entityManagerFactory;\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-title\">JpaCartRepository<\/span><span class=\"hljs-params\">(EntityManagerFactory entityManagerFactory)<\/span> <\/span>{\n    <span class=\"hljs-keyword\">this<\/span>.entityManagerFactory = entityManagerFactory;\n  }\n\n  <span class=\"hljs-meta\">@Override<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">save<\/span><span class=\"hljs-params\">(Cart cart)<\/span> <\/span>{\n    <span class=\"hljs-keyword\">try<\/span> (EntityManager entityManager = entityManagerFactory.createEntityManager()) {\n      entityManager.getTransaction().begin();\n      entityManager.merge(CartMapper.toJpaEntity(cart));\n      entityManager.getTransaction().commit();\n    }\n  }\n\n  <span class=\"hljs-meta\">@Override<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> Optional&lt;Cart&gt; <span class=\"hljs-title\">findByCustomerId<\/span><span class=\"hljs-params\">(CustomerId customerId)<\/span> <\/span>{\n    <span class=\"hljs-keyword\">try<\/span> (EntityManager entityManager = entityManagerFactory.createEntityManager()) {\n      CartJpaEntity cartJpaEntity =\n          entityManager.find(CartJpaEntity<span class=\"hljs-class\">.<span class=\"hljs-keyword\">class<\/span>, <span class=\"hljs-title\">customerId<\/span>.<span class=\"hljs-title\">value<\/span>())<\/span>;\n      <span class=\"hljs-keyword\">return<\/span> CartMapper.toModelEntityOptional(cartJpaEntity);\n    }\n  }\n\n  <span class=\"hljs-comment\">\/\/ method deleteByCustomerId(\u2026), see below<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Die <code>deleteByCustomerId(\u2026)<\/code>-Methode sucht zun\u00e4chst \u00fcber <code>EntityManager.find(\u2026)<\/code> den entsprechenden Warenkorb und l\u00f6scht diesen dann via <code>EntityManager.remove(\u2026)<\/code> aus der Datenbank. Der Parameter <code>cascade = CascadeType.ALL<\/code> an der <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/adapter\/src\/main\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/jpa\/CartJpaEntity.java#L25\" target=\"_blank\">@OneToMany-Annotation der CartJpaEntity<\/a> sorgt daf\u00fcr, dass alle zugeh\u00f6rigen <code>CartLineItemJpaEntity<\/code>-Datens\u00e4tze ebenfalls gel\u00f6scht werden.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@Override<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">deleteByCustomerId<\/span><span class=\"hljs-params\">(CustomerId customerId)<\/span> <\/span>{\n  <span class=\"hljs-keyword\">try<\/span> (EntityManager entityManager = entityManagerFactory.createEntityManager()) {\n    entityManager.getTransaction().begin();\n\n    CartJpaEntity cartJpaEntity =\n        entityManager.find(CartJpaEntity<span class=\"hljs-class\">.<span class=\"hljs-keyword\">class<\/span>, <span class=\"hljs-title\">customerId<\/span>.<span class=\"hljs-title\">value<\/span>())<\/span>;\n\n    <span class=\"hljs-keyword\">if<\/span> (cartJpaEntity != <span class=\"hljs-keyword\">null<\/span>) {\n      entityManager.remove(cartJpaEntity);\n    }\n\n    entityManager.getTransaction().commit();\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Damit ist auch die Implementierung der Adapter abgeschlossen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"entitymanagerfactoryfactory-und-persistence-xml\">EntityManagerFactoryFactory und persistence.xml<\/h3>\n\n\n\n<p>Wir ben\u00f6tigen noch eine weitere Klasse, um die in die Konstruktoren der Adapter injizierte <code>EntityManagerFactory<\/code> zu erzeugen. Das machen wir \u00fcber die <code>EntityManagerFactoryFactory<\/code>, die im selben Paket wie die Adapter liegt:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">EntityManagerFactoryFactory<\/span> <\/span>{\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-title\">EntityManagerFactoryFactory<\/span><span class=\"hljs-params\">()<\/span> <\/span>{}\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> EntityManagerFactory <span class=\"hljs-title\">createMySqlEntityManagerFactory<\/span><span class=\"hljs-params\">(\n      String jdbcUrl, String user, String password)<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> Persistence.createEntityManagerFactory(\n        <span class=\"hljs-string\">\"eu.happycoders.shop.adapter.out.persistence.jpa\"<\/span>,\n        Map.of(\n            <span class=\"hljs-string\">\"hibernate.dialect\"<\/span>,                 <span class=\"hljs-string\">\"org.hibernate.dialect.MySQLDialect\"<\/span>,\n            <span class=\"hljs-string\">\"hibernate.hbm2ddl.auto\"<\/span>,            <span class=\"hljs-string\">\"update\"<\/span>,\n            <span class=\"hljs-string\">\"jakarta.persistence.jdbc.driver\"<\/span>,   <span class=\"hljs-string\">\"com.mysql.jdbc.Driver\"<\/span>,\n            <span class=\"hljs-string\">\"jakarta.persistence.jdbc.url\"<\/span>,      jdbcUrl,\n            <span class=\"hljs-string\">\"jakarta.persistence.jdbc.user\"<\/span>,     user,\n            <span class=\"hljs-string\">\"jakarta.persistence.jdbc.password\"<\/span>, password));\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Durch den Map-Eintrag \u201ehibernate.hbm2ddl.auto = update\u201c werden alle ben\u00f6tigten Datenbanktabellen automatisch angelegt. Diese Einstellung ist nur f\u00fcr kleine Demo-Anwendungen wie diese und f\u00fcr Integration Tests geeignet. F\u00fcr eine produktive Anwendung empfehle ich den Einsatz von <a href=\"https:\/\/www.red-gate.com\/products\/flyway\/\" target=\"_blank\" rel=\"noopener\">Flyway<\/a> oder <a href=\"https:\/\/www.liquibase.com\/\" target=\"_blank\" rel=\"noopener\">Liquibase<\/a>.<\/p>\n\n\n\n<p>F\u00fcr JPA ben\u00f6tigen wir au\u00dferdem noch eine <em>persistence.xml<\/em>-Klasse im <em>src\/main\/resources\/META-INF<\/em>-Verzeichnis des <em>adapter<\/em>-Moduls mit folgendem Inhalt:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">persistence<\/span> <span class=\"hljs-attr\">xmlns<\/span>=<span class=\"hljs-string\">\"https:\/\/jakarta.ee\/xml\/ns\/persistence\"<\/span> <span class=\"hljs-attr\">version<\/span>=<span class=\"hljs-string\">\"3.0\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">persistence-unit<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"eu.happycoders.shop.adapter.out.persistence.jpa\"<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">class<\/span>&gt;<\/span>eu.happycoders.shop.adapter.out.persistence.jpa.CartJpaEntity<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">class<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">class<\/span>&gt;<\/span>eu.happycoders.shop.adapter.out.persistence.jpa.CartLineItemJpaEntity<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">class<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">class<\/span>&gt;<\/span>eu.happycoders.shop.adapter.out.persistence.jpa.ProductJpaEntity<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">class<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">exclude-unlisted-classes<\/span>&gt;<\/span>true<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">exclude-unlisted-classes<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">persistence-unit<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">persistence<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Beachte, dass der Name der Persistence Unit in der XML-Datei, \u201eeu.happycoders.shop.adapter.out.persistence.jpa\u201c, exakt derjenige ist, der in der <code>EntityManagerFactoryFactory<\/code> als erster Parameter an die Methode <code>Persistence.createEntityManagerFactory(\u2026)<\/code> \u00fcbergeben wird.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"integrationstests-fuer-jpa-adapter\">Integrationstests f\u00fcr JPA-Adapter<\/h2>\n\n\n\n<p>Im Abschnitt <a href=\"\/de\/software-craftsmanship\/hexagonale-architektur-java\/#unit-tests-fuer-persistenz-adapter\">Unit-Tests f\u00fcr Persistenz-Adapter<\/a> des vorherigen Teils der Serie habe ich die Aufteilung der Testklassen in die abstrakten Klassen <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/adapter\/src\/test\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/AbstractProductRepositoryTest.java\" target=\"_blank\">AbstractProductRepositoryTest<\/a> und <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/adapter\/src\/test\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/AbstractCartRepositoryTest.java\" target=\"_blank\">AbstractCartRepositoryTest<\/a> sowie die konkreten Klassen <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/adapter\/src\/test\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/inmemory\/InMemoryProductRepositoryTest.java\" target=\"_blank\">InMemoryProductRepositoryTest<\/a> und <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/adapter\/src\/test\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/inmemory\/InMemoryCartRepositoryTest.java\" target=\"_blank\">InMemoryCartRepositoryTest<\/a> erl\u00e4utert: Die abstrakten Klassen implementieren alle Testf\u00e4lle, und die konkreten Klassen erzeugen die jeweiligen Instanzen der JPA-Adapter-Klassen.<\/p>\n\n\n\n<p>Hier noch einmal das zugeh\u00f6rige Klassendiagramm:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"428\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-800x428.png\" alt=\"Ports and Adapters: Hierarchie der JPA-Adapter-Testklassen\" class=\"wp-image-36659\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-800x428.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-224x120.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-336x180.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-504x270.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-672x360.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-400x214.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-600x321.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-944x505.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-1200x642.png 1200w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram.png 1600w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Hierarchie der JPA-Adapter-Testklassen<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Um die zwei neuen JPA-Adapter zu testen, m\u00fcssen wir nur noch die Klassen <code>JpaProductRepositoryTest<\/code> und <code>JpaCartRepositoryTest<\/code> hinzuf\u00fcgen und die in den abstrakten Basisklassen definierten abstrakten Methoden <code>createProductRepository()<\/code> bzw. <code>createCartRepository()<\/code> implementieren.<\/p>\n\n\n\n<p>Hier ist ein Screenshot aller Adapter-Test-Klassen in meiner IDE:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-half_400\"><img decoding=\"async\" width=\"400\" height=\"356\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-java-adapter-test-module-400x356.png\" alt=\"Ports and Adapters mit Java - Adapter-Test-Klassen\" class=\"wp-image-37624\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-java-adapter-test-module-400x356.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-java-adapter-test-module-224x199.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-java-adapter-test-module-336x299.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-java-adapter-test-module-504x449.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-java-adapter-test-module-672x598.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-java-adapter-test-module-600x534.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-java-adapter-test-module.png 800w\" sizes=\"(max-width: 400px) 100vw, 400px\" \/><\/figure>\n<\/div>\n\n\n<p>Im n\u00e4chsten Abschnitt zeige ich dir die Implementierung eines der zwei JPA-Repository-Adapter. Der andere Adapter wird analog implementiert.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"integrationstests-fuer-jpaproductrepository-adapter\">Integrationstests f\u00fcr JpaProductRepository-Adapter<\/h3>\n\n\n\n<p>Als neue Dependency hatten wir bisher lediglich die Jakarta Persistence API in die <em>pom.xml<\/em>-Datei des <em>adapter<\/em>-Moduls aufgenommen. Um die Integrationstests auszuf\u00fchren, ben\u00f6tigen wir nun auch Hibernate als konkrete Implementierung dieser API.<\/p>\n\n\n\n<p>Dazu f\u00fcgen wir folgende Abh\u00e4ngigkeiten im Test-Scope hinzu:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.hibernate.orm<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>hibernate-core<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>6.3.1.Final<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.hibernate.validator<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>hibernate-validator<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>8.0.1.Final<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.glassfish<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>jakarta.el<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>5.0.0-M1<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>mysql<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>mysql-connector-java<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>8.0.33<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.testcontainers<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>mysql<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>1.19.0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><em>hibernate-core<\/em> ist die eigentliche Hibernate-Library. Diese ben\u00f6tigt zur Laufzeit noch <em>hibernate-validator<\/em>, <em>jakarta.el<\/em> und einen JDBC-Treiber \u2013 als solchen verwenden wir <em>mysql-connector-java<\/em>. <\/p>\n\n\n\n<p>Die letzte Dependency, <em>org.testcontainers:mysql<\/em>, erlaubt es uns, wie du gleich sehen wirst, sehr komfortabel aus den Integrationstests heraus per Docker eine MySQL-Datenbank zu starten.<\/p>\n\n\n\n<p>Den <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/adapter\/src\/test\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/jpa\/JpaProductRepositoryTest.java\" target=\"_blank\">JpaProductRepositoryTest<\/a> implementieren wir dann wie folgt, im Paket <em>eu.happycoders.shop.adapter.out.persistence.jpa<\/em>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">JpaProductRepositoryTest<\/span>\n    <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">AbstractProductRepositoryTest<\/span>&lt;<span class=\"hljs-title\">JpaProductRepository<\/span>&gt; <\/span>{\n\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> MySQLContainer&lt;?&gt; mysql;\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> EntityManagerFactory entityManagerFactory;\n\n  <span class=\"hljs-meta\">@BeforeAll<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">startDatabase<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    mysql = <span class=\"hljs-keyword\">new<\/span> MySQLContainer&lt;&gt;(DockerImageName.parse(<span class=\"hljs-string\">\"mysql:8.1\"<\/span>));\n    mysql.start();\n\n    entityManagerFactory =\n        EntityManagerFactoryFactory.createMySqlEntityManagerFactory(\n            mysql.getJdbcUrl(), <span class=\"hljs-string\">\"root\"<\/span>, <span class=\"hljs-string\">\"test\"<\/span>);\n  }\n\n  <span class=\"hljs-meta\">@Override<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">protected<\/span> JpaProductRepository <span class=\"hljs-title\">createProductRepository<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> JpaProductRepository(entityManagerFactory);\n  }\n\n  <span class=\"hljs-meta\">@AfterAll<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">stopDatabase<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    entityManagerFactory.close();\n    mysql.stop();\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In der mit <code>@BeforeAll<\/code> annotierten Methode <code>startDatabase()<\/code> starten wir in den ersten zwei Zeilen die MySQL-Datenbank \u00fcber die <a rel=\"noopener\" href=\"https:\/\/java.testcontainers.org\/modules\/databases\/jdbc\/\" target=\"_blank\">Testcontainers-JDBC-API<\/a>.<\/p>\n\n\n\n<p>Dann erstellen wir \u00fcber unsere im vorherigen Abschnitt erstellte <code>EntityManagerFactoryFactory<\/code> eine <code>EntityManagerFactory<\/code> und speichern diese im Feld <code>entityManagerFactory<\/code>. Benutzername und Passwort sind die Standardwerte des <a rel=\"noopener\" href=\"https:\/\/java.testcontainers.org\/modules\/databases\/mysql\/\" target=\"_blank\">Testcontainers-MySQL-Moduls<\/a>.<\/p>\n\n\n\n<p>Die aus der abstrakten Elternklasse \u00fcberschriebene <code>createProductRepository()<\/code>-Methode muss dann blo\u00df noch eine Instanz von <code>JpaProductRepository<\/code> erzeugen und dabei dem Konstruktor die zuvor erstellte <code>EntityManagerFactory<\/code>-Instanz \u00fcbergeben.<\/p>\n\n\n\n<p>In der mit <code>@AfterAll<\/code> annotierten <code>stopDatabase()<\/code>-Methode schlie\u00dfen wir die <code>EntityManagerFactory<\/code> und beenden den MySQL-Docker-Container.<\/p>\n\n\n\n<p>Die Testklasse <a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/adapter\/src\/test\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/jpa\/JpaCartRepositoryTest.java\" target=\"_blank\" rel=\"noopener\">JpaCartRepositoryTest<\/a> wird analog dazu implementiert. <\/p>\n\n\n\n<p>Du solltest jetzt die Tests direkt aus deiner IDE oder mit folgendem Aufruf aus einem Terminal heraus ausf\u00fchren k\u00f6nnen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">mvn clean test<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"anpassung-des-bootstrap-moduls\">Anpassung des Bootstrap-Moduls<\/h2>\n\n\n\n<p>Unsere JPA-Adapter sind bereit. Nun m\u00fcssen wir noch das <em>bootstrap<\/em>-Modul dahingehend anpassen, dass wir \u2013 wie in der Einf\u00fchrung angek\u00fcndigt \u2013 per System Property festlegen k\u00f6nnen, ob die alten In-Memory-Adapter oder die neuen JPA-Adapter eingesetzt werden.<\/p>\n\n\n\n<p>Dazu m\u00fcssen wir zun\u00e4chst die folgenden Abh\u00e4ngigkeiten in die <em>pom.xml<\/em> des <em>bootstrap<\/em>-Moduls einf\u00fcgen. Es handelt sich um die gleichen Abh\u00e4ngigkeiten, wie wir sie im <em>adapter<\/em>-Modul im Test-Scope hinzugef\u00fcgt haben:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-26\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.hibernate.orm<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>hibernate-core<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>6.3.1.Final<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>runtime<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.hibernate.validator<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>hibernate-validator<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>8.0.1.Final<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>runtime<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.glassfish<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>jakarta.el<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>5.0.0-M1<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>runtime<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>mysql<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>mysql-connector-java<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>8.0.33<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>runtime<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-26\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Im GitHub-Repository habe ich die Versionen \u00fcbrigens zentral in einem <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/pom.xml#L69\" target=\"_blank\">Dependency-Management-Block in der pom.xml im Root-Verzeichnis<\/a> definiert, nicht in den <em>pom.xml<\/em>-Dateien der Module. Dieser Artikel w\u00e4re allerdings un\u00fcbersichtlicher geworden, wenn ich f\u00fcr jede Dependency zwei Eintr\u00e4ge gemacht h\u00e4tte.<\/p>\n\n\n\n<p>Leider gibt es momentan noch eine kleine Inkompatiblit\u00e4t: Sowohl <em>resteasy-undertow<\/em> als auch <em>hibernate-core<\/em> haben eine Dependency auf die <em>jandex<\/em>-Library \u2013 allerdings mit zwei unterschiedlichen Group-IDs, sodass Maven beide Libraries in den Classpath aufnimmt. Das k\u00f6nnen wir verhindern, indem wir der bestehenden Dependency zu <em>resteasy-undertow<\/em> einen <code>exclusion<\/code>-Block hinzuf\u00fcgen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-27\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.jboss.resteasy<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>resteasy-undertow<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">exclusions<\/span>&gt;<\/span>\n        <span class=\"hljs-comment\">&lt;!-- Conflicts with io.smallrye:jandex, a dependency from Hibernate --&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">exclusion<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.jboss<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>jandex<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">exclusion<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">exclusions<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-27\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Zuletzt m\u00fcssen wir nur noch die <code>initPersistenceAdapters()<\/code>-Methode der <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/main\/bootstrap\/src\/main\/java\/eu\/happycoders\/shop\/bootstrap\/RestEasyUndertowShopApplication.java\" target=\"_blank\">RestEasyUndertowShopApplication<\/a> anpassen.<\/p>\n\n\n\n<p>Aktuell sieht diese noch wie folgt aus:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-28\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">initPersistenceAdapters<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n  cartRepository = <span class=\"hljs-keyword\">new<\/span> InMemoryCartRepository();\n  productRepository = <span class=\"hljs-keyword\">new<\/span> InMemoryProductRepository();\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-28\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Wir \u00e4ndern sie wie folgt ab:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-29\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">initPersistenceAdapters<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n  String persistence = System.getProperty(<span class=\"hljs-string\">\"persistence\"<\/span>, <span class=\"hljs-string\">\"inmemory\"<\/span>);\n  <span class=\"hljs-keyword\">switch<\/span> (persistence) {\n    <span class=\"hljs-keyword\">case<\/span> <span class=\"hljs-string\">\"inmemory\"<\/span> -&gt; initInMemoryAdapters();\n    <span class=\"hljs-keyword\">case<\/span> <span class=\"hljs-string\">\"mysql\"<\/span> -&gt; initMySqlAdapters();\n    <span class=\"hljs-keyword\">default<\/span> -&gt; <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> IllegalArgumentException(\n        <span class=\"hljs-string\">\"Invalid 'persistence' property: '%s' (allowed: 'inmemory', 'mysql')\"<\/span>\n            .formatted(persistence));\n  }\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">initInMemoryAdapters<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n  cartRepository = <span class=\"hljs-keyword\">new<\/span> InMemoryCartRepository();\n  productRepository = <span class=\"hljs-keyword\">new<\/span> InMemoryProductRepository();\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">initMySqlAdapters<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n  EntityManagerFactory entityManagerFactory =\n      EntityManagerFactoryFactory.createMySqlEntityManagerFactory(\n          <span class=\"hljs-string\">\"jdbc:mysql:\/\/localhost:3306\/shop\"<\/span>, <span class=\"hljs-string\">\"root\"<\/span>, <span class=\"hljs-string\">\"test\"<\/span>);\n\n  cartRepository = <span class=\"hljs-keyword\">new<\/span> JpaCartRepository(entityManagerFactory);\n  productRepository = <span class=\"hljs-keyword\">new<\/span> JpaProductRepository(entityManagerFactory);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-29\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Wir lesen zun\u00e4chst die System Property \u201epersistence\u201c aus, wobei wir den Wert \u201einmemory\u201c als Default-Wert festlegen. Je nach gesetztem Wert rufen wir dann entweder <code>initInMemoryAdapters()<\/code> oder <code>initMySqlAdapters()<\/code> auf.<\/p>\n\n\n\n<p>Die Method <code>initInMemoryAdapters()<\/code> entspricht der vorherigen <code>initPersistenceAdapters()<\/code>-Methode und erzeugt die In-Memory-Adapter.<\/p>\n\n\n\n<p>Die Methode <code>initMySqlAdapters()<\/code> erzeugt \u2013 wie wir es in den Integrationstests gemacht haben \u2013 \u00fcber die <code>EntityManagerFactoryFactory<\/code> eine <code>EntityManagerFactory<\/code> und \u00fcbergibt diese an die Konstruktoren von <code>JpaCartRepository<\/code> und <code>JpaProductRepository<\/code>.<\/p>\n\n\n\n<p>Connection-URL, Benutzername und Passwort habe ich hier hart kodiert \u2013 das sollte man in einer produktiven Anwendung nat\u00fcrlich nicht so machen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"start-der-anwendung\">Start der Anwendung<\/h2>\n\n\n\n<p>Du kannst die Anwendung wieder direkt aus deiner IDE starten, indem du die <code>Launcher<\/code>-Klasse des <em>bootstrap<\/em>-Moduls startest. Wenn du keine weiteren Einstellungen \u00e4nderst, wird die Anwendung weiterhin im In-Memory-Modus gestartet.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"start-einer-lokalen-mysql-datenbank\">Start einer lokalen MySQL-Datenbank<\/h3>\n\n\n\n<p>Um die Anwendung im MySQL-Modus zu starten, musst du zun\u00e4chst eine lokale MySQL-Datenbank starten. Das geschieht am einfachsten \u00fcber Docker, z. B. \u00fcber das folgende Kommando:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-30\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">docker run --name hexagon-mysql -d -p3306:3306 \\\n    -e MYSQL_DATABASE=shop -e MYSQL_ROOT_PASSWORD=test mysql:8.1\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-30\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Falls dein lokaler Port 3306 bereits durch eine andere MySQL-Installation belegt ist, verwende einfach einen anderen freien Port, z. B. Port 3307, indem du <code>-p3306:3306<\/code> durch <code>-p3307:3306<\/code> ersetzt. Du musst dann den Port in der <code>initMySqlAdapters()<\/code>-Methode der <code>RestEasyUndertowShowApplication<\/code>-Klasse entsprechend anpassen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"start-der-anwendung-im-mysql-modus\">Start der Anwendung im MySQL-Modus<\/h3>\n\n\n\n<p>Um schlie\u00dflich auch die Anwendung im MySQL-Modus zu starten, muss du die entsprechende System Property setzen. In IntelliJ geht das beispielsweise wie folgt:<\/p>\n\n\n\n<p>1. Klicke auf das gr\u00fcne Start-Icon der Launcher-Klasse und dann auf \u201eModify Run Configuration...\u201c:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-half_600\"><img decoding=\"async\" width=\"600\" height=\"216\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-modify-launcher-config-600x216.png\" alt=\"Start der Ports-and-Adapters-Anwendung - \u00c4nderung der Konfiguration\" class=\"wp-image-37655\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-modify-launcher-config-600x216.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-modify-launcher-config-224x81.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-modify-launcher-config-336x121.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-modify-launcher-config-504x181.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-modify-launcher-config-672x242.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-modify-launcher-config-400x144.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-modify-launcher-config-800x288.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-modify-launcher-config-944x340.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-modify-launcher-config.png 1200w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/figure>\n<\/div>\n\n\n<p>2. Klicke in dem sich \u00f6ffnenen Dialog entweder auf \u201eModify options\u201c und dann auf \u201eAdd VM options\u201c \u2013 oder dr\u00fccke Alt+V. Dadurch wird das Eingabefeld \u201eVM options\u201c sichtbar. Trage dort <nobr>\u201e-Dersistence=mysql\u201c<\/nobr> ein:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img decoding=\"async\" width=\"1200\" height=\"896\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-add-system-property.png\" alt=\"Start der Ports-and-Adapters-Anwendung - Setzen der System Property\" class=\"wp-image-37656\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-add-system-property.png 1200w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-add-system-property-224x167.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-add-system-property-336x251.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-add-system-property-504x376.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-add-system-property-672x502.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-add-system-property-400x299.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-add-system-property-600x448.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-add-system-property-800x597.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-add-system-property-944x705.png 944w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n<\/div>\n\n\n<p>3. Klick auf \u201eOK\u201c und starte danach die Anwendung. Was ich noch gerne mache, ist im \u201eModify options\u201c-Dropdown die letzte Option \u201eShow the run\/debug configuration settings before start\u201c zu aktivieren. Dann \u00f6ffnet sich das \u201eEdit Run Configuration\u201c-Fenster bei jedem Start der Anwendung, sodass man die Systemeigenschaft bei jedem Start sehen und ggf. \u00e4ndern kann.<\/p>\n\n\n\n<p>Wenn die Anwendung l\u00e4uft, kannst du ihre Endpunkte \u2013 so wie <a href=\"\/de\/software-craftsmanship\/hexagonale-architektur-java\/#start-der-anwendung\">im Abschnitt \u201eStart der Anwendung\u201c des vorherigen Teils<\/a> beschrieben \u2013 testen. Im Gegensatz zur In-Memory-L\u00f6sung sollten jetzt nach einem Neustart der Anwendung alle Daten erhalten bleiben.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"fazit-und-ausblick\">Fazit und Ausblick<\/h2>\n\n\n\n<p>Wir haben in diesem Teil des Tutorials eine nach der Ports-und-Adapters-Architektur entwickelte Anwendung um einen Datenbank-Adapter erweitert. Neben der Implementierung der Entity-, Mapper- und Adapter-Klassen im <em>adapter<\/em>-Modul mussten wir lediglich ein paar Zeilen Code im <em>bootstrap<\/em>-Modul hinzuf\u00fcgen, um die neuen Adapter-Klassen zu instanziieren. <\/p>\n\n\n\n<p>Im Kern der Anwendung, also in den <em>model<\/em>- und <em>application<\/em>-Klassen mussten wir nicht eine einzige Zeile Code \u00e4ndern \u2013 eine beeindruckende Eigenschaft der Ports-und-Adapters-Architektur!<\/p>\n\n\n\n<p>Nun haben wir zwar eine lauff\u00e4hige Anwendung \u2013 wir haben allerdings bereits festgestellt, dass das Erzeugen der <code>EntityManager<\/code> und das Transaktionsmanagement ziemlich viel Boilerplate-Code erforderten. Und wenn wir unsere Anwendung \u201eproduction-ready\u201c machen wollen, m\u00fcssten wir die Anwendung besser konfigurierbar und robuster machen und br\u00e4uchten auch noch Health- und Metrics-Endpoints. Da w\u00fcrde es ohne Application Framework langsam umst\u00e4ndlich werden.<\/p>\n\n\n\n<p>Daher zeige ich dir in den n\u00e4chsten zwei Teilen der Artikelserie, wie wir <a href=\"\/de\/software-craftsmanship\/hexagonale-architektur-quarkus\/\">unsere Applikation in eine Quarkus-Anwendung umwandeln<\/a> und danach <a href=\"\/de\/software-craftsmanship\/hexagonale-architektur-spring-boot\/\">in eine Spring-Anwendung<\/a>. Und damit werde ich \u2013 hoffentlich eindrucksvoll \u2013 demonstrieren, dass bei der hexgonalen Architektur selbst das Application Framework nur ein austauschbares technisches Detail ist.<\/p>\n<aside><p>Wenn dir der Artikel weitergeholfen hat, w\u00fcrde ich mich sehr \u00fcber eine positive Bewertung auf meinem <a href=\"https:\/\/www.provenexpert.com\/de-de\/sven-woltmann-happycoders-eu\/7smk\/\" target=\"_blank\" rel=\"noopener\">ProvenExpert-Profil<\/a> freuen. Dein Feedback hilft mir, meine Inhalte weiter zu verbessern und motiviert mich, neue informative Artikel zu schreiben.<\/p>\r\n                        <p>\ud83d\udc49 <a href=\"https:\/\/www.provenexpert.com\/de-de\/sven-woltmann-happycoders-eu\/7smk\/\" target=\"_blank\" rel=\"noopener\">Bewertung abgeben<\/a><\/p>\r\n                        <p>M\u00f6chtest du auf dem Laufenden bleiben und informiert werden, wenn neue Artikel auf HappyCoders.eu ver\u00f6ffentlicht werden? Dann <a href=\"#\" data-formkit-toggle=\"d8ee997126\">klicke hier<\/a>, um dich f\u00fcr den HappyCoders-Newsletter anzumelden.<\/p>\r\n                        <p>\ud83d\udc49 <a href=\"#\" data-formkit-toggle=\"d8ee997126\">Newsletter-Anmeldung<\/a><\/p><\/aside>","protected":false},"excerpt":{"rendered":"<p>Im dritten Teil der Serie \u00fcber hexagonale Architektur erweitern wir die Anwendung um einen JPA-Adapter, der die Daten in einer MySQL-Datenbank speichert.<\/p>\n","protected":false},"author":1,"featured_media":37747,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_seopress_robots_primary_cat":"","_seopress_titles_title":"Ports and Adapters Java-Tutorial: Datenbankadapter","_seopress_titles_desc":"Im dritten Teil der Serie \u00fcber hexagonale Architektur erweitern wir die Anwendung um einen JPA-Adapter, der die Daten in einer MySQL-Datenbank speichert.","_seopress_robots_index":"","_uag_custom_page_level_css":"","_wp_convertkit_post_meta":{"form":"-1","landing_page":"","tag":"0","restrict_content":"0"},"_metis_text_type":"standard","_metis_text_length":34619,"_post_count":0,"footnotes":""},"categories":[204],"tags":[209],"class_list":["post-36799","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software-craftsmanship","tag-hexagonale-architektur"],"uagb_featured_image_src":{"full":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature.jpg",1770,986,false],"thumbnail":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature.jpg",150,84,false],"medium":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature.jpg",300,167,false],"medium_large":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature.jpg",768,428,false],"large":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature.jpg",1024,570,false],"feature_thumb_224":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature-224x125.jpg",224,125,true],"feature_thumb_336":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature-336x187.jpg",336,187,true],"feature_thumb_504":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature-504x281.jpg",504,281,true],"feature_thumb_672":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature-672x374.jpg",672,374,true],"half_400":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature-400x223.jpg",400,223,true],"half_600":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature-600x334.jpg",600,334,true],"full_800":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature-800x446.jpg",800,446,true],"full_944":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature-944x526.jpg",944,526,true],"full_1200":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature-1200x668.jpg",1200,668,true],"wide_1180":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature-1180x490.jpg",1180,490,true],"wide_1770":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature-1770x735.jpg",1770,735,true],"1536x1536":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature.jpg",1536,856,false],"2048x2048":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/09\/ports-and-adapters-feature.jpg",1770,986,false]},"uagb_author_info":{"display_name":"Sven Woltmann","author_link":"https:\/\/www.happycoders.eu\/de\/author\/sven\/"},"uagb_comment_info":0,"uagb_excerpt":"Im dritten Teil der Serie \u00fcber hexagonale Architektur erweitern wir die Anwendung um einen JPA-Adapter, der die Daten in einer MySQL-Datenbank speichert.","public_identification_id":"04ed7b622f9e43b1819df9160210ec1a","private_identification_id":"3424b46c323040518c9cd9c932805f80","_links":{"self":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/36799","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/comments?post=36799"}],"version-history":[{"count":10,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/36799\/revisions"}],"predecessor-version":[{"id":52452,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/36799\/revisions\/52452"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/media\/37747"}],"wp:attachment":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/media?parent=36799"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/categories?post=36799"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/tags?post=36799"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}