From 72e18db2d81ca3bd7ecdf199fd6c26303c6303f6 Mon Sep 17 00:00:00 2001 From: Lukas-Kaufmann Date: Fri, 12 Apr 2024 11:59:31 +0200 Subject: [PATCH] add upsert --- CHANGES.md | 6 +++ .../kotlin/ls/jooq/execute/RecordMutators.kt | 15 ++++++ .../ls/jooq/execute/RecordMutatorsTest.kt | 52 +++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 511b4f7..43cb49c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Changes +## Unreleased + +### Feature + +- added `DSLContext.upsert` + ## 2024-03-26 / 0.3.0 ### Feature diff --git a/src/main/kotlin/ls/jooq/execute/RecordMutators.kt b/src/main/kotlin/ls/jooq/execute/RecordMutators.kt index 77e25a7..cb8eb8a 100644 --- a/src/main/kotlin/ls/jooq/execute/RecordMutators.kt +++ b/src/main/kotlin/ls/jooq/execute/RecordMutators.kt @@ -59,3 +59,18 @@ suspend fun > DSLContext.insertAndRefreshRecord(record: U record.from(inserted) return record } + +suspend fun > DSLContext.upsert(record: R): R = + insertInto(record.table) + .set(record) + .onDuplicateKeyUpdate() + .set(record) + .returning() + .awaitFirst() + +suspend inline fun > DSLContext.upsert(init: R.() -> Unit): R { + val constructor = checkNotNull(R::class.java.getConstructor()) { "no default constructor found for ${R::class}" } + val record = constructor.newInstance() + record.init() + return upsert(record) +} diff --git a/src/test/kotlin/ls/jooq/execute/RecordMutatorsTest.kt b/src/test/kotlin/ls/jooq/execute/RecordMutatorsTest.kt index 2c240f8..babce40 100644 --- a/src/test/kotlin/ls/jooq/execute/RecordMutatorsTest.kt +++ b/src/test/kotlin/ls/jooq/execute/RecordMutatorsTest.kt @@ -6,6 +6,8 @@ import io.kotest.core.spec.style.FreeSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe +import ls.jooq.execute.awaitFirst +import kotlinx.coroutines.reactive.awaitFirst import kotlinx.coroutines.reactive.awaitSingle import ls.jooq.db.generated.Tables import ls.jooq.db.generated.tables.records.AuthorRecord @@ -15,6 +17,10 @@ class RecordMutatorsTest : FreeSpec({ val ctx = DBExtension.dslContext + afterTest { + ctx.deleteFrom(Tables.AUTHOR).awaitFirst() + } + "DSLContext.insertAndRefreshRecord" - { "should insert the record" { @@ -60,6 +66,52 @@ class RecordMutatorsTest : FreeSpec({ } } + "DSLContext.upsert()" - { + + "should insert if the given primary key doesn't exist" { + val count: Int = ctx.selectCount().from(Tables.AUTHOR).awaitFirst() + count shouldBe 0 + val author = ctx.upsert { + id = 1 + firstName = "Max" + lastName = "Muster" + } + + val foundAuthor = ctx.selectFrom(Tables.AUTHOR).awaitFirst() + foundAuthor shouldBe author + } + + "should insert if no primary key is set and the primary key is generated" { + ctx.selectCount().from(Tables.AUTHOR).awaitFirst() shouldBe 0 + val author = ctx.upsert { + firstName = "Max" + lastName = "Muster" + } + + val foundAuthor = ctx.selectFrom(Tables.AUTHOR).awaitFirst() + foundAuthor shouldBe author + } + + "should update values of existing record with same primary key" { + ctx.selectCount().from(Tables.AUTHOR).awaitFirst() shouldBe 0 + val author = ctx.upsert { + firstName = "Max" + lastName = "Muster" + } + + val updatedAuthor = ctx.upsert { + id = author.id + firstName = "Not max" + lastName = "Muster" + } + + val foundAuthor = ctx.selectFrom(Tables.AUTHOR).awaitFirst() + foundAuthor.firstName shouldBe "Not max" + updatedAuthor.id shouldBe foundAuthor.id + author.id shouldBe foundAuthor.id + } + } + "DSLContext.updateAndExecute()" - { "should create an update statement for the given record and execute it" {