Leoš Přikryl

leos.prikryl@commity.cz

@leos_prikryl

jOOQ

  • Java knihovna pro práci s databázemi
  • dobrý kompromis mezi ORM a JDBC
  • výhody proti ORM
    • méně magie, jednodušší na naučení a pochopení
    • nesnaží se skrýt SQL, máme k dispozici jeho plnou sílu
    • vhodné i pro existující databáze
  • výhody proti JDBC
    • lepší syntaxe - DSL pro SQL
    • typová bezpečnost
    • nezávislost na typu databáze

Database first přístup

  • Zdrojem pravdy je databáze, kód se generuje z ní
  • Databáze se mění méně často než kód
  • Vývojáři musejí přemýšlet nad strukturou dat v databázi
  • Vede to k efektivnějšímu využívání databáze

Code generator

  • Vygeneruje Java/Kotlin kód s popisem databáze
    • tabulky, sloupce, klíče, funkce...
    • Entity, POJO, DAO

Ukázka

SQL
SELECT last_name, first_name
FROM "user"
WHERE role = 'admin'
ORDER BY last_name, first_name
jOOQ
dsl.select(USER.LAST_NAME, USER.FIRST_NAME)
    .from(USER)
    .where(USER.ROLE.eq("admin"))
    .orderBy(USER.LAST_NAME, USER.FIRST_NAME)

Typová bezpečnost

Použití špatného datového typu pro sloupec ROLE

dsl.select(USER.LAST_NAME, USER.FIRST_NAME)
    .from(USER)
    .where(USER.ROLE.eq(1))

Po přejmenování LAST_NAME na SURNAME a přegenerování kódu

dsl.select(USER.LAST_NAME, USER.FIRST_NAME)
    .from(USER)
    .where(USER.ROLE.eq("admin"))

Použití bez generátoru

  • jOOQ lze používat i bez generátoru, ale nedoporučuji to
  • Hodí se to v případě, že strukturu databáze neznáte v době kompilace
dsl.select(USER.LAST_NAME, USER.FIRST_NAME)
    .from(USER)
    .where(USER.ROLE.eq("admin"))
    .orderBy(USER.LAST_NAME, USER.FIRST_NAME)
dsl.select(field("last_name"), field("first_name"))
    .from(table("user"))
    .where(field("role").eq("admin"))
    .orderBy(field("last_name"), field("first_name"))

Složitější dotaz

SELECT author.first_name, author.last_name, count(*)
FROM author
    JOIN book ON book.author_id = author.id
    JOIN language ON book.language_id = language.id
WHERE language.code = 'CZ'
    AND book.published_in > DATE '2020-01-01'
GROUP BY author.first_name, author.last_name
HAVING COUNT(*) > 5
ORDER BY author.last_name ASC NULLS FIRST
LIMIT 5
OFFSET 10
ctx.select(AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME, count())
    .from(AUTHOR)
        .join(BOOK).onKey()
        .join(LANGUAGE).onKey()
    .where(LANGUAGE.CODE.eq("CZ")
        .and(BOOK.PUBLISHED_IN.gt(LocalDate.of(2020, 1, 1))))
    .groupBy(AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)
    .having(count().gt(5))
    .orderBy(AUTHOR.LAST_NAME.asc().nullsFirst())
    .limit(5)
    .offset(10)

Mapování na POJO

data class Book(
    val id: Int,
    val title: String?
)
ctx.select(BOOK.ID, BOOK.TITLE)
    .from(BOOK)
    .fetchInto(Book::class.java)

Zapisování do databáze

  • Pomocí SQL
    ctx.insertInto(AUTHOR, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)
        .values("Jack", "London")
        .execute();
  • Active Record pattern
    val author = ctx.newRecord(AUTHOR).apply {
        firstName = "Jack"
        lastName = "London"
    }
    author.store()
  • DAO + POJOs
    val author = Author().apply {
        firstName = "Jack"
        lastName = "London"
    }
    authorDao.insert(author)

Pokročilejší funkcionality

  • Emulace neexistujících databázových funkcí
  • Podpora enumerací, polí a JSON sloupců

Zanořené datové struktury

{
    "id": 1,
    "name": {
        "first": "George",
        "last": "Orwell"
    },
    "books": [
        {
            "id": 1,
            "title": "1984"
        },
        {
            "id": 2,
            "title": "Animal Farm"
        }
    ]
}
data class Author(
    val id: Int,
    val name: Name,
    val books: Array<Book>
)

data class Name(
    val first: String,
    val last: String
)

data class Book(
    val id: Int,
    val title: String
)

Zanořené datové struktury

ctx.select(
    AUTHOR.ID,
    row(AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME).`as`("name"),
    multiset(
        select(row(BOOK.ID, BOOK.TITLE))
            .from(BOOK)
            .where(BOOK.AUTHOR_ID.eq(AUTHOR.ID))
    ).`as`("books")
)
    .from(AUTHOR)
    .fetchInto(Author::class.java)

Nevýhody

  • Není vhodné pro ukládání složitých objektových grafů
  • Je zdarma pouze pro poslední verze open source DB
  • Trochu one man show Lukase Edera (@lukaseder)

Děkuji za pozornost