Datenbankzentrische Anwendungen mit Spring Boot und jOOQ - Michael J. Simons 2016-09-21 - DOAG
←
→
Transkription von Seiteninhalten
Wenn Ihr Browser die Seite nicht korrekt rendert, bitte, lesen Sie den Inhalt der Seite unten
Table of Contents Stichworte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Einleitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Das Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Spring Boot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 jOOQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Oracle JET. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 Spring Boot, jOOQ und Oracle Jet: Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 NetBeans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Bonus: Oracle Docker Images und Maven Repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 Ressourcen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 Kontakt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Stichworte Spring Boot, jOOQ, Oracle JET, Flyway, SQL, NetBeans, Docker Einleitung In diesem Vortrag wird eine Variante datenbankzentrischer Anwendungen mit einer modernen Architektur vorgestellt, die sowohl in einer klassischen Verteilung als auch "cloud native" genutzt werden kann und dabei eine sehr direkte Interaktion mit Datenbanken erlaubt. jOOQ ist eine von vielen Möglichkeiten, Datenbankzugriff in Java zu realisieren, aber weder eine Objektrelationale Abbildung (ORM) noch "Plain SQL", sondern eine typsichere Umsetzung aktueller SQL Standards in Java. jOOQ "schützt" den Entwickler nicht vor SQL Code, sondern unterstützt ihn dabei, typsicher Abfragen in Java zu schreiben. Spring Boot setzt seit 2 Jahren neue Standards im Bereich der Anwendungsentwicklung mit dem Spring Framework. Waren vor wenigen Jahren noch aufwändige XML Konfigurationen notwendig, ersetzen heute "opinionated defaults" manuelle Konfiguration. Eine vollständige Spring Boot Anwendung passt mittlerweile in einen Tweet. Viele bestehende System lassen sich nicht ohne größere Schmerzen in die schöne, neue Welt aus Microservices überführen, sei es aus menschlichen oder technischen Gründen. Vor 15 Jahren war es erheblich einfacher als heute, einen "Fullstack Developer" zu finden, aber wie oft wurde in den vergangenen 2 bis 5 Jahren Jahren das Einhorn gesichtet, dass sowohl mit komplexen Datenbankschemata und Abfragen als auch mit dem gerade aktuellen JavaScript Framework für das Frontend auskennt? Da nach wie vor in großen Anwendungen Datenstrukturen und Daten die am wenigsten oft ausgetauschten Komponenten eines System wird in diesen System die Datenbank integrierender Bestandteil sein. Es wäre fatal, Wissen über die Daten, Zugriffsmethoden und Abfragen "verkommen" zu lassen. 1
Das Beispiel Seit 2002 gibt es das Last.fm Portal. Dort konnte und kann man Musik "scrobbeln". Als Scrobbeln bezeichnet man die Speicherung von Interpret, Album und Titel eines gerade gehörten Musikstückes. Auf meinem täglichen Fotoprojekt Daily Fratze habe ich seit 2005 eine ähnliche Funktion implementiert. Das Datenmodel ist seitdem konstant gewesen: Nicht kompliziert, eher einfach, aber vollkommen ausreichend. Das Datenmodel ist sogar eines, das sich sehr leicht in eine Objektrelationale Abbildung übersetzen lässt, aber kann man damit dann dieses Ergebnis in weniger als 5 SQL Statements erzeugen? Man kann. Die Daten der drei Charts werden jeweils mit einem MySQL Statement selektiert, das im Gegensatz zum Datenbankmodel furchtbar kompliziert ist und trotz Hibernate als Native Query hinterlegt ist. Das Ziel ist nun, diese Daten so über die Datenbank bereitzustellen, dass • das Selectstatement verständlich bleibt ("Modern SQL") • das Statement während der Kompilierung überprüft wird • die erzeugten Daten nicht ans Frontend gekoppelt sind 2
Spring Boot . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.4.1.RELEASE) Was ist Spring Boot? Spring Boot ist kein neues Framework. Spring Boot bedient sich des bekannten und bewährten Spring Frameworks und des darum entstandenen Ökosystems, um "fertig konfigurierte Instanzen" des Frameworks als autonome, self-contained Anwendungen zu erzeugen. Spring Boot selber besteht im wesentliche aus sogenannten spring-boot-starters, die die für den jeweiligen Zweck nötigen Konfigurationsklassen und Abhängigkeiten beinhalten. Auch für jOOQ gibt es einen passenden Starter. Eine neue Spring Boot Anwendung anlegen Eine vollständige Anwendung kann in wenigen Augenblicken erstellt bzw. generiert werden. Entweder kann manuell ein Buildfile (entweder für Maven oder Gradle) geschrieben oder ein vollständiges Projekt über den Spring Initializer unter start.spring.io generiert werden: 4.0.0 ac.simons doag2016 0.0.1-SNAPSHOT jar #DOAG2016 org.springframework.boot spring-boot-starter-parent 1.4.0.RELEASE UTF-8 3
UTF-8 1.8 org.springframework.boot spring-boot-starter-jooq org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-devtools true com.h2database h2 runtime org.springframework.boot spring-boot-maven-plugin Zusammen mit dieser Klasse ist eine vollständige Java Anwendung fertig, inklusive einer konfigurierten In-Memory Datenbank: 4
package ac.simons.doag2016; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String... args) { SpringApplication.run(Application.class, args); } } Das entstehende "fat jar" ist knapp 18Mb groß, beinhaltet aber bereits ein eingebetteten Tomcat als Servletcontainer sowie das Spring Framework. Fazit Mit Spring Boot wird das Spring Framework extrem zugänglich. Sowohl für Entwickler, die sich sehr lange nicht im Java Umfeld bewegt, zum Beispiel lange im Bereich PL/SQL oder Forms Entwicklung tätig waren, als auch für Menschen, die noch nicht lange im Alltag mit Softwareentwicklung zu tun haben. Da der Fokus nicht mehr auf einer aufwändigen Konfiguration liegt, kann man sich auf interessante Fragen konzentrieren: Auf Fachlichkeit, Softwarearchitektur, Datenstrukturen oder auch darauf, modernes Java oder SQL zu lernen! 5
jOOQ Was ist jOOQ? Obiges Datenmodel kann mit wenigen Zeilen Code auf JPA Entitäten abgebildet werden. Möchte ich dann aber eine Abfrage erstellen, die sowohl eine Common Table Expression (CTE) als auch mehrere Windowfunctions enthält, funktioniert das garantiert nicht mit der JPA Query Language (JPQL), höchsten mit nativen Queries und aus dieser Query WITH previous_month AS (SELECT p.track_id, count(*) as cnt, dense_rank() over(order by count(*) desc) as position FROM plays p WHERE trunc(p.played_on, 'DD') between date'2016-04-01' and date'2016-04-30' GROUP BY p.track_id), current_month AS (SELECT p.track_id, count(*) as cnt, dense_rank() over(order by count(*) desc) as position FROM plays p WHERE trunc(p.played_on, 'DD') between date'2016-05-01' and date'2016-05-31' GROUP BY p.track_id) SELECT a.artist || ' - ' || t.name || ' (' || t.album || ')' as label, current_month.cnt, previous_month.position - current_month.position as change FROM tracks t JOIN artists a on a.id = t.artist_id JOIN current_month current_month on current_month.track_id = t.id LEFT OUTER join previous_month on previous_month.track_id = t.id ORDER BY current_month.cnt desc, label asc FETCH FIRST 20 ROWS ONLY; Eine Möglichkeit, diese Abfrage in JPA / Hibernate abzubilden: @Entity @SqlResultSetMapping( name = "ChartMapping", columns = { @ColumnResult(name = "label", type = String.class), @ColumnResult(name = "cnt", type = Integer.class), @ColumnResult(name = "chage", type = Integer.class) }) @NamedNativeQueries( @NamedNativeQuery( name = "ChartQuery", resultSetMapping = "ChartMapping", query = "" 6
+ "WITH \n" + " previous_month AS\n" + " (SELECT p.track_id, count(*) as cnt, \n" + " dense_rank() over(order by count(*) desc) as position \n" + " FROM plays p \n" + " WHERE trunc(p.played_on, 'DD') between date'2016-04-01' and date'2016-04-30' GROUP BY p.track_id),\n" + " current_month AS\n" + " (SELECT p.track_id, count(*) as cnt, \n" + " dense_rank() over(order by count(*) desc) as position \n" + " FROM plays p \n" + " WHERE trunc(p.played_on, 'DD') between date'2016-05-01' and date'2016-05-31' GROUP BY p.track_id)\n" + "SELECT a.artist || ' - ' || t.name || ' (' || t.album || ')' as label,\n" + " current_month.cnt, \n" + " previous_month.position - current_month.position as change\n" + " FROM tracks t\n" + " JOIN artists a on a.id = t.artist_id\n" + " JOIN current_month current_month on current_month.track_id = t.id\n" + " LEFT OUTER join previous_month on previous_month.track_id = t.id\n" + " ORDER BY current_month.cnt desc, label asc" ) ) public class PlayEntity { public static void main(String... a) { // Don't do this at home EntityManager entityManager; List results = entityManager.createNamedQuery("ChartQuery" ).setMaxResults(20).getResultList(); results.stream().forEach((record) -> { String label = (String) record[0]; Integer cnt = (Integer) record[1]; Integer change = (Integer) record[2]; }); } } SQL love no more… Das Ziel von jOOQ ist nicht, obigen SQL Code zu verstecken, im Gegenteil. jOOQ ist in erster Linie ein Codegenerator, aus dem Datenbankschema Zugriffsklassen für eine Domain Specific Language (DSL) generiert. Diese DSL beinhaltet in erster Linie nur die Fachlichkeit, die durch das Datenmodel vorgeben ist und keine Architekturvorgabe. Ihr Ziel ist es, SQL zu schreiben. Obige Abfrage typsicher mit jOOQ, keine Annotationen: 7
this.create .with(currentMonth) .with(previousMonth) .select(label, currentMonth.field("cnt"), previousMonth.field("position").minus( currentMonth.field("position") ).as("change") ) .from(TRACKS) .join(ARTISTS).onKey() .join(currentMonth).on(currentMonth.field("track_id", BigDecimal.class).eq (TRACKS.ID)) .leftOuterJoin(previousMonth).on(previousMonth.field("track_id", BigDecimal .class).eq(TRACKS.ID)) .orderBy(currentMonth.field("cnt").desc(), label.asc()) .limit(n) .fetch() .formatJSON(response.getOutputStream()); Architekturstil jOOQ zielt im Auge des Autors auf eine SQL-basierte 2-Schichtenarchitektur. Es ist möglich, das Repository-Muster anzuwenden, aber die Erfahrung spricht dagegen. jOOQ spielt seine Stärken dann aus, wenn es um die Modellierung komplexer Abfrage geht, nicht in möglichst hoher Abstraktion. jOOQ im Buildprozess jOOQ wird in einem frühen Stadium in den Buildprozess eingebunden, im Falle von Maven in der generate-sources Phase. Es kann konfiguriert werden, welche Datenbankobjekte (jOOQ unterstützt Tabellen, Prozeduren, Funktionen, Packages und mehr) betrachtet werden, welches Zielverzeichnis genutzt wird und welche zusätzliche POJOs und Dataaccess Objekte generiert werden. Kann man auf der "grünen Wiese" anfangen oder hat generell die Möglichkeit, Hilfsmittel zur Datenbankmigration anzuwenden, kann zum Beispiel dem Generierungsprozess eine Migration mit Flyway vorgeschaltet werden. Flyway wird ebenfalls von Spring Boot unterstützt, so dass eine konsistente Version von Entwicklungs- und Produktionsdatenbank sichergestellt werden kann. jOOQ und Spring Boot Obiges Projekt funktioniert ohne weiteres zutun mit den im Buildprozess generierten Klassen. Die in Spring Boot konfigurierte Datenbankverbindung wird automatisch einem jOOQ Connection Provider zur Verfügung gestellt. 8
Weitere, sehr detaillierte Konfiguration ist problemlos möglich: @Bean public org.jooq.Configuration jooqConfig( final ConnectionProvider connectionProvider, final TransactionProvider transactionProvider, final ExecuteListenerProvider executeListenerProvider, @Value("${jooq.renderFormatted:false}") final boolean renderFormatted ) { final DefaultConfiguration hlp = new DefaultConfiguration(); return hlp .derive(hlp.settings() .withRenderNameStyle(RenderNameStyle.LOWER) .withRenderKeywordStyle(RenderKeywordStyle.UPPER) .withRenderFormatted(renderFormatted) ) .derive(connectionProvider) .derive(transactionProvider) .derive(executeListenerProvider) .derive(SQLDialect.ORACLE); } Kommerzieller Einsatz von jOOQ jOOQ ist kostenfrei nutzbar mit OpenSource Datenbanken unter Apache Software License 2.0. Für den Einsatz mit kommerziellen Datenbanken stehen verschiedene, kommerzielle Lizenzen zur Verfügung. In der Demo zum Vortrag wird eine Oracle Database Enterprise Edition genutzt. Damit die Demo gestartet werden kann, muss vor dem Build eine Trialversion der jOOQ Professional Edition im lokalen Maven Repository installiert werden. Fazit jOOQ ist mit Spring Boot quasi trivial verwendbar: Sowohl die notwendige Codegenerierung im Buildprozess als auch das Bootstrapping des jOOQ Kontextes passen in das Entwicklungsmodel von Spring Boot. Akzeptiert man eine zweischichtige Architektur kann man sehr leicht vorhandenes Wissen im Bereich Datenbankmodellierung und Abfrage in moderne Softwareentwicklung bringen. Innerhalb einer Spring Boot Anwendung kann jOOQ nahezu in beliebige Cloudumgebungen deployed werden. jOOQ passt - wie viele andere Werkzeuge auch - nicht auf jeden Anwendungsfall. Liegt ein Domain Driven Design Ansatz vor, sind komplexe Abfragen nicht maßgeblicher Bestandteil einer Anwendung oder ist der Schreibzugriff wichtiger, sprechen viele gute Gründe für den Einsatz eines ORMs wie JPA / Hibernate, gerne auch zusammen mit Spring Data JPA. Ein hier skizzierter Anwendungsfall, quasi eine direkte Abbildung einer HTTP Abfrage auf eine komplexe Query, lässt sich allerdings hervorragend mit jOOQ abbilden. 9
Oracle JET Das JavaScript Frontend Dilemma Der Schwerpunkt dieses Vortrages liegt nicht auf dem Frontend, aber die besten Daten sind nur schlecht erfassbar ohne ansprechende Präsentation. Viele moderne Anwendungen werden "headless" betrieben, ohne ein auf dem Server generiertes Webfrontend. Wird eine UI erstellt, so führt 2016 kaum noch ein Weg um JavaScript und allem was dazu gehört, vorbei. Mit einer schonungslosen Ausdauer werden Probleme gelöst: Aus diesem Dilemma gibt es mehrere Auswege: Man wählt ein Komponentenframework wie eine beliebige JSF Implementierung, GWT oder Vaadin, das auf Seiten des Servers HTML und JavaScript Komponenten erzeugt. Solange man keine speziellen Anpassungen benötigt, wird der Entwickler gut bis sehr gut vor den "Schmerzen" obigen Bildes geschützt. Ganz schnell läuft man aber Gefahr, dass scheinbar einfache Kundenanforderungen so sehr in den generierten Clientcode eingreifen, 10
dass die hohe Abstraktion mehr schadet als nutzt. Ein anderer Ausweg bietet die Zusammenstellung eigener Sammlungen von Komponenten oder der Entwurf eigener Frameworks (vermutlich die Quelle vieler auf *.js endender Frameworks). In beiden Fällen läuft man Gefahr, vom Wartungsaufwand überrannt zu werden. Oracles Lösung Oracle JET, kurz für JavaScript Extension Toolkit, wählt genau den Ansatz intelligent zusammengestellter Komponenten, um Entwicklern eine Sammlung von Werkzeugen zur Verfügung zu stellen, um modulare Anwendungen auf Basis moderner JavaScript, CSS3 und HTML5 Prinzipien zur Verfügung zu stellen. Das Ziel der Demo war, die über jOOQ / SQL selektierten mit Spring Boot einem Frontend zur Verfügung zu stellen und dort darzustellen: • Als Balkendiagram • Als kumulatives Liniendiagram • Mit ansprechender, zugänglicher Eingabemöglichkeit für Parameter • Grundsätzlich Internationalisierbar Mit dem Oracle JET Cookbook war das tatsächlich kein Problem, innerhalb weniger Tage konnte die Anwendung umgesetzt werden. Laut eigener Aussage zielt Oracle mit dem JET Framework auf "intermediate to advanced JavaScript developers". Mit grundsätzlich vorhandenem Wissen zu JavaScript, jQuery und der MVVM Implementierung KnockoutJS gelang es, obige Anforderungen an die Demo einfach umzusetzen: 11
Fazit Die Demo ist eine hübsche Spielerei, beinhaltet aber viele Dinge, die im Geschäftsalltag benötigt werden. Die Technologie ist zugänglich und nach erster Einschätzung auch Zukunftssicher, findet man sie doch bereits in Teilen in Oracle Apex wieder. Eine deutlich ausführlichere Beschreibung von Oracle JET von Andreas Koop steht auf den DOAG Seiten zur Verfügung: Moderne HTML5-Anwendungen mit Oracle JET entwickeln. Spring Boot, jOOQ und Oracle Jet: Fazit Steht man vor der Entscheidung, neue Produkte auf Basis bestehender, großer Datenmodelle zu Entwicklen und gegebenenfalls Rücksicht auf alte Anwendungen nehmen zu müssen, ist der hier vorgestellte Stack eine Möglichkeit, vorhandenes Wissen über SQL und Datenbanken mit auf eine moderne Plattform zu nehmen und mit einem ansprechenden Frontend zu versehen. NetBeans Die offizielle, offene und freie Java IDE NetBeans bietet per se exzellentes Javatooling, offizielle Plugins für die Entwicklung von Oracle JET Anwendungen sowie Unterstützung für Spring Boot. Einen ersten Einstieg nach dieser Demo bietet der Vortrag NetBeans, Maven und Spring Boot …mehr Spaß zusammen. 12
Bonus: Oracle Docker Images und Maven Repository Oracle Datenbank für Docker Oracle stellt seit 2016 offizielle Skripte zur Erzeugung von Images für Docker zur Verfügung: Oracles docker images repository. Diese Skripte sind gleichermaßen unter OS X, Windows 10 und Linux lauffähig und machen es sehr einfache, lokale Datenbanken zu Entwicklungszwecken, Testen oder ähnlichem zu Erzeugen. Die Demo beinhaltet mehrere Schritte, in denen gezeigt wird, wie die Skripte zur Verwenden sind. Offizielle Maven Koordinaten für den Oracle JDBC Treiber Oracles JDBC Treiber ist nach wie vor nicht im central repository verfügbar. Allerdings steht seit einiger Zeit ein offizielles Oracle Repository zur Verfügung, das mit wenig Vorarbeit genutzt werden kann, so dass nicht in eigenen Repositories eine parallel Installation des JDBC Treibers gepflegt werden muss. Die Demo zeigt die Verwendung des Oracle Repositories, Oracle JDBC steht unter com.oracle.jdbc ojdbc7 com.oracle.jdbc orai18n zur Verfügung. Ressourcen • Spring Boot • jOOQ, • Flyway • SQL, • Oracle JET • NetBeans • Docker 13
Kontakt Michael J. Simons michael-simons.eu @rotnroll666 michael@simons.ac ENERKO INFORMATIK GmbH Markt 45-47 52062 Aachen Germany 14
Sie können auch lesen