Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Bootiful database-centric applications with jOOQ

Bootiful database-centric applications with jOOQ

See the recording of this talk here: https://www.youtube.com/watch?v=4pwTd6NEuN0

jOOQ is one of several quite different technologies to access relational data from Java based applications.

jOOQ is short for "Java object oriented querying" and describes a query builder framework that takes a look at your database schema, independent wether you use an Open Source database like PostgreSQL or an commercial product like Oracle Database., and provides you with a domain specific language (DSL) for generating statements.

jOOQs goal is explicitly not to shield the user from SQL but providing a type safe way to use it.

Learn in this session who you can facilitate the "magic" of Spring Boot to provide jOOQ with needed resources and then use it to publish advanced analytic queries as HTTP apis.

Along the way you learn how automatic database migrations help you to do continuous delivery even with database centric applications.

Michael Simons

May 18, 2017
Tweet

More Decks by Michael Simons

Other Decks in Programming

Transcript

  1. @spring_io #springio17 ABOUT ME MICHAEL SIMONS • Lives and works

    in Aachen • Developer at ENERKO INFORMATIK • Leader of the Euregio JUG • Blogs about Java, Spring and software architecture on
 info.michael-simons.eu • Co-Autor arc(42) by example • @rotnroll666 on Twitter
  2. @spring_io #springio17 EVERYONE HAS THEIR OWN PERSISTENCE FRAMEWORK ACTUALLY, A

    LOT… • JDBC • Springs JDBCTemplate • JPA • JPQL • Criteria query • jOOQ • many more Y U NOT GIVE ME THE RIGHT TOOL?
  3. @spring_io #springio17 HOW DO YOU WANT IT? PLAIN SQL Select

    * from tracks where album = 'True Survivor';
  4. @spring_io #springio17 HOW DO YOU WANT IT? 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<Track> tracks = entityManager .createQuery("Select t from tracks where album = :album") .setParameter("album", "True Survivor") .getResultList(); } }
  5. @spring_io #springio17 HOW DO YOU WANT IT? JPA + SPRING

    DATA public interface TrackRepository extends JpaRepository<TrackEntity, Integer> { public List<Track> findAllByAlbum(final String name); public static void main(String...a) { TrackRepository trackRepository; final List<Track> tracks = trackRepository .findAllByAlbum("True Survivor"); } }
  6. @spring_io #springio17 THE WINDOW FUNCTION…IT MOVES THROUGH EVERY LIVING THING

    SELECT ALL THE STUFF… 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;
  7. @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<Object[]> 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]; }); } } REALLY? SQL MEETS 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());
  8. @spring_io #springio17 JUST BECAUSE YOU'RE USING HIBERNATE, DOESN'T MEAN YOU

    HAVE TO USE IT FOR EVERYTHING. Gavin King JPA & HIBERNATE?
  9. @spring_io #springio17 JAVA OBJECT ORIENTED QUERYING WHAT IS JOOQ? •

    „Query builder framework“ • Java DSL for generating database and schema specific statements • The schema is the leading factor • jOOQ generates a corresponding Java-schema • Typesafe • OpenSource for OpenSource Datenbanken, $ ~ $$ for Enterprise databases
  10. JOOQ JAVA-BASED SCHEMA BUILD 
 PROCESS runs GENERATOR DATABASE reverse


    engineers JAVA-BASED
 SCHEMA creates APPLICATION DSL CONTEXT uses uses SQL generates
  11. @spring_io #springio17 DATABASEMIGRATIONS WORKFLOW • Build against development database •

    starts migration • starts jOOQ generator • Application runs against production database • starts migration again ➡Generated Java schema is in sync with the production database
  12. HOW TO USE THIS WITH SPRING BOOT? <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jooq</artifactId>

    </dependency> <dependency> <groupId>org.flywaydb</groupId> <artifactId>flyway-core</artifactId> </dependency>
  13. <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jooq</artifactId> <exclusions> <exclusion> <groupId>org.jooq</groupId> <artifactId>jooq</artifactId> </exclusion> </exclusions> </dependency>

    <dependency> <groupId>org.jooq.pro</groupId> <artifactId>jooq</artifactId> <version>${jooq.version}</version> </dependency> <dependency> <groupId>org.flywaydb</groupId> <artifactId>flyway-core</artifactId> </dependency> HOW TO USE THIS WITH SPRING BOOT?
  14. @spring_io #springio17 <properties> <db.url> jdbc:oracle:thin:@//localhost:1521/ORCLPDB1 </db.url> <db.username>doag2016</db.username> <db.password>doag2016</db.password> <db.schema>DOAG2016</db.schema> </properties>

    DEVELOPMENT CONNECTION INSIDE POM THE BUILD PROCESS IS A LITTLE BIT „HARDER“ spring.datasource.url = @db.url@ spring.datasource.username = @db.username@ spring.datasource.password = @db.password@ USE MAVEN PROPERTIES IN DEFAULT CONFIGURATION
  15. @spring_io #springio17 RUN FLYWAY DURING BUILD AND RUNTIME FLYWAY <build>

    <plugins> <plugin> <groupId>org.flywaydb</groupId> <artifactId>flyway-maven-plugin</artifactId> <version>${flyway.version}</version> <executions> <execution> <phase>generate-sources</phase> <goals> <goal>migrate</goal> </goals> </execution> </executions> <configuration> <url>${db.url}</url> <user>${db.username}</user> <password>${db.password}</password> <locations> <location>filesystem:src/main/resources/db/migration</location> </locations> </configuration> </plugin> </plugins> </build>
  16. @spring_io #springio17 JOOQ AFTER MIGRATIONS JOOQ <plugin> <groupId>org.jooq.pro</groupId> <artifactId>jooq-codegen-maven</artifactId> <version>${jooq.version}</version>

    <executions> <execution> <phase>generate-sources</phase> <goals> <goal>generate</goal> </goals> </execution> </executions> <configuration> <jdbc> <driver>oracle.jdbc.OracleDriver</driver> <url>${db.url}</url> <user>${db.username}</user> <password>${db.password}</password> </jdbc> <generator> <!-- whatever --> </generator> </configuration> </plugin>
  17. @spring_io #springio17 SUMMARY • Map queries to URLs • Simple

    as well as complex queries • Logging of generated queries is quite useful • Spring MVC parameters can be used without fear of SQL injection as bind parameters • Reuse query fragments • Mapping records to POJOs and other • DAOs possible, too • Use jOOQ as SQL generator only
  18. @spring_io #springio17 DEMO WAS CREATED ORACLE 12C… NOW ON POSTGRESQL

    MOSTLY… • jOOQ tries to be as close as possible to SQL-standards • knows how to emulate clauses for various databases • Example • LIMIT vs. OFFSET x ROWS / FETCH • Vendor specific functions (trunc vs. date_trunc etc.)
  19. @spring_io #springio17 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
  20. @spring_io #springio17 USECASES • Analytic functions (Use the database features

    you most likely paid for already) • „Upsert“ (Merge-Statements) • Partial selects (only the columns you need without going through Entity Graphs)
  21. @spring_io #springio17 WHERE ARE THE SILVER BULLETS NOW? JPA /

    HIBERNATE *AND* JOOQ • Automatic database migrations • JPA / Hibernate with Spring Data JPA • JPQL Queries if necessary (Criteria queries if masochistic) • Don’t hide native queries in annotations! • Create complex queries and projections with jOOQ • Pass to EntityManager to retrive Entities or • use DSL Context directly
  22. @spring_io #springio17 „GIVE ME THE GENRE THAT HAS BEEN PLAYED

    THE MOST…“ (*) (*) Classic SQL-Interview question
  23. @spring_io #springio17 ALL ENTRIES THAT HAVE „THE MOST“ PLAIN SQL

    select id, genre from ( select g.id, g.genre, rank() over (order by count(*) desc) rnk from plays p join tracks t on p.track_id = t.id join genres g on t.genre_id = g.id group by g.id, g.genre ) src where src.rnk = 1;
  24. @spring_io #springio17 PERFECTLY FINE TO USE INSIDE SPRING DATA JPA

    REPOSITORY 2 INTERFACES, ONE CLASS Custom extension GenreRepositoryExt interface containing interface GenreRepositoryExt { List<GenreEntity> findWithHighestPlaycount(); }
  25. @spring_io #springio17 PERFECTLY FINE TO USE INSIDE SPRING DATA JPA

    REPOSITORY 2 INTERFACES, ONE CLASS Spring Data JPA Repository, extending GenreRepositoryExt
 public interface GenreRepository extends CrudRepository<GenreEntity, Integer>, GenreRepositoryExt { }
  26. @spring_io #springio17 Class implementing our query with jOOQ class GenreRepositoryImpl

    implements GenreRepositoryExt { private final EntityManager entityManager; private final DSLContext create; public List<GenreEntity> findWithHighestPlaycount() { final SelectQuery<Record> sqlGenerator = this.create.select()./* Query */getQuery(); final String sql = sqlGenerator .getSQL(ParamType.NAMED); final Query query = entityManager .createNativeQuery(sql, GenreEntity.class); return query.getResultList(); } } PERFECTLY FINE TO USE INSIDE SPRING DATA JPA REPOSITORY 2 INTERFACES, ONE CLASS
  27. @spring_io #springio17 RESOURCES KNOW YOUR TOOLS! • Most certainly the

    manuals! • https://modern-sql.com • https://blog.jooq.org • https://vladmihalcea.com/tutorials/ hibernate/ • http://www.thoughts-on-java.org/ persistence/
  28. @spring_io #springio17 RESOURCES THANK YOU FOR YOUR TIME! • Demo

    project:
 github.com/michael-simons/bootiful-databases • Slides:
 speakerdeck.com/michaelsimons • Kontakt: michael-simons.eu • Twitter: @rotnroll666
  29. @spring_io #springio17 RESOURCES SPRING BOOT BUCH • Q4 2017 at

    Dpunkt.verlag • springbootbuch.de • @springbootbuch • Example for chapter „Persistenz“:
 github.com/springbootbuch/database_examples
 Contains JDBCTemplate, JPA and JOOQ with Pagila-Demo Database (PostgreSQL)