Maarten
05/30/2025, 8:43 AMMark lindsay
05/31/2025, 1:25 AMAdam Jarvis
06/05/2025, 4:24 PMwhere
to be closer to andWhere
?
fun where(predicate: Op<Boolean>): Query {
where?.let {
error("WHERE clause is specified twice. Old value = '$it', new value = '$predicate'")
}
where = predicate
return this
}
^ to me, I end up just using andWhere
everywhere because of the built in handling of chaining
fun Query.andWhere(andPart: SqlExpressionBuilder.() -> Op<Boolean>) = adjustWhere {
val expr = Op.build { andPart() }
if (this == null) expr else this and expr
}
Why always throwing the error for chaining where
?Maarten
06/13/2025, 1:55 PMkoji.lin
06/18/2025, 11:45 PMclass HogeService {
suspend fun getArticle(id: String){
newSuspendedTransaction {
val article = articleRepo.find(id)
// what ever other repo
viewCountRepo.increase(article.id)
}
// other suspend logic here, like fetching some info from remote
....
return article
}
}
suspend fun <T : Any> dbQuery(context: CoroutineContext?, statement: suspend () -> T?): T? =
TransactionManager.currentOrNull()?.withSuspendTrnsaction(context, statement)
?:newSuspendedTransaction(context, statement = statement)
class ArticleRepo {
suspend fun find(id:String) {
return dbQuery(Dispatchers.IO) {
//....
}
}
}
// Same patter as ArticleRepo
class ViewCountRepo...
So this should be open transaction at service layer and pass transaction to repo to run on other Dispatcher.
Is this usage correct? Especially I saw https://www.jetbrains.com/help/exposed/transactions.html#working-with-coroutines has phrase like below that maybe my example is wrong due to transaction may start from ktor's eventloop dispatcher then used in IO and IO may use different thread in Dispatchers.IO. Maybe this means two unrelated coroutines but ok for parent/child relationship?
> Please note that such code remains blocking (as it still uses JDBC) and you should not try to share a transaction between multiple threads as it may lead to undefined behavior.
So curious if it's correct or I should do in another way.Anatoly
06/19/2025, 7:12 PMNo value specified for parameter 3
class JsonContainsAnyExpression(
expression: Expression<*>,
elements: List<String>
) : CustomOperator<Boolean>("?|", BooleanColumnType(), expression, arrayLiteral(elements))
fun Expression<*>.containsAny(elements: List<String>): JsonContainsAnyExpression =
JsonContainsAnyExpression(this, elements)
addLogger(StdSQLLogger) - если добавляешь логгер, вообще выдает:
NullPointerException
Paul Rule
06/24/2025, 2:59 AMnewSuspendedTransaction
block is close, TransactionManager.currentOrNull()
returns a not null value - I would expect it to be null.
If I remove the delay()
call, it will work as expected.
get("/") {
require(TransactionManager.currentOrNull() == null) { "1 - transaction should not exist at the start" }
newSuspendedTransaction {
require(TransactionManager.currentOrNull() != null) { "2 - transaction should exist now" }
delay(2000) // Simulate some processing time - if I remove this delay things will work as I expect
}
// this next line fails
require(TransactionManager.currentOrNull() == null) { "3 - transaction should not exist at the end" }
call.respondText("OK")
}
To make it even more mysterious I cannot get this scenario to fail in a test - the following code works fine, and TransactionManager.currentOrNull()
returns null after the newSuspendedTransaction
scope closes.
suspend fun main() {
val database = TestPostgresDatabase
val fixtures = Fixtures(database).initialise()
coroutineScope {
launch(Dispatchers.IO) {
require(TransactionManager.currentOrNull() == null) { "1 - transaction should not exist at the start" }
newSuspendedTransaction {
require(TransactionManager.currentOrNull() != null) { "2 - transaction should exist now" }
delay(2000) // Simulate some processing time
}
require(TransactionManager.currentOrNull() == null) { "3 - transaction should not exist at the end" }
println("Transaction completed successfully ${TransactionManager.currentOrNull()}")
}.join()
}
}
Can anyone tell me what I’m doing wrong or missing please? What I'm trying to is test if a transaction is already in progress or not using TransactionManager.currentOrNull()
so I know when to initialise my connection with row level security parameters. For the most part its working well but there is this one condition where there is a network call - which must result in yielding similar to the delay above, and then I get left with a closed connection...Hildebrandt Tobias
06/29/2025, 6:06 PMvalue: String
comes from an API call over HTTP.
So I want to always compare against a given String
in the where clause.
I can't filter after the fact in Kotlin, because it's part of a paging scheme.
I tried poking AI about a better way, but they were no help in this case.
fun <T : Any, V: T?> Column<V>.compareWithString(value: String): Op<Boolean> {
if(columnType is EntityIDColumnType<*>){
val checkType = (columnType as EntityIDColumnType<T>).idColumn.columnType
val typedValue = when (checkType) {
is IntegerColumnType -> value.toInt() as T
is LongColumnType -> value.toLong() as T
is UUIDColumnType -> UUID.fromString(value) as T
is StringColumnType -> value as T
is BooleanColumnType -> value.toBooleanStrictOrNull() as T
else -> error("Unsupported column type for comparison: $columnType")
}
return (this as Column<EntityID<T>>) eq typedValue
} else {
val typedValue = when (columnType) {
is IntegerColumnType -> value.toInt() as V
is LongColumnType -> value.toLong() as V
is UUIDColumnType -> UUID.fromString(value) as V
is StringColumnType -> value as V
is BooleanColumnType -> value.toBooleanStrictOrNull() as V
else -> error("Unsupported column type for comparison: $columnType")
}
return this eq typedValue
}
}
Mario Andhika
06/30/2025, 3:57 AMOleg Babichev
07/02/2025, 7:12 PMMario Andhika
07/03/2025, 12:36 PMOleg Babichev
07/08/2025, 11:57 AMandrewg
07/08/2025, 4:57 PMload
to eagerly load this relationship up-front, it entirely disregards my defined order. This has been confirmed with a sql logger, in which no ORDER BY is added to the SQL query.
Is this known limitation and, if so, are there any workarounds?David Hamilton
07/11/2025, 6:51 AM1.0.0-beta-1
and I wondered if there were any machine-friendly resources I could point an AI to?
I guess, ideally, the resources would also highlight different styles of API usage between Core/DSL and DAO.Rok Oblak
07/12/2025, 8:09 PMjava.lang.NoSuchMethodError: 'kotlinx.datetime.Instant kotlinx.datetime.ConvertersKt.toKotlinInstant(java.time.Instant)'
Rok Oblak
07/13/2025, 12:56 PMd.bellingroth
07/21/2025, 7:20 AMsuspend fun main() {
R2dbcDatabase.connect("r2dbc:pool:<postgresql://postgres:postgres@localhost:54325/postgres>")
val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
val lockId = 1337L
val jobs = mutableListOf<kotlinx.coroutines.Job>()
for (i in 1..10) {
jobs += coroutineScope.launch {
suspendTransaction {
println("Task $i trying to acquire lock with ID $lockId")
exec("SELECT pg_advisory_xact_lock(?)", listOf(
LongColumnType() to lockId
))
println("Task $i acquired lock with ID $lockId")
async {
println("Task $i started")
delay(1000) // Simulate some work
println("Task $i completed after 1 second")
}.await()
exec("SELECT pg_advisory_unlock(?)", listOf(
LongColumnType() to lockId
))
println("Task $i released lock with ID $lockId")
}
}
}
jobs.joinAll()
}
Without the connection pool everything works as expected and the jobs wait for each other. But as soon as I enable the connection pool all jobs are able to get the advisory lock at the same time and run in parallel.
I just wanted to make sure, that I'm not getting something totally wrong. Am I using something not as intended?Mario Andhika
07/31/2025, 2:17 AMDatabase.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
But want to initialize the in-memory database with a data filee5l
08/01/2025, 7:32 AMdhkim
08/01/2025, 8:31 AMe5l
08/01/2025, 9:00 AMMichael Friend
08/04/2025, 7:49 PMSqlException
and wrap them in ExposedSqlException
, but for other things like SchemaUtils.create(...) the PSQLException
is whats thrown instead of being wrapped when the connection to the database fails. Both of those extend SQLException
so i could catch that but i'd like to have some more fine grained control over handling different errorsplanerist
08/07/2025, 2:19 PMsuspendTransaction
works fine, but the second one fails.
Full code snippet below (full code in comments):
kotlin
suspendTransaction(db = db) {
SchemaUtils.create(UsersTable)
}
suspendTransaction(db = db) {
// ===> Fails here
UsersTable.insert {
it[name] = "Alice"
}
}
Any ideas on what might be going wrong?Roman Makeev
08/09/2025, 8:24 PMminivac
08/13/2025, 9:17 AMexec
function no longer exists and I don't see any alternative in the breaking changes docsFilip Lastic
08/14/2025, 2:42 PMWilson Chuks
08/16/2025, 1:45 PMbatchUpsert
shouldReturnGeneratedValues: Boolean = true,
doc:
Does it mean the updated rows won't be returned?
/**
* Represents the SQL statement that either batch inserts new rows into a table, or updates the existing rows if insertions violate unique constraints.
*
* @param data Collection of values to use in batch upsert.
* @param keys (optional) Columns to include in the condition that determines a unique constraint match. If no columns are provided,
* primary keys will be used. If the table does not have any primary keys, the first unique index will be attempted.
* @param onUpdate Lambda block with an [UpdateStatement] as its argument, allowing values to be assigned to the UPDATE clause.
* To specify manually that the insert value should be used when updating a column, for example within an expression
* or function, invoke `insertValue()` with the desired column as the function argument.
* If left null, all columns will be updated with the values provided for the insert.
* @param onUpdateExclude List of specific columns to exclude from updating.
* If left null, all columns will be updated with the values provided for the insert.
* @param where Condition that determines which rows to update, if a unique violation is found.
* @param shouldReturnGeneratedValues Specifies whether newly generated values (for example, auto-incremented IDs) should be returned.
* See [Batch Insert](<https://github.com/JetBrains/Exposed/wiki/DSL#batch-insert>) for more details.
* @sample org.jetbrains.exposed.sql.tests.shared.dml.UpsertTests.testBatchUpsertWithNoConflict
*/
fun <T : Table, E : Any> T.batchUpsert(
data: Iterable<E>,
vararg keys: Column<*>,
onUpdate: (UpsertBuilder.(UpdateStatement) -> Unit)? = null,
onUpdateExclude: List<Column<*>>? = null,
where: (SqlExpressionBuilder.() -> Op<Boolean>)? = null,
shouldReturnGeneratedValues: Boolean = true,
body: BatchUpsertStatement.(E) -> Unit
): List<ResultRow> {
return batchUpsert(data.iterator(), null, onUpdate, onUpdateExclude, where, shouldReturnGeneratedValues, keys = keys, body = body)
}
albrechtroehm
08/22/2025, 10:20 AM12:14:21.307 [Test worker @kotlinx.coroutines.test runner#298] ERROR t.s.h.common.database.ExposedDB - Database operation threw:
java.lang.ClassCastException: class org.jetbrains.exposed.v1.r2dbc.transactions.TransactionManager cannot be cast to class org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager (org.jetbrains.exposed.v1.r2dbc.transactions.TransactionManager and org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager are in unnamed module of loader 'app')
at org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager$Companion.getManager(TransactionManager.kt:162)
at org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager$Companion.managerFor(TransactionManager.kt:156)
at org.jetbrains.exposed.v1.jdbc.transactions.JdbcTransactionInterfaceKt.getTransactionManager(JdbcTransactionInterface.kt:37)
at org.jetbrains.exposed.v1.jdbc.transactions.TransactionManagerKt.keepAndRestoreTransactionRefAfterRun(TransactionManager.kt:470)
at org.jetbrains.exposed.v1.jdbc.transactions.TransactionManagerKt.transaction(TransactionManager.kt:322)
at org.jetbrains.exposed.v1.jdbc.transactions.TransactionManagerKt.transaction$default(TransactionManager.kt:317)
Michael Friend
08/26/2025, 8:27 PMval versions =
suspendTransaction {
Applications
.selectAll()
.toList()
}
Horatio Thomas
08/28/2025, 3:41 AM