Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,49 @@ import kotlin.collections.plus
*/
internal object JdbcQueriesRepository {
/**
* Executes database operations using the specified data source.
* @param lambda Operation to execute within the database session
* @return Result of the operation
* Executes a database operation using a dedicated session.
*
* If the underlying connection has auto-commit disabled, the transaction is
* committed on success and rolled back on failure.
*/
private fun <T> AdminJdbcTable.usingDataSource(lambda: (Session) -> T): T {
private fun <T> AdminJdbcTable.usingDataSource(block: (Session) -> T): T {
val dataSource =
getDatabaseKey()?.let { KtorAdminHikariCP.dataSource(it) }
getDatabaseKey()?.let(KtorAdminHikariCP::dataSource)
?: KtorAdminHikariCP.dataSource()

val session = session(dataSource)
val invoke = using(session, lambda)
session.close()
return invoke

return try {
session.executeInTransaction(block)
} finally {
session.close()
}
}

/**
* Executes the given block inside the current session.
*
* When auto-commit is disabled, the transaction is committed if the operation
* succeeds or rolled back if an exception occurs.
*/
private inline fun <T> Session.executeInTransaction(block: (Session) -> T): T {
val transactional = !connection.autoCommit

return try {
val result = block(this)

if (transactional) {
connection.commit()
}

result
} catch (t: Throwable) {
if (transactional) {
runCatching { connection.rollback() }
}

throw t
}
}

/**
Expand Down Expand Up @@ -883,6 +914,7 @@ internal object JdbcQueriesRepository {
val joinColumn = reference.foreignKey
"LEFT JOIN $joinTable AS $joinAlias ON $currentTable.$referenceColumn = $joinAlias.$joinColumn"
}

else -> null
}
if (joinCondition != null && joinCondition !in joins) {
Expand Down Expand Up @@ -936,6 +968,7 @@ internal object JdbcQueriesRepository {
val joinColumn = reference.foreignKey
"LEFT JOIN $joinTable AS $joinAlias ON $currentTable.$referenceColumn = $joinAlias.$joinColumn"
}

else -> null
}
if (joinCondition != null && joinCondition !in joins) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ir.amirroid.ktoradmin.repository

class JdbcQueriesRepositoryAutoCommitTest : JdbcQueriesRepositoryIntegrationTest() {
override val autoCommit: Boolean = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue

class JdbcQueriesRepositoryIntegrationTest {
abstract class JdbcQueriesRepositoryIntegrationTest {
protected open val autoCommit: Boolean = true

private lateinit var dataSource: HikariDataSource
private lateinit var usersTable: TestJdbcTable
private lateinit var rolesTable: TestJdbcTable
Expand Down Expand Up @@ -74,6 +76,7 @@ class JdbcQueriesRepositoryIntegrationTest {
username = "sa"
password = ""
maximumPoolSize = 4
isAutoCommit = autoCommit
},
)
KtorAdminHikariCP.defaultCustom(dataSource)
Expand Down Expand Up @@ -1053,7 +1056,10 @@ class JdbcQueriesRepositoryIntegrationTest {
@Test
fun `getAllReferences returns empty list when table is empty`() {
deleteAllUsers()
dataSource.connection.use { it.execute("DELETE FROM roles") }
dataSource.connection.use { conn ->
conn.execute("DELETE FROM roles")
if (!conn.autoCommit) conn.commit()
}
val items = JdbcQueriesRepository.getAllReferences(rolesTable)
assertEquals(emptyList(), items)
}
Expand Down Expand Up @@ -1820,6 +1826,7 @@ class JdbcQueriesRepositoryIntegrationTest {
conn.execute(
"INSERT INTO users (id, name, age, active, status, score, nickname, profile_id, organization_id) VALUES ($i, 'User$i', ${20 + i}, true, 'ACTIVE', $i.0, NULL, NULL, 1)",
)
if (!conn.autoCommit) conn.commit()
}
}
val config = KtorAdminConfiguration()
Expand Down Expand Up @@ -1901,6 +1908,7 @@ class JdbcQueriesRepositoryIntegrationTest {
dataSource.connection.use { conn ->
conn.execute("DELETE FROM user_roles")
conn.execute("DELETE FROM users")
if (!conn.autoCommit) conn.commit()
}
}

Expand Down Expand Up @@ -1995,6 +2003,10 @@ class JdbcQueriesRepositoryIntegrationTest {
connection.execute("INSERT INTO roles (id, label) VALUES (2, 'Editor')")
connection.execute("INSERT INTO roles (id, label) VALUES (3, 'Auditor')")
connection.execute("INSERT INTO user_roles (user_id, role_id) VALUES (1, 1)")

if (!autoCommit) {
connection.autoCommit = true
}
}

private fun Connection.execute(sql: String) = createStatement().use { it.execute(sql) }
Expand Down
2 changes: 2 additions & 0 deletions sample/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,6 @@ dependencies {
implementation(libs.hibernate.core)
implementation(libs.javax.persistence.api)
implementation(libs.hibernate.validator)

implementation(libs.hikari)
}
17 changes: 17 additions & 0 deletions sample/src/main/kotlin/ir/amirreza/Admin.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package ir.amirreza

import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import io.ktor.server.application.Application
import io.ktor.server.application.install
import ir.amirreza.action.MyCustomAction
Expand Down Expand Up @@ -45,6 +47,21 @@ fun Application.configureAdmin(database: Database) {
password = "your_password",
driver = JDBCDrivers.POSTGRES
)
// val dataSource =
// HikariDataSource(
// HikariConfig().apply {
// jdbcUrl = "jdbc:postgresql://localhost:5432/postgres"
// username = "amirreza"
// password = "your_password"
// driverClassName = JDBCDrivers.POSTGRES
// isAutoCommit = false
// },
// )
//
// jdbc(
// key = null,
// dataSource = dataSource
// )
mongo(
key = null,
databaseName = "0@localhost",
Expand Down