From 962d7b6341c47c1226ff24a8d5decd2cdcc2450a Mon Sep 17 00:00:00 2001 From: lukaseder Date: Fri, 9 Jun 2017 10:23:37 +0200 Subject: [PATCH] [#6336] Add another Kotlin example using apply --- jOOQ-examples/jOOQ-kotlin-example/pom.xml | 2 +- .../example/kotlin/FunWithKotlinAndJOOQ.kt | 52 +++++++++++++ .../kotlin/org/jooq/example/kotlin/Utils.kt | 74 ------------------- 3 files changed, 53 insertions(+), 75 deletions(-) delete mode 100644 jOOQ-examples/jOOQ-kotlin-example/src/main/kotlin/org/jooq/example/kotlin/Utils.kt diff --git a/jOOQ-examples/jOOQ-kotlin-example/pom.xml b/jOOQ-examples/jOOQ-kotlin-example/pom.xml index d0a652c6c9..79725aef96 100644 --- a/jOOQ-examples/jOOQ-kotlin-example/pom.xml +++ b/jOOQ-examples/jOOQ-kotlin-example/pom.xml @@ -41,7 +41,7 @@ org.jetbrains.kotlin - kotlin-stdlib + kotlin-stdlib-jre8 ${kotlin.version} diff --git a/jOOQ-examples/jOOQ-kotlin-example/src/main/kotlin/org/jooq/example/kotlin/FunWithKotlinAndJOOQ.kt b/jOOQ-examples/jOOQ-kotlin-example/src/main/kotlin/org/jooq/example/kotlin/FunWithKotlinAndJOOQ.kt index 2c96ede3de..bf55ae0bfc 100644 --- a/jOOQ-examples/jOOQ-kotlin-example/src/main/kotlin/org/jooq/example/kotlin/FunWithKotlinAndJOOQ.kt +++ b/jOOQ-examples/jOOQ-kotlin-example/src/main/kotlin/org/jooq/example/kotlin/FunWithKotlinAndJOOQ.kt @@ -48,6 +48,8 @@ import java.sql.* fun main(args: Array) { + // This example project simply uses a standalone JDBC connection, but you're free to use any other + // means to connect to your database, including standard DataSources val properties = Properties(); properties.load(properties::class.java.getResourceAsStream("/config.properties")); @@ -60,6 +62,9 @@ fun main(args: Array) { val a = AUTHOR val b = BOOK + // This example shows just standard jOOQ API usage, which selects a couple of columns from a join expression + // and then uses a closure to iterate over results. String interpolation is used, along with the implicit + // it variable and the nice Record.get(Field) syntax sugar using it[field] syntax header("Books and their authors") ctx.select(a.FIRST_NAME, a.LAST_NAME, b.TITLE) .from(a) @@ -69,6 +74,10 @@ fun main(args: Array) { println("${it[b.TITLE]} by ${it[a.FIRST_NAME]} ${it[a.LAST_NAME]}") } + // Kotlin allows for destructuring any type that has component1(), component2(), component3(), ... methods + // into a tuple of local variables. These methods are currently added ad-hoc as operators further down in this + // file (scroll down). Future support for these methods is on the roadmap: + // https://github.com/jOOQ/jOOQ/issues/6245 header("Books and their authors with destructuring") for ((first, last, title) in ctx.select(a.FIRST_NAME, a.LAST_NAME, b.TITLE) .from(a) @@ -76,10 +85,13 @@ fun main(args: Array) { .orderBy(1, 2, 3)) println("$title by $first $last") + // Generated records (available through selectFrom()) contain getters (and setters) for each column. + // Kotlin allows for property access syntax of Java getters! header("An author") val author = ctx.selectFrom(a).where(a.ID.eq(1)).fetchOne(); println("${author.firstName} ${author.lastName}") + // Setters can profit from this property access syntax as well. header("Creating a new author") val author2 = ctx.newRecord(a); author2.firstName = "Alice" @@ -87,6 +99,8 @@ fun main(args: Array) { author2.store() println("${author2.firstName} ${author2.lastName}") + // With "imports" the namespace of an object for a closure, so we can avoid repeating the owner + // reference of the properties (and methods) every time header("Using the with 'clause'") with (author2) { firstName = "Bob" @@ -94,6 +108,7 @@ fun main(args: Array) { store() } + // This also works with tables, and their columns! header("With can be used with statements too, to locally import tables") with (a) { ctx.select(FIRST_NAME, LAST_NAME) @@ -105,21 +120,46 @@ fun main(args: Array) { } } + // Apply is a nice method that also closes over an object making all its methods available without the + // need to explicitly reference the owner object. That's very very nice for this type of dynamic SQL! + header("Conditional query clauses") + val filtering = true; + val joining = true; + ctx.select(a.FIRST_NAME, a.LAST_NAME, if (joining) count() else value("")) + .from(a) + .apply { if (filtering) where(a.ID.eq(1)) } + .apply { if (joining) join(b).on(a.ID.eq(b.AUTHOR_ID)) } + .apply { if (joining) groupBy(a.FIRST_NAME, a.LAST_NAME) } + .orderBy(a.ID) + .fetch { + println("${it[a.FIRST_NAME]} ${it[a.LAST_NAME]} ${if (joining) it[count()] else ""}") + } + + // Map (key, value) destructuring in foreach loops! header("As a map") for ((k, v) in author2.intoMap()) println("${k.padEnd(20)} = $v") + // More destructuring header("As maps") for (r in ctx.fetch(b)) for ((k, v) in r.intoMap()) println("${r[b.ID]}: ${k.padEnd(20)} = $v") + // We can use inline functions that we design ourselves very easily. E.g. ilike() is not part of the + // jOOQ API. It's defined further down in this file header("Custom jOOQ API extensions") println("${ctx.select(b.TITLE).from(b).where(b.TITLE.ilike("%animal%")).fetchOne(b.TITLE)}") + // Classic elvis operator, etc. header("Null safe dereferencing") println("${ctx.fetchOne(b, b.ID.eq(5))?.title ?: "book not found"}") + // Some operators can profit from "overloading" in Kotlin. E.g. + // - the unary minus operator "-" maps to Field.unaryMinus() + // - the binary plus operator "+" maps to Field.plus(Number) + // - the unary negation operator "!" maps to Condition.not() + // - etc. header("Operator overloading") ctx.select(a.FIRST_NAME, a.LAST_NAME, -count(), a.ID + 3) .from(a) @@ -131,6 +171,7 @@ fun main(args: Array) { println("Actor ID ${id - 3}: $first $last wrote ${-count} books") } + // Don't we wish for multiline strings in Java? header("Using multiline strings with the plain SQL API") ctx.resultQuery(""" SELECT * @@ -144,6 +185,8 @@ fun main(args: Array) { println("${it.intoMap()}") } + // If you parse a SQL (multiline) string with jOOQ, jOOQ will try to translate the syntax to + // the target dialect. header("Using multiline strings with the parser") val colX = field("x") val colY = field("y") @@ -162,6 +205,15 @@ fun main(args: Array) { } } +/** + * Just some nice formatted header printing + */ +fun header(text : String) { + println() + println(text) + println(text.toCharArray().map { _ -> '-' }.joinToString(separator = "")) +} + // Operators for Kotlin language-supported operator overloading // Support for these will be added to jOOQ where not already available: // https://github.com/jOOQ/jOOQ/issues/6246 diff --git a/jOOQ-examples/jOOQ-kotlin-example/src/main/kotlin/org/jooq/example/kotlin/Utils.kt b/jOOQ-examples/jOOQ-kotlin-example/src/main/kotlin/org/jooq/example/kotlin/Utils.kt deleted file mode 100644 index ffc6e731fe..0000000000 --- a/jOOQ-examples/jOOQ-kotlin-example/src/main/kotlin/org/jooq/example/kotlin/Utils.kt +++ /dev/null @@ -1,74 +0,0 @@ -/** - * This work is dual-licensed - * - under the Apache Software License 2.0 (the "ASL") - * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") - * ============================================================================= - * You may choose which license applies to you: - * - * - If you're using this work with Open Source databases, you may choose - * either ASL or jOOQ License. - * - If you're using this work with at least one commercial database, you must - * choose jOOQ License - * - * For more information, please visit http://www.jooq.org/licenses - * - * Apache Software License 2.0: - * ----------------------------------------------------------------------------- - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * jOOQ License and Maintenance Agreement: - * ----------------------------------------------------------------------------- - * Data Geekery grants the Customer the non-exclusive, timely limited and - * non-transferable license to install and use the Software under the terms of - * the jOOQ License and Maintenance Agreement. - * - * This library is distributed with a LIMITED WARRANTY. See the jOOQ License - * and Maintenance Agreement for more details: http://www.jooq.org/licensing - */ -package org.jooq.example.kotlin - -// This is from the stdlib, which doesn't support AutoCloseable yet -// https://github.com/JetBrains/kotlin/pull/807 -inline fun T.use(block: (T) -> R): R { - var closed = false - try { - return block(this) - } catch (e: Exception) { - closed = true - try { - close() - } catch (closeException: Exception) { - // eat the closeException as we are already throwing the original cause - // and we don't want to mask the real exception - - // TODO on Java 7 we should call - // e.addSuppressed(closeException) - // to work like try-with-resources - // http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html#suppressed-exceptions - } - throw e - } finally { - if (!closed) { - close() - } - } -} - -/** - * Just some nice formatted header printing - */ -fun header(text : String) { - println() - println(text) - println(text.toCharArray().map { _ -> '-' }.joinToString(separator = "")) -} \ No newline at end of file