The following code produces a `Can't init value ou...
# exposed
a
The following code produces a
Can't init value outside the transaction
, but its in a transaction. Has anyone encountered this before?
Copy code
override suspend fun createUser(
        name: String,
        username: String,
        bio: String,
        igUsername: String?,
        tiktokUsername: String?,
        imageUrl: String,
        imageWidth: Int,
        imageHeight: Int
    ): User = dbQuery {
        val avatar = Image.new(UUID.randomUUID()) {
            url = imageUrl
            height = imageHeight
            width = imageWidth
        }
        val user = User.new(UUID.randomUUID()) {
            this.name = name
            this.username = username
            this.bio = bio
            this.igUsername = igUsername
            this.tiktokUsername = tiktokUsername
            this.avatar = avatar
        }
        return@dbQuery user
    }
Copy code
internal suspend fun <T> dbQuery(block: () -> T): T =
    withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
        transaction {
            if (System.getenv("environment").contentEquals("debug", ignoreCase = true)) {
                addLogger(StdOutSqlLogger)
            }
            block()
        }
    }
e
I think you need to use suspendedTransaction with coroutines
a
@Emil Kantis Does
newSuspendedTransaction
work in this case to replace
transaction
or should I do this?
Copy code
internal suspend fun <T> dbQuery(block: () -> T): T =
    withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
        transaction {
            if (System.getenv("environment").contentEquals("debug", ignoreCase = true)) {
                addLogger(StdOutSqlLogger)
            }
            suspend { 
                suspendedTransaction { 
                    block()
                }
            }
        }.invoke()
    }
e
I think the former, but I have only dabbled briefly using exposed + coroutines. It makes sense though - that a transaction could be bound to the current thread while the coroutine is not, and once the coroutine resumes execution on another thread you would run into issues..
a
Unfortunately, neither worked
e
Tried this?
Copy code
internal suspend fun <T> dbQuery(block: () -> T): T =
    newSuspendedTransaction(<http://Dispatchers.IO|Dispatchers.IO>) {
            if (System.getenv("environment").contentEquals("debug", ignoreCase = true)) {
                addLogger(StdOutSqlLogger)
            }
            block()
    }
a
Actually I found the error
Its something to do with with converting the DAO model into a new object. I use an extension function called toPresentable to do it. It seems to be tyring to mutate a value even though I am only using getters
Copy code
class User(id: EntityID<UUID>) : UUIDEntity(id) {
    companion object : UUIDEntityClass<User>(UsersTable)
    var name by UsersTable.name
    var username by UsersTable.username
    var bio by UsersTable.bio
    var igUsername by UsersTable.igUrl
    var tiktokUsername by UsersTable.tikTokUrl
    val posts by Post referrersOn PostsTable.author
    var avatar by Image referencedOn  UsersTable.image
}
Copy code
internal data class UserPresentable(
    val uuid: String,
    val username: String,
    val name: String,
    val bio: String,
    val igUsername: String?,
    val tiktokUsername: String?,
    val avatar: ImagePresentable,
) {
    data class ImagePresentable(val url: String, val height: Int, val width: Int)
}
Copy code
internal fun Image.toPresentable(): UserPresentable.ImagePresentable {
    return UserPresentable.ImagePresentable(
        url = url,
        height = height,
        width = width
    )
}

internal fun User.toPresentable(): UserPresentable {
    return UserPresentable(
        uuid = id.value.toString(),
        username = username,
        tiktokUsername = tiktokUsername,
        igUsername = igUsername,
        name = name,
        bio = bio,
        avatar = avatar.toPresentable(),
    )
}
@Emil Kantis
e
Okay.. could be related to Exposed needing to load more data when you try to convert it (maybe
avatar.toPresentable()
forces it to load another entity that wasn’t fetched in the initial query?)
a
That’s exactly it. Thank you! I am not sure why exposed would be setup this way. I am going to open an issue.
331 Views