From 1a70f3f03b3938099b638299169ccd2967f3fc6c Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Thu, 18 Jul 2019 15:59:07 +0200 Subject: [PATCH] feature #11: select with extra parameters --- .gitignore | 3 +- .../fr/postgresjson/connexion/Connection.kt | 68 ++++++++------- .../fr/postgresjson/connexion/Requester.kt | 82 ++++++++++--------- .../kotlin/fr/postgresjson/ConnectionTest.kt | 22 +++++ .../kotlin/fr/postgresjson/RequesterTest.kt | 14 ++++ .../query/Test/selectOneWithParameters.sql | 2 +- 6 files changed, 124 insertions(+), 67 deletions(-) diff --git a/.gitignore b/.gitignore index 670f3a4..83c49aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Project exclude paths /out/ /build/ -.gradle \ No newline at end of file +.gradle +/var/log/ \ No newline at end of file diff --git a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt index db9478d..2ebcac9 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt @@ -13,13 +13,16 @@ import fr.postgresjson.utils.LoggerDelegate import org.slf4j.Logger import java.util.concurrent.CompletableFuture +typealias SelectOneCallback = QueryResult.(T?) -> Unit +typealias SelectCallback = QueryResult.(List) -> Unit +typealias SelectPaginatedCallback = QueryResult.(Paginated) -> Unit interface Executable { - fun > select(sql: String, typeReference: TypeReference, values: List = emptyList()): R? - fun > select(sql: String, typeReference: TypeReference, values: Map): R? - fun > select(sql: String, typeReference: TypeReference>, values: List = emptyList()): List - fun > select(sql: String, typeReference: TypeReference>, values: Map): List - fun > select(sql: String, page: Int, limit: Int, typeReference: TypeReference>, values: Map): Paginated + fun > select(sql: String, typeReference: TypeReference, values: List = emptyList(), block: SelectOneCallback = {}): R? + fun > select(sql: String, typeReference: TypeReference, values: Map, block: SelectOneCallback = {}): R? + fun > select(sql: String, typeReference: TypeReference>, values: List = emptyList(), block: SelectCallback = {}): List + fun > select(sql: String, typeReference: TypeReference>, values: Map, block: SelectCallback = {}): List + fun > select(sql: String, page: Int, limit: Int, typeReference: TypeReference>, values: Map, block: SelectPaginatedCallback = {}): Paginated fun exec(sql: String, values: List = emptyList()): QueryResult fun exec(sql: String, values: Map): QueryResult fun sendQuery(sql: String): QueryResult @@ -47,47 +50,52 @@ class Connection( fun inTransaction(f: (Connection) -> CompletableFuture) = connect().inTransaction(f) - override fun > select(sql: String, typeReference: TypeReference, values: List): R? { + override fun > select(sql: String, typeReference: TypeReference, values: List, block: (QueryResult, R?) -> Unit): R? { val result = exec(sql, compileArgs(values)) val json = result.rows[0].getString(0) return if (json === null) { null } else { serializer.deserialize(json, typeReference) + }.also { + block(result, it) } } - inline fun > selectOne(sql: String, values: List = emptyList()): R? = - select(sql, object: TypeReference() {}, values) + inline fun > selectOne(sql: String, values: List = emptyList(), noinline block: SelectOneCallback = {}): R? = + select(sql, object: TypeReference() {}, values, block) - override fun > select(sql: String, typeReference: TypeReference, values: Map): R? { + override fun > select(sql: String, typeReference: TypeReference, values: Map, block: (QueryResult, R?) -> Unit): R? { return replaceArgs(sql, values) { - select(this.sql, typeReference, this.parameters) + select(this.sql, typeReference, this.parameters, block) } } - inline fun > selectOne(sql: String, values: Map): R? = - select(sql, object: TypeReference() {}, values) + inline fun > selectOne(sql: String, values: Map, noinline block: SelectOneCallback = {}): R? = + select(sql, object: TypeReference() {}, values, block) - override fun > select(sql: String, typeReference: TypeReference>, values: List): List { + override fun > select(sql: String, typeReference: TypeReference>, values: List, block: (QueryResult, List) -> Unit): List { val result = exec(sql, compileArgs(values)) val json = result.rows[0].getString(0) return if (json === null) { listOf>() as List } else { serializer.deserializeList(json, typeReference) + }.also { + block(result, it) } } - inline fun > select(sql: String, values: List = emptyList()): List = - select(sql, object: TypeReference>() {}, values) + inline fun > select(sql: String, values: List = emptyList(), noinline block: SelectCallback = {}): List = + select(sql, object: TypeReference>() {}, values, block) override fun > select( sql: String, page: Int, limit: Int, typeReference: TypeReference>, - values: Map + values: Map, + block: (QueryResult, Paginated) -> Unit ): Paginated { val offset = (page - 1) * limit val newValues = values @@ -95,11 +103,11 @@ class Connection( .plus("limit" to limit) val line = replaceArgs(sql, newValues) { - exec(this.sql, compileArgs(this.parameters)).rows[0] + exec(this.sql, compileArgs(this.parameters)) } return line.run { - val json = getString(0) + val json = rows[0].getString(0) val entities = if (json === null) { listOf>() as List } else { @@ -109,8 +117,10 @@ class Connection( entities, offset, limit, - getInt("total") ?: error("The query not return total") + rows[0].getInt("total") ?: error("The query not return total") ) + }.also { + block(line, it) } } @@ -118,22 +128,24 @@ class Connection( sql: String, page: Int, limit: Int, - values: Map = emptyMap() + values: Map = emptyMap(), + noinline block: SelectPaginatedCallback = {} ): Paginated = - select(sql, page, limit, object: TypeReference>() {}, values) + select(sql, page, limit, object: TypeReference>() {}, values, block) override fun > select( sql: String, typeReference: TypeReference>, - values: Map + values: Map, + block: (QueryResult, List) -> Unit ): List { return replaceArgs(sql, values) { - select(this.sql, typeReference, this.parameters) + select(this.sql, typeReference, this.parameters, block) } } - inline fun > select(sql: String, values: Map): List = - select(sql, object: TypeReference>() {}, values) + inline fun > select(sql: String, values: Map, noinline block: SelectCallback = {}): List = + select(sql, object: TypeReference>() {}, values, block) override fun exec(sql: String, values: List): QueryResult { return stopwatchQuery(sql, values) { @@ -156,9 +168,9 @@ class Connection( private fun compileArgs(values: List): List { return values.map { if (it is EntityI<*>) { - val json = serializer.serialize(it) - serializer.collection.set>(it as EntityI) - json + serializer.serialize(it).apply { + serializer.collection.set>(it as EntityI) + } } else { it } diff --git a/src/main/kotlin/fr/postgresjson/connexion/Requester.kt b/src/main/kotlin/fr/postgresjson/connexion/Requester.kt index ff1599d..7ff62aa 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Requester.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Requester.kt @@ -78,35 +78,39 @@ class Requester( return sql } - override fun > select(typeReference: TypeReference, values: List): R? { - return connection.select(this.toString(), typeReference, values) + override fun > select(typeReference: TypeReference, values: List, block: (QueryResult, R?) -> Unit): R? { + return connection.select(this.toString(), typeReference, values, block) } - inline fun > selectOne(values: List = emptyList()): R? = select(object: TypeReference() {}, values) + inline fun > selectOne(values: List = emptyList(), noinline block: SelectOneCallback = {}): R? = + select(object: TypeReference() {}, values, block) - override fun > select(typeReference: TypeReference, values: Map): R? { - return connection.select(this.toString(), typeReference, values) + override fun > select(typeReference: TypeReference, values: Map, block: (QueryResult, R?) -> Unit): R? { + return connection.select(this.toString(), typeReference, values, block) } - inline fun > selectOne(values: Map): R? = select(object: TypeReference() {}, values) + inline fun > selectOne(values: Map, noinline block: SelectOneCallback = {}): R? = + select(object: TypeReference() {}, values, block) - override fun > select(typeReference: TypeReference>, values: List): List { - return connection.select(this.toString(), typeReference, values) + override fun > select(typeReference: TypeReference>, values: List, block: (QueryResult, List) -> Unit): List { + return connection.select(this.toString(), typeReference, values, block) } - inline fun > select(values: List = emptyList()): List = select(object: TypeReference>() {}, values) + inline fun > select(values: List = emptyList(), noinline block: SelectCallback = {}): List = + select(object: TypeReference>() {}, values, block) - override fun > select(typeReference: TypeReference>, values: Map): List { - return connection.select(this.toString(), typeReference, values) + override fun > select(typeReference: TypeReference>, values: Map, block: (QueryResult, List) -> Unit): List { + return connection.select(this.toString(), typeReference, values, block) } - inline fun > select(values: Map): List = select(object: TypeReference>() {}, values) + inline fun > select(values: Map, noinline block: SelectCallback = {}): List = + select(object: TypeReference>() {}, values, block) - override fun > select(page: Int, limit: Int, typeReference: TypeReference>, values: Map): Paginated { - return connection.select(this.toString(), page, limit, typeReference, values) + override fun > select(page: Int, limit: Int, typeReference: TypeReference>, values: Map, block: (QueryResult, Paginated) -> Unit): Paginated { + return connection.select(this.toString(), page, limit, typeReference, values, block) } - inline fun > select(page: Int, limit: Int, values: Map = emptyMap()): Paginated = - select(page, limit, object: TypeReference>() {}, values) + inline fun > select(page: Int, limit: Int, values: Map = emptyMap(), noinline block: SelectPaginatedCallback = {}): Paginated = + select(page, limit, object: TypeReference>() {}, values, block) override fun exec(values: List): QueryResult { return connection.exec(sql, values) @@ -125,52 +129,56 @@ class Requester( /** * Select One entity with list of parameters */ - override fun > select(typeReference: TypeReference, values: List): R? { + override fun > select(typeReference: TypeReference, values: List, block: (QueryResult, R?) -> Unit): R? { val args = compileArgs(values) val sql = "SELECT * FROM ${definition.name} ($args)" - return connection.select(sql, typeReference, values) + return connection.select(sql, typeReference, values, block) } - inline fun > selectOne(values: List = emptyList()): R? = select(object: TypeReference() {}, values) + inline fun > selectOne(values: List = emptyList(), noinline block: SelectOneCallback = {}): R? = + select(object: TypeReference() {}, values, block) /** * Select One entity with named parameters */ - override fun > select(typeReference: TypeReference, values: Map): R? { + override fun > select(typeReference: TypeReference, values: Map, block: (QueryResult, R?) -> Unit): R? { val args = compileArgs(values) val sql = "SELECT * FROM ${definition.name} ($args)" - return connection.select(sql, typeReference, values) + return connection.select(sql, typeReference, values, block) } - inline fun > selectOne(values: Map): R? = select(object: TypeReference() {}, values) + inline fun > selectOne(values: Map, noinline block: SelectOneCallback = {}): R? = + select(object: TypeReference() {}, values, block) /** * Select list of entities with list of parameters */ - override fun > select(typeReference: TypeReference>, values: List): List { + override fun > select(typeReference: TypeReference>, values: List, block: (QueryResult, List) -> Unit): List { val args = compileArgs(values) val sql = "SELECT * FROM ${definition.name} ($args)" - return connection.select(sql, typeReference, values) + return connection.select(sql, typeReference, values, block) } - inline fun > select(values: List = emptyList()): List = select(object: TypeReference>() {}, values) + inline fun > select(values: List = emptyList(), noinline block: SelectCallback = {}): List = + select(object: TypeReference>() {}, values, block) /** * Select list of entities with named parameters */ - override fun > select(typeReference: TypeReference>, values: Map): List { + override fun > select(typeReference: TypeReference>, values: Map, block: (QueryResult, List) -> Unit): List { val args = compileArgs(values) val sql = "SELECT * FROM ${definition.name} ($args)" - return connection.select(sql, typeReference, values) + return connection.select(sql, typeReference, values, block) } - inline fun > select(values: Map): List = select(object: TypeReference>() {}, values) + inline fun > select(values: Map, noinline block: SelectCallback = {}): List = + select(object: TypeReference>() {}, values, block) - override fun > select(page: Int, limit: Int, typeReference: TypeReference>, values: Map): Paginated { + override fun > select(page: Int, limit: Int, typeReference: TypeReference>, values: Map, block: (QueryResult, Paginated) -> Unit): Paginated { val offset = (page - 1) * limit val newValues = values .plus("offset" to offset) @@ -179,10 +187,10 @@ class Requester( val args = compileArgs(newValues) val sql = "SELECT * FROM ${definition.name} ($args)" - return connection.select(sql, page, limit, typeReference, values) + return connection.select(sql, page, limit, typeReference, values, block) } - inline fun > select(page: Int, limit: Int, values: Map = emptyMap()): Paginated = - select(page, limit, object: TypeReference>() {}, values) + inline fun > select(page: Int, limit: Int, values: Map = emptyMap(), noinline block: SelectPaginatedCallback = {}): Paginated = + select(page, limit, object: TypeReference>() {}, values, block) override fun exec(values: List): QueryResult { val args = compileArgs(values) @@ -230,13 +238,13 @@ class Requester( val connection: Connection override fun toString(): String - fun > select(typeReference: TypeReference, values: List = emptyList()): R? - fun > select(typeReference: TypeReference, values: Map): R? + fun > select(typeReference: TypeReference, values: List = emptyList(), block: SelectOneCallback = {}): R? + fun > select(typeReference: TypeReference, values: Map, block: SelectOneCallback = {}): R? - fun > select(typeReference: TypeReference>, values: List = emptyList()): List - fun > select(typeReference: TypeReference>, values: Map): List + fun > select(typeReference: TypeReference>, values: List = emptyList(), block: SelectCallback = {}): List + fun > select(typeReference: TypeReference>, values: Map, block: SelectCallback = {}): List - fun > select(page: Int, limit: Int, typeReference: TypeReference>, values: Map): Paginated + fun > select(page: Int, limit: Int, typeReference: TypeReference>, values: Map, block: SelectPaginatedCallback = {}): Paginated fun exec(values: List = emptyList()): QueryResult fun exec(values: Map): QueryResult diff --git a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt index 9c72b10..e07fdbb 100644 --- a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt +++ b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt @@ -151,4 +151,26 @@ class ConnectionTest(): TestAbstract() { assertEquals(result.total, 10) assertEquals(result.offset, 0) } + + @Test + fun `selectOne with extra parameters`() { + val params: Map = mapOf( + "first" to "ff", + "third" to 123, + "seconde" to "sec" + ) + val result: ObjTest3? = connection.selectOne( + """ + SELECT json_build_object('first', :first::text, 'seconde', :seconde::text, 'third', :third::int), 'plop'::text as other + """.trimIndent(), + params + ) { + assertEquals("ff", it!!.first) + assertEquals("plop", rows[0].getString("other")) + } + assertNotNull(result) + assertEquals("ff", result!!.first) + assertEquals("sec", result.seconde) + assertEquals(123, result.third) + } } \ No newline at end of file diff --git a/src/test/kotlin/fr/postgresjson/RequesterTest.kt b/src/test/kotlin/fr/postgresjson/RequesterTest.kt index a430691..212d8b0 100644 --- a/src/test/kotlin/fr/postgresjson/RequesterTest.kt +++ b/src/test/kotlin/fr/postgresjson/RequesterTest.kt @@ -117,4 +117,18 @@ class RequesterTest: TestAbstract() { Assert.assertEquals(result.total, 10) Assert.assertEquals(result.offset, 0) } + + @Test + fun `call selectOne on query with extra parameter`() { + val resources = File(this::class.java.getResource("/sql/query").toURI()) + val obj: ObjTest = Requester(getConnextion()) + .addQuery(resources) + .getQuery("Test/selectOneWithParameters") + .selectOne(mapOf("name" to "myName")) { + assertEquals("myName", it!!.name) + Assert.assertEquals("plop", rows[0].getString("other")) + }!! + + assertEquals("myName", obj.name) + } } \ No newline at end of file diff --git a/src/test/resources/sql/query/Test/selectOneWithParameters.sql b/src/test/resources/sql/query/Test/selectOneWithParameters.sql index 3e90686..26b1b8e 100644 --- a/src/test/resources/sql/query/Test/selectOneWithParameters.sql +++ b/src/test/resources/sql/query/Test/selectOneWithParameters.sql @@ -1 +1 @@ -select json_build_object('id', 2, 'name', :name::text); \ No newline at end of file +select json_build_object('id', 2, 'name', :name::text), 'plop'::text as other; \ No newline at end of file