$30 off During Our Annual Pro Sale. View Details »

Datenbankzentrische Anwendungen mit Spring Boot und jOOQ

Datenbankzentrische Anwendungen mit Spring Boot und jOOQ

Aktualisierte Version der deutschen Version des Talks:

= Datenbankzentrische Anwendungen mit Spring Boot und jOOQ

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.

Der Autor setzt die Kombination beider Technologien erfolgreich zur Migration einer bestehenden, komplexen Oracle Forms Client Server Anwendung mit zahlreichen Tabellen und PL/SQL Stored Procedures hinzu einer modernen Architektur ein. Das Projekt profitiert sehr davon, die Datenbankstrukturen nicht in einen ORM "zu zwängen".

Der Vortrag demonstriert, wie Spring Boot genutzt wird, um den Kontext für jOOQ vorzubereiten (Datenbankverbindung, Transaktionen etc.) und anschließend fortgeschrittene, analytische Abfragen als HTTP apis zu veröffentlichen.

Auf dem Weg dorthin wird ebenfalls über Datenbankmigrationen gesprochen und wie auch an dieser Stelle die Magie von Spring Boot eingesetzt werden kann, um Entwicklungs-, Test- und Produktivdatenbanken synchron zu halten.

Michael Simons

April 26, 2017
Tweet

More Decks by Michael Simons

Other Decks in Programming

Transcript

  1. SPRING BOOT UND JOOQ
    DATENBANKZENTRISCHE
    ANWENDUNGEN MIT

    View Slide

  2. ÜBER MICH
    MICHAEL SIMONS
    ▸ Entwickler bei ENERKO
    INFORMATIK in Aachen
    ▸ Datenbankzentrische
    Anwendungen im Energiemarkt
    ▸ Leiter Euregio JUG
    ▸ Bloggt zu Java und Spring Themen
    unter info.michael-simons.eu
    ▸ Co-Autor arc(42) by example
    ▸ @rotnroll666 auf Twitter

    View Slide

  3. DATENBANKZENTRISCHE ANWENDUNGEN FÜR DEN ENERGIEMARKT

    View Slide

  4. DESKTOP GIS AUF BASIS VON ORACLE SPATIAL

    View Slide

  5. JAVA UND
    DATENBANKEN
    Plain SQL, ORM oder etwas
    dazwischen?

    View Slide

  6. ETWAS DAZWISCHEN?
    JEDE MENGE!
    ▸ JDBC
    ▸ Springs JDBCTemplate
    ▸ JPA
    ▸ JPQL
    ▸ Criteria query
    ▸ jOOQ
    ▸ noch einige mehr…
    Y U NOT GIVE ME THE RIGHT TOOL?

    View Slide

  7. NUN…

    View Slide

  8. Quelle: https://www.flickr.com/photos/133021668@N04/31403416393

    View Slide

  9. Quelle: https://www.flickr.com/photos/potentialpast/5582667252

    View Slide

  10. Quelle: https://en.wikipedia.org/wiki/Wolf_of_Ansbach

    View Slide

  11. View Slide

  12. DIE HITPARADE
    ZUERST EIN BEISPIEL

    View Slide

  13. View Slide

  14. DAS ZUGEHÖRIGE SCHEMA

    View Slide

  15. WIE HÄTTET IHR ES GERNE?
    PLAIN SQL
    Select *
    from tracks
    where album = 'True Survivor';

    View Slide

  16. WIE HÄTTET IHR ES GERNE?
    PLAIN JPA
    @Entity
    @Table(name = "tracks")
    public class TrackEntity implements Serializable {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    @Column
    private String album;
    public static void main(String...a) {
    final EntityManagerFactory factory =
    Persistence.createEntityManagerFactory("whatever");
    final EntityManager entityManager =
    factory.createEntityManager();
    List tracks = entityManager
    .createQuery("Select t from tracks where album = :album")
    .setParameter("album", "True Survivor“)
    .getResultList();
    }
    }

    View Slide

  17. WIE HÄTTET IHR ES GERNE?
    JPA + SPRING DATA
    public interface TrackRepository extends
    JpaRepository {
    public List findAllByAlbum(final String name);
    public static void main(String...a) {
    TrackRepository trackRepository;
    final List tracks = trackRepository
    .findAllByAlbum("True Survivor");
    }
    }

    View Slide

  18. @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 = ""
    + "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];
    });
    }
    }
    ERNSTHAFT? SQL TRIFFT JAVA
    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());

    View Slide

  19. JUST BECAUSE YOU'RE USING
    HIBERNATE, DOESN'T MEAN YOU
    HAVE TO USE IT FOR EVERYTHING.
    Gavin King
    JPA & HIBERNATE?

    View Slide

  20. JOOQ

    View Slide

  21. JAVA OBJECT ORIENTED QUERYING
    WAS IST JOOQ?
    ▸ „Query builder framework“
    ▸ Java DSL zur Generierung datenbankspezifischer Statements
    ▸ Das Schema ist die „treibende Kraft“
    ▸ Generierung eines Java-Schemas (Optional, aber empfohlen)
    ▸ Typsicher
    ▸ OpenSource für OpenSource Datenbanken, $ bis $$ für
    Enterprise Datenbanken

    View Slide

  22. View Slide

  23. JOOQ
    JAVA BASIERTES SCHEMA
    BUILD 

    PROCESS
    runs
    GENERATOR
    DATABASE
    reverse

    engineers
    JAVA-BASED

    SCHEMA
    creates
    APPLICATION DSL CONTEXT
    uses
    uses
    SQL
    generates

    View Slide

  24. SCHEMA
    DATENBANKMIGRATIONEN SIND ESSENTIELL
    ▸ Liquibase
    ▸ Flyway

    View Slide

  25. DATENBANKMIGRATIONEN
    WORKFLOW
    ▸ Build gegen Entwicklungsdatenbank
    ▸ startet Migration
    ▸ startet jOOQ Generator
    ▸ Anwendung gegen Produktionsdatenbank
    ▸ startet ebenfalls Migration
    ➡ Java Schema „passt“ immer zur Datenbank

    View Slide


  26. org.springframework.boot
    spring-boot-starter-jooq


    org.jooq
    jooq




    org.jooq.pro
    jooq
    ${jooq.version}


    org.flywaydb
    flyway-core

    WIE FUNKTIONIERT DAS MIT SPRING BOOT?

    org.springframework.boot
    spring-boot-starter-jooq


    org.flywaydb
    flyway-core

    View Slide

  27. DER BUILD PROCESS IST NUR WENIG AUFWÄNDIGER
    spring.datasource.url = @db.url@
    spring.datasource.username = @db.username@
    spring.datasource.password = @db.password@
    MAVEN PROPERTIES IN DEFAULT-KONFIGURATION NUTZEN


    jdbc:oracle:thin:@//localhost:1521/ORCLPDB1

    doag2016
    doag2016
    DOAG2016

    DEVELOPMENT CONNECTION IM POM

    View Slide

  28. FLYWAY WÄHREND DES BUILDS WIE ZUR LAUFZEIT NUTZEN
    FLYWAY



    org.flywaydb
    flyway-maven-plugin
    ${flyway.version}


    generate-sources

    migrate




    ${db.url}
    ${db.username}
    ${db.password}

    filesystem:src/main/resources/db/migration





    View Slide

  29. JOOQ NACH DEN MIGRATIONEN
    JOOQ



    org.jooq.pro
    jooq-codegen-maven
    ${jooq.version}


    generate-sources

    generate





    oracle.jdbc.OracleDriver
    ${db.url}
    ${db.username}
    ${db.password}







    View Slide

  30. DEMO

    View Slide

  31. ZUSAMMENFASSUNG
    ▸ Direkte Abbildung von Abfragen auf URLs
    ▸ Von einfach bis kompliziert alles möglich
    ▸ Logging der generierten Queries ist hilfreich
    ▸ Einfache Übergabe von Parametern an Queries
    ▸ Oft benutzte Fragmente können wiederverwendet werden
    ▸ Records können auf beliebige POJOs abgebildet werden
    ▸ Spezialisiertes Domainen-Modell
    ▸ DAOs sind auch möglich

    View Slide

  32. THE SQL… IT'S ALWAYS BEEN
    THERE, IT WILL GUIDE YOU
    Lukas Skyeder
    THE PROBLEM WITH INTERNET QUOTES IS THAT YOU CANT ALWAYS DEPEND
    ON THEIR ACCURACY" - ABRAHAM LINCOLN, 1864

    View Slide

  33. THE WINDOW FUNCTION…IT MOVES THROUGH EVERY LIVING THING
    USE CASE ANALYTIC FUNCTIONS
    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;

    View Slide

  34. USE THE INDEX, LUKE
    USE CASE „UPSERT“ / MERGE STATEMENT
    MERGE INTO tablename USING table_reference ON (condition)
    WHEN MATCHED THEN
    UPDATE SET column1 = value1 [, column2 = value2 ...]
    WHEN NOT MATCHED THEN
    INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...]);

    View Slide

  35. WO SIND JETZT DIE SILBERNEN KUGELN?
    JPA / HIBERNATE UND JOOQ
    ▸ Automatische Datenbankmigrationen
    ▸ JPA / Hibernate zusammen mit Spring Data JPA
    ▸ JPQL Queries falls nötig (Eventuell Criteria Queries)
    ▸ Native Queries nicht in Annotationen verstecken!
    ▸ SQL Code komplexer Abfragen und Projektionen mit jOOQ
    generieren
    ▸ An den EntityManager übergeben und Entitäten selektieren
    ▸ oder den DSL Context direkt benutzen

    View Slide

  36. View Slide

  37. USING SQL-1999
    HIERARCHISCHE ABFRAGE MITTELS REKURSIVER CTE*
    WITH RECURSIVE cte AS (
    SELECT id, content FROM tweets WHERE id = ?
    UNION ALL
    SELECT t.id, t.content
    FROM cte c
    JOIN tweets t on t.in_reply_to_status_id = c.id
    ) SELECT * FROM cte;
    (*) Common Table Expression

    View Slide

  38. JOOQ INNERHALB VON SPRING DATA REPOSITORIES
    2 INTERFACES, 1 KLASSE
    ▸ Eigenes Interface TweetRepositoryExt, das eine Methode
    enthält:
    public interface TweetRepositoryExt {
    List getTweetHierarchy(long id);
    }

    View Slide

  39. JOOQ INNERHALB VON SPRING DATA REPOSITORIES
    ▸ Spring Data JPA Repository, erbt von TweetRepositoryExt

    public interface TweetRepository extends
    Repository,
    TweetRepositoryExt {
    }

    View Slide

  40. @RequiredArgsConstructor
    public class TweetRepositoryImpl implements
    TweetRepositoryExt {
    private final DSLContext create;
    @Override
    public List getTweetHierarchy(

    final long id
    ) {
    // Todo: Insert some Magic ;)
    return query.getResultList();
    }
    }
    JOOQ INNERHALB VON SPRING DATA REPOSITORIES
    ▸ Implementierung von TweetRepositoryExt

    View Slide

  41. View Slide

  42. DEMO

    View Slide

  43. RESSOURCEN
    KENNT EURE WERKZEUGE!
    ▸ https://modern-sql.com
    ▸ https://blog.jooq.org
    ▸ https://vladmihalcea.com/tutorials/
    hibernate/
    ▸ http://www.thoughts-on-java.org/
    persistence/
    ▸ Und natürlich die jeweilige
    Referenzdokumentation

    View Slide

  44. RESSOURCEN
    DANKE FÜR EURE ZEIT!
    ▸ Heutige Demo Projekte:

    github.com/michael-simons/DOAG2016

    github.com/michael-simons/tweetarchive
    ▸ Slides: speakerdeck.com/michaelsimons
    ▸ Artikel in DOAG RedStack Magazin

    http://info.michael-simons.eu/2017/03/06/red-stack-magazin-
    datenbankzentrische-anwendungen-mit-spring-boot-und-jooq/
    ▸ Kontakt: michael-simons.eu // @rotnroll666

    View Slide

  45. RESSOURCEN
    SPRING BOOT BUCH
    ▸ Q4 2017 im Dpunkt.verlag
    ▸ springbootbuch.de
    ▸ @springbootbuch
    ▸ Beispiele des Kapitels „Persistenz“:

    github.com/springbootbuch/database_examples

    Beinhaltet JDBCTemplate, JPA und JOOQ mit Pagila-
    Demo Datenbank (PostgreSQL)

    View Slide