https://kotlinlang.org logo
Join Slack
Powered by
# exposed
  • m

    Maarten

    05/30/2025, 8:43 AM
    Hey guys, I posted an issue on YouTrack a couple days ago about an N+1 problem that's supposed to be fixed, according to the docs. We're testing a switch from Hibernate to Exposed on our backend and making sure this works is a big blocker. Could you tell me if I'm missing anything?
  • m

    Mark lindsay

    05/31/2025, 1:25 AM
    Hello and good evening
  • a

    Adam Jarvis

    06/05/2025, 4:24 PM
    Was curious, do others have thoughts on adjusting
    where
    to be closer to
    andWhere
    ?
    Copy code
    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
    Copy code
    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
    ?
    l
    b
    • 3
    • 5
  • m

    Maarten

    06/13/2025, 1:55 PM
    Has anyone successfully managed to share transactions between spring jpa and exposed? Running exposed within a JPA @Transactional function, I get the error that there's no transaction in context
    b
    • 2
    • 3
  • k

    koji.lin

    06/18/2025, 11:45 PM
    Hi, I'm curious about how to handle coroutine and transaction well. There are some examples like

    https://www.youtube.com/watch?v=Y-iMbMro9tE▾

    have some information, but for the case like we want to wrap few repository call in the same transaction, I still have question. I'm using ktor so wanna make service suspend, also still use jdbc with suspend not r2dbc in beta.
    Copy code
    class 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.
    • 1
    • 1
  • a

    Anatoly

    06/19/2025, 7:12 PM
    Hello. Help please. How to create custom Operator with question mark? Exposed thinks this question is parameter...
    No value specified for parameter 3
    Copy code
    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
  • p

    Paul Rule

    06/24/2025, 2:59 AM
    Hi all, I’m looking for some insight into this issue I’m having with my Ktor/exposed application… If I do the following in a KTOR route, after the
    newSuspendedTransaction
    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.
    Copy code
    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.
    Copy code
    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...
    a
    k
    • 3
    • 14
  • h

    Hildebrandt Tobias

    06/29/2025, 6:06 PM
    Hey I just wrote this and wonder if there is an easier way to achieve the same thing. Background is that the
    value: 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.
    Copy code
    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
        }
    }
    • 1
    • 1
  • m

    Mario Andhika

    06/30/2025, 3:57 AM
    Is r2dbc “worth it” for coroutine-based systems?
    👀 1
    a
    • 2
    • 1
  • o

    Oleg Babichev

    07/02/2025, 7:12 PM
    🐙 Exposed 1.0.0-beta-3 is now available 🐙
    🎉 9
    😒 1
  • m

    Mario Andhika

    07/03/2025, 12:36 PM
    For branding, should I use the squid logo (the one on GitHub readme) or the database with arrow logo?
  • o

    Oleg Babichev

    07/08/2025, 11:57 AM
    🐙 Exposed 1.0.0-beta-4 with a hot fix is available 🐙
    🎉 2
  • a

    andrewg

    07/08/2025, 4:57 PM
    Eager loading is not respecting the order of my relationships? In my DAO definition, I have defined an order for my relationship according to the example here: https://www.jetbrains.com/help/exposed/dao-relationships.html#ordered-reference When I lazily access my relationship, this works as expected. However, when I use
    load
    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?
    • 1
    • 1
  • d

    David Hamilton

    07/11/2025, 6:51 AM
    Hi. Are there any plans to create LLMS documents for Exposed? Junie is currently getting horribly confused by the package rename in
    1.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.
    1️⃣ 1
    • 1
    • 1
  • r

    Rok Oblak

    07/12/2025, 8:09 PM
    Hi, is it currently impossible to use exposed with kotlinx datetime 0.7.2? Getting a
    Copy code
    java.lang.NoSuchMethodError: 'kotlinx.datetime.Instant kotlinx.datetime.ConvertersKt.toKotlinInstant(java.time.Instant)'
    j
    j
    • 3
    • 4
  • r

    Rok Oblak

    07/13/2025, 12:56 PM
    Separately - I suppose there will not be any nice migration guide until the stable 1.0.0 release? Seems like I would have to find and replace a bunch of imports, but then also many things got renamed and I have no idea to what (without checking the commits).
    s
    b
    • 3
    • 2
  • d

    d.bellingroth

    07/21/2025, 7:20 AM
    Hi there. I have a problem with exposed 1.0.0-beta4. If I use r2dbc connection pool, somehow transactions seem to be reused between different transaction blocks. In our application, we use postgresql advisory locks to coordinate some work. It could for example look like this:
    Copy code
    suspend 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?
  • m

    Mario Andhika

    07/31/2025, 2:17 AM
    Is it possible to initialize H2 database with a .sql file? I’m trying to setup H2 for testing, so it would load predefined data on start, but NOT save transactions back to the file. So I’d like to keep:
    Copy code
    Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
    But want to initialize the in-memory database with a data file
    ✅ 1
    l
    • 2
    • 2
  • e

    e5l

    08/01/2025, 7:32 AM
    🚀 Exposed 1.0.0-beta-5 is live! Review the changelog now before the stable launch—your feedback shapes the final release 👉 https://github.com/JetBrains/Exposed/releases/tag/1.0.0-beta-5
  • d

    dhkim

    08/01/2025, 8:31 AM
    How many beta releases are expected before reaching RC status?
  • e

    e5l

    08/01/2025, 9:00 AM
    We have only one small breaking change to merge. It is expected to have the release candidate end of month and the release in September
    K 6
    👍 6
    👍🏾 1
  • m

    Michael Friend

    08/04/2025, 7:49 PM
    are there any docs/examples on error handling when using exposed? Im trying to add some resiliency to my caching layer without broadly catching Exception. I found in the source code that at least some of the functions in exposed catch
    SqlException
    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 errors
  • p

    planerist

    08/07/2025, 2:19 PM
    I’m getting a “No transaction in context.” exception with the simplest code ever. What’s strange is that the first
    suspendTransaction
    works fine
    , but the second one fails. Full code snippet below (full code in comments):
    Copy code
    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?
    e
    o
    • 3
    • 9
  • r

    Roman Makeev

    08/09/2025, 8:24 PM
    Hello, I'm having an issue with Exposed, where it doesn't seems to work after ShadowJar relocation. It may seems, that this error should be addressed to GradleUp team, and I did it already. But may be you know the solution The main problem is after relocation, Exposed can't find kotlin.Any class. It happens only when using .nullable() inside tables The reproduce project located here https://github.com/makeevrserg/ExposedGradleFail
    • 1
    • 1
  • m

    minivac

    08/13/2025, 9:17 AM
    what is the new way to execute raw sql in exposed 1.0 (beta 5)? seems like
    exec
    function no longer exists and I don't see any alternative in the breaking changes docs
    • 1
    • 1
  • f

    Filip Lastic

    08/14/2025, 2:42 PM
    Hi. I would like to use interceptors for following two use cases but I am not sure how to implement it properly. Anyone have some example? 1. I would like to fill timestamps created_at and updated_at automatically. I can use clientDefault, but I would like to have the same value for both columns during insert 2. I would like to update updated_at to current timestamp in update statement I am using just exposed DSL, not DAO. I would like to make it generic, supporting insert/batchinsert/update/upsert.... Any ideas?
  • w

    Wilson Chuks

    08/16/2025, 1:45 PM
    Need confirmation on this
    batchUpsert
    shouldReturnGeneratedValues: Boolean = true,
    doc: Does it mean the updated rows won't be returned?
    Copy code
    /**
     * 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)
    }
  • a

    albrechtroehm

    08/22/2025, 10:20 AM
    Hello, I started playing around with the 1.0.0-beta and the r2dbc support, adding some tests to a existing project so we can soon switch over. I now started seeing these errors and I’m wondering if it’s generally discouraged to mix jdbc & r2dbc in one project or if it’s just a bad test setup, since the errors only appear for the whole test-suite but not for the test-classes in isolation. Maybe someone has some insights on this.
    Copy code
    12: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)
  • m

    Michael Friend

    08/26/2025, 8:27 PM
    Has anyone had (DSL) queries hang forever in a ktor route? Im using R2DBC and for some reason all of the queries i make that work just fine when not run from a ktor context (e.g. in a playground main function) but when i call them from a ktor route they hang forever and never return a result Example code is pretty boilerplate but this call never returns but only when running in a ktor route
    Copy code
    val versions =
      suspendTransaction {
        Applications
          .selectAll()
          .toList()
      }
    • 1
    • 1
  • h

    Horatio Thomas

    08/28/2025, 3:41 AM
    is there a way to write unit tests for exposed daos like room daos https://developer.android.com/training/data-storage/room/testing-db?
    l
    • 2
    • 1