Skip to content

Commit

Permalink
Added bulk insert
Browse files Browse the repository at this point in the history
  • Loading branch information
thiagocordeiro committed Dec 31, 2024
1 parent 01fbe51 commit 38b91f4
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 20 deletions.
23 changes: 19 additions & 4 deletions src/main/kotlin/io/tcds/orm/Table.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,26 @@ abstract class Table<E>(
) : ResultSetEntry<E> {
val columns = mutableListOf<Column<E, *>>()

fun insert(vararg entries: E) = entries.map {
val params = params(it)
val sql = "INSERT INTO $table (${params.columns()}) VALUES (${params.marks()})"
fun insert(vararg entries: E): List<JdbcStatement> {
val cols = columns.joinToString(",") { it.name }
val marks = columns.joinToString(",") { "?" }
val sql = "INSERT INTO $table ($cols) VALUES ($marks)"

connection.write(sql, params)
return entries.map { connection.write(sql, params(it)) }
}

fun bulkInsert(entries: List<E>): JdbcStatement {
val cols = columns.joinToString(",") { it.name }
val marks = columns.joinToString(",") { "?" }
val params = mutableListOf<Param<*>>()

val sql = StringBuilder().apply {
appendLine("INSERT INTO $table ($cols)")
appendLine(" VALUES")
appendLine(entries.joinToString(",\n") { params.addAll(params(it)).let { " ($marks)" } })
}.toString().trim()

return connection.write(sql, params)
}

fun loadBy(where: Statement, order: OrderStatement<E> = emptyList()): E? = findBy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import io.tcds.orm.Param
import io.tcds.orm.param.InstantParam
import java.time.Instant

class TimestampColumn<Entity>(
class DatetimeColumn<Entity>(
name: String,
value: (Entity) -> Instant,
) : Column<Entity, Instant>(name, value) {
override fun columnType(): String = "TIMESTAMP"
override fun columnType(): String = "DATETIME"
override fun valueParam(value: Instant): Param<Instant> = InstantParam(this.name, value)
override fun entryParam(entry: Entity): Param<Instant> = InstantParam(this.name, valueOf(entry))
override fun ddl(): String = "`$name` DATETIME(6) NOT NULL"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import io.tcds.orm.Param
import io.tcds.orm.param.nullable.NullableInstantParam
import java.time.Instant

class NullableTimestampColumn<Entity>(name: String, value: (Entity) -> Instant?) : Column<Entity, Instant?>(name, value) {
class NullableDatetimeColumn<Entity>(name: String, value: (Entity) -> Instant?) : Column<Entity, Instant?>(name, value) {
override fun columnType(): String = "DATETIME NULL"
override fun valueParam(value: Instant?): Param<Instant?> = NullableInstantParam(this.name, value)
override fun entryParam(entry: Entity): Param<Instant?> = NullableInstantParam(this.name, valueOf(entry))
Expand Down
8 changes: 4 additions & 4 deletions src/main/kotlin/io/tcds/orm/extension/column.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ fun <E> Table<E>.doubleNullable(name: String, value: (E) -> Double?) = column(Nu

fun <E> Table<E>.bool(name: String, value: (E) -> Boolean) = column(BooleanColumn(name = name, value = value))

fun <E> Table<E>.date(name: String, value: (E) -> Instant) = column(TimestampColumn(name = name, value = value))
fun <E> Table<E>.datetime(name: String, value: (E) -> Instant) = column(TimestampColumn(name = name, value = value))
fun <E> Table<E>.dateNullable(name: String, value: (E) -> Instant?) = column(NullableTimestampColumn(name = name, value = value))
fun <E> Table<E>.datetimeNullable(name: String, value: (E) -> Instant?) = column(NullableTimestampColumn(name = name, value = value))
fun <E> Table<E>.date(name: String, value: (E) -> Instant) = column(DatetimeColumn(name = name, value = value))
fun <E> Table<E>.datetime(name: String, value: (E) -> Instant) = column(DatetimeColumn(name = name, value = value))
fun <E> Table<E>.dateNullable(name: String, value: (E) -> Instant?) = column(NullableDatetimeColumn(name = name, value = value))
fun <E> Table<E>.datetimeNullable(name: String, value: (E) -> Instant?) = column(NullableDatetimeColumn(name = name, value = value))

fun <E, T : Enum<*>> Table<E>.enum(name: String, value: (E) -> T) = column(EnumColumn(name = name, value = value))
fun <E, T> Table<E>.json(name: String, value: (E) -> T) = column(JsonColumn(name = name, value = value))
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/io/tcds/orm/statement/Statement.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ package io.tcds.orm.statement

import io.tcds.orm.Condition
import io.tcds.orm.Param
import io.tcds.orm.column.TimestampColumn
import io.tcds.orm.column.DatetimeColumn
import io.tcds.orm.extension.*
import io.tcds.orm.param.InstantParam
import java.time.Instant
import java.time.LocalDateTime

data class Statement(val conditions: MutableList<Pair<Operator, Condition>>) {
companion object {
fun <E> deletedAt() = TimestampColumn<E>("deleted_at") { Instant.now() }
fun <E> deletedAt() = DatetimeColumn<E>("deleted_at") { Instant.now() }
}

fun toStmt() = conditions.toStmt()
Expand Down
52 changes: 52 additions & 0 deletions src/test/kotlin/io/tcds/orm/TableBulkInsertTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package io.tcds.orm

import fixtures.Address
import fixtures.AddressTable
import io.mockk.*
import io.tcds.orm.connection.Connection
import io.tcds.orm.extension.toInstant
import io.tcds.orm.param.BooleanParam
import io.tcds.orm.param.InstantParam
import io.tcds.orm.param.StringParam
import org.junit.jupiter.api.Test

class TableBulkInsertTest {
private val connection: Connection = mockk()
private val table = AddressTable(connection)

private val first = Address.galaxyHighway()
private val second = Address.galaxyAvenue()

@Test
fun `given the entry then invoke write in the connection`() {
every { connection.write(any(), any()) } returns mockk()
val entries = listOf(first, second)

table.bulkInsert(entries)

verify {
connection.write(
"""
INSERT INTO addresses (id,street,number,main,created_at)
VALUES
(?,?,?,?,?),
(?,?,?,?,?)
""".trimIndent(),
listOf(
// first
StringParam(table.id.name, first.id),
StringParam(table.street.name, first.street),
StringParam(table.number.name, first.number),
BooleanParam(table.main.name, first.main),
InstantParam(table.createdAt.name, first.createdAt.toInstant()),
// second
StringParam(table.id.name, second.id),
StringParam(table.street.name, second.street),
StringParam(table.number.name, second.number),
BooleanParam(table.main.name, second.main),
InstantParam(table.createdAt.name, second.createdAt.toInstant()),
),
)
}
}
}
2 changes: 1 addition & 1 deletion src/test/kotlin/io/tcds/orm/TableDdlTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class TableDdlTest {
`double` DECIMAL(10, 2) NOT NULL,
`string` VARCHAR(255) NOT NULL,
`boolean` BOOLEAN NOT NULL,
`instant` TIMESTAMP NOT NULL
`instant` DATETIME(6) NOT NULL
);
""".trimIndent(),
table.ddl(),
Expand Down
2 changes: 1 addition & 1 deletion src/test/kotlin/io/tcds/orm/TableInsertTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class TableInsertTest {

verify {
connection.write(
"INSERT INTO addresses (id, street, number, main, created_at) VALUES (?, ?, ?, ?, ?)",
"INSERT INTO addresses (id,street,number,main,created_at) VALUES (?,?,?,?,?)",
listOf(
StringParam(table.id.name, address.id),
StringParam(table.street.name, address.street),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import java.sql.PreparedStatement
import java.sql.Timestamp
import java.time.Instant

class TimestampColumnTest {
class DatetimeColumnTest {
private val stmt: PreparedStatement = mockk()
private val instant = Instant.now()
private val column = TimestampColumn<ColumnTypes>("foo") { it.instant }
private val column = DatetimeColumn<ColumnTypes>("foo") { it.instant }

@Test
fun `given a column then describe its configuration`() = Assertions.assertEquals("foo" to "TIMESTAMP", column.describe())
fun `given a column then describe its configuration`() = Assertions.assertEquals("foo" to "DATETIME", column.describe())

@Test
fun `given a date value when it is not null then set the value into the statement`() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import java.sql.Timestamp
import java.sql.Types
import java.time.Instant

class NullableTimestampColumnTest {
class NullableDatetimeColumnTest {
private val stmt: PreparedStatement = mockk()
private val instant = Instant.now()
private val column = NullableTimestampColumn<ColumnTypes>("foo") { it.instant }
private val column = NullableDatetimeColumn<ColumnTypes>("foo") { it.instant }

@Test
fun `given a column then describe its configuration`() = Assertions.assertEquals("foo" to "DATETIME NULL", column.describe())
Expand Down

0 comments on commit 38b91f4

Please sign in to comment.