Getting Started with
JOOQ and Flyway
Wiring up your Java app with DB
JUG.LV 08.05.2014 RIGA
DMITRY LEBEDEV & RUSTAM ARSLANOV @ 28STONE
About
Dmitry - developer
Rustam - DevOps guy
Customer - finances
Project - set of
microservices
http://www.28stone.com
Motivation
Use simple tools
Try something new
Keep codebase maintainable
Flyway
SQL scripts for migration
Java beans for complex logic
Simple commands
JOOQ
Java API corresponds to SQL semantics
ActiveRecord & JPA beans generation
Strict typing in Java
Simple Tool: Flyway
flyway:clear
flyway:init
flyway:migrate
Simple Tool: JOOQ
List<BookRecord> list = create.select(field("BOOK.TITLE"), field("AUTHOR.
FIRST_NAME"), field("AUTHOR.LAST_NAME"))
.from(table("BOOK"))
.join(table("AUTHOR"))
.on(field("BOOK.AUTHOR_ID").equal(field("AUTHOR.ID")))
.where(field("BOOK.PUBLISHED_IN").equal(1948))
.fetchInto(BookRecord.class);
Flyway
migrate
V1_1__create_book_table.sql
V1_2__create_author_table.sql
V1_3__alter_book_table.sql DB
Database
DB
book
author
schema_version
Database
DB
schema_version
JOOQ
generate
DB
book
author
SchemaImpl
Tables
BOOK
AUTHOR
BookRecord
AuthorRecord
JOOQ
SchemaImpl
Tables
BOOK
AUTHOR
domain-objects_<version>.jar
BookRecord
AuthorRecord
component_1
domain-objects-version=1.1
component_2
domain-objects-version=1.1
component_3
domain-objects-version=1.2
Build systems support
Flyway:
command line (java)
java API
ant
maven 2,3
gradle
sbt
Jooq:
command line (java)
java API
maven 2,3
gradle (3d party)
domain-objects build workflow (gradle)
clean
resource
preparation
deployflyway
generated
compilation
jar
main
compilation
● resources
filtering
clean
init
repair
validate
info
migrate
● generated
compile
jooq
generateJooq
domain-objects deploy workflow (gradle)
clean deployflyway
get
dependencies
clean
init
repair
validate
info
migrate
... ... ...
● get domain-
objects
● deploy or run
application
Flyway in CI (jenkins)
Flyway versioning
One or more numeric parts
Separated by a dot (.) or an underscore (_)
Underscores are replaced by dots at runtime
Leading zeroes are ignored in each part
1
001
5.2
5_2 (5.2 at runtime)
1.2.3.4.5.6.7.8.9
205.68
20130115113556
2013.1.15.11.35.56
2013.01.15.11.35.56
1__CreateTable.sql
001__AlterTable.sql
5.2__InsertDate.sql
5_2__DeleteData.sql
1.2.3.4.5.6.7.8.9__AnotherScript.sql
205.68__Transform.sql
20130115113556__InsertView.sql
2013.1.15.11.35.56__AddService.sql
2013.01.15.11.35.56__Delete.sql
Let’s Do Some Programming!
Building queries / SQL generation
Fetching results
Updating records
Not covering:
● Lazy fetch
● Transaction management
● Constraints & Relationships
DSLContext - starting point
DSLContext create = DSL.using(conn, SQLDialect.MYSQL);
Result<Record> result = create.select().from(AUTHOR).fetch();
Building queries
create.select(field("BOOK.TITLE"), field("AUTHOR.FIRST_NAME"), field
("AUTHOR.LAST_NAME"))
.from(table("BOOK"))
.join(table("AUTHOR"))
.on(field("BOOK.AUTHOR_ID").equal(field("AUTHOR.ID")))
.where(field("BOOK.PUBLISHED_IN").equal(1948))
.fetch();
Building queries
create.select(BOOK.TITLE, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)
.from(BOOK)
.join(AUTHOR)
.on(BOOK.AUTHOR_ID.equal(AUTHOR.ID))
.where(BOOK.PUBLISHED_IN.equal(1948))
.fetch();
Building queries
create.select(BOOK.TITLE, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)
.from(BOOK)
.join(AUTHOR)
.on(BOOK.AUTHOR_ID.equal(AUTHOR.ID))
.where(BOOK.PUBLISHED_IN.equal(1948))
.orderBy(BOOK.TITLE.asc())
.fetch();
Building queries
create.select(BOOK.TITLE, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)
.from(BOOK)
.join(AUTHOR)
.on(BOOK.AUTHOR_ID.equal(AUTHOR.ID))
.where(BOOK.PUBLISHED_IN.equal(1948))
.orderBy(BOOK.TITLE.asc()).limit(10).offset(100)
.fetch();
SQL Generation
String sql = create.select(BOOK.TITLE, AUTHOR.FIRST_NAME, AUTHOR.
LAST_NAME)
.from(BOOK)
.join(AUTHOR)
.on(BOOK.AUTHOR_ID.equal(AUTHOR.ID))
.where(BOOK.PUBLISHED_IN.equal(1948))
.orderBy(BOOK.TITLE.asc()).limit(10).offset(100)
.getSQL();
Fetching results: Record
create.select(BOOK.TITLE, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)
.from(BOOK)
.join(AUTHOR)
.on(BOOK.AUTHOR_ID.equal(AUTHOR.ID))
.where(BOOK.PUBLISHED_IN.equal(1948))
.orderBy(BOOK.TITLE.asc()).limit(10).offset(100)
.fetch();
Fetching results: TableRecord
create.selectFrom(BOOK)
.where(BOOK.PUBLISHED_IN.equal(1948))
.orderBy(BOOK.TITLE.asc()).limit(10).offset(100)
.fetch();
List<BookRecord> result1 = create.selectFrom(BOOK)...
Result<Record> result2 = create.select().from(BOOK)...
// somewhere in iteration loop
bookRecord.getTitle();
record.get(“title”);
Record VS. TableRecord
Fetching results: CSV
create.selectFrom(BOOK)
.where(BOOK.PUBLISHED_IN.equal(1948))
.orderBy(BOOK.TITLE.asc()).limit(10).offset(100)
.fetch().formatCSV(‘,’,””);
Fetching results: JSON
create.selectFrom(BOOK)
.where(BOOK.PUBLISHED_IN.equal(1948))
.orderBy(BOOK.TITLE.asc()).limit(10).offset(100)
.fetch().formatJSON();
Fetching results: XML
create.selectFrom(BOOK)
.where(BOOK.PUBLISHED_IN.equal(1948))
.orderBy(BOOK.TITLE.asc()).limit(10).offset(100)
.fetch().formatXML();
Fetching results: HTML
create.selectFrom(BOOK)
.where(BOOK.PUBLISHED_IN.equal(1948))
.orderBy(BOOK.TITLE.asc()).limit(10).offset(100)
.fetch().formatHTML();
Fetching results: POJOs
create.select(BOOK.TITLE, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)
.from(BOOK)
.join(AUTHOR)
.on(BOOK.AUTHOR_ID.equal(AUTHOR.ID))
.where(BOOK.PUBLISHED_IN.equal(1948))
.orderBy(BOOK.TITLE.asc()).limit(10).offset(100)
.fetchInto(BookDTO.class);
Fetching results: Maps
create.select(BOOK.TITLE, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)
.from(BOOK)
.join(AUTHOR)
.on(BOOK.AUTHOR_ID.equal(AUTHOR.ID))
.where(BOOK.PUBLISHED_IN.equal(1948))
.orderBy(BOOK.TITLE.asc()).limit(10).offset(100)
.fetchMaps();
Records: create
// Create a new record
BookRecord book1 = create.newRecord(BOOK);
// Insert the record: INSERT INTO BOOK (TITLE) VALUES ('1984');
book1.setTitle("1984");
book1.store();
Records: update
BookRecord book2 = create.fetchOne(BOOK, BOOK.ID.equal(id));
// Update the record: UPDATE BOOK SET TITLE = 'Animal Farm' WHERE ID
= [id]
book2.setTitle("Animal Farm");
book2.store();
Records: delete
BookRecord book = create.fetchOne(BOOK, BOOK.ID.equal(5));
// Delete the book
book.delete();
Records: batch changes
// Fetch a bunch of books
List<BookRecord> books = create.fetch(BOOK);
// Modify the above books, and add some new ones:
modify(books);
addMore(books);
// Batch-update and/or insert all of the above books
create.batchStore(books);
Records: update using a query
create.update(BOOK)
.set(BOOK.IS_BESTSELLER, 1)
.where(BOOK.SELLED_COPIES.greater(100000))
.execute();
Q&A!
any questions?!
JOOQ and Flyway

JOOQ and Flyway