Skip to content

Ktorm

Databases & CachingDatabase/ORMKotlin

What it is

Ktorm is a lightweight Kotlin ORM with a strongly-typed DSL whose operators mirror SQL closely, plus an optional entity-object layer.

Declare table objects, then build queries with a fluent DSL. Ktorm's operators make conditions read naturally while staying type-checked.

Installation

implementation("org.ktorm:ktorm-core:4.1.1")

Getting started

The smallest useful thing you can do with it, and what each part means.

Table objects and typed queries
object Books : Table<Nothing>("books") {
    val id = int("id").primaryKey()
    val title = varchar("title")
    val year = int("year")
    val authorId = int("author_id")
}

val database = Database.connect(dataSource)

val results = database
    .from(Books)
    .innerJoin(Authors, on = Books.authorId eq Authors.id)
    .select(Books.title, Authors.name)
    .where { (Books.year greaterEq 1990) and (Authors.country eq "US") }
    .orderBy(Books.year.desc())
    .limit(10)
    .map { row -> row[Books.title] to row[Authors.name] }
Infix operators such as `eq` and `greaterEq` keep the condition readable while still being type-checked — comparing an Int column to a String will not compile.

Advanced usage

Where the library earns its place over a simpler alternative.

Aggregation and bulk operations
val countsByYear = database
    .from(Books)
    .select(Books.year, count(Books.id))
    .groupBy(Books.year)
    .having { count(Books.id) greater 5 }
    .associate { it.getInt(1) to it.getInt(2) }

database.batchInsert(Books) {
    incoming.forEach { book ->
        item {
            set(Books.title, book.title)
            set(Books.year, book.year)
        }
    }
}

database.useTransaction {
    database.update(Books) {
        set(it.year, 1966)
        where { it.id eq 42 }
    }
}
batchInsert issues one statement for the whole collection rather than looping — the difference between a slow import and a fast one is usually exactly this.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Column not found in the result row
The column was not included in select(). Ktorm returns only what you asked for.
Connection leaks
Use a pooled DataSource such as HikariCP and useTransaction, rather than managing connections manually.

Best practices

  • Use the SQL DSL rather than the entity layer for anything with joins or aggregation.
  • Wrap multi-statement work in useTransaction.
  • Use batchInsert and batchUpdate for bulk writes.
  • Consider Exposed if you want JetBrains' backing and a larger community; Ktorm is smaller and more SQL-literal.

Background

Why it exists, and what it was reacting to.

Ktorm's distinguishing feature is operator overloading: comparisons and boolean combinations use ordinary Kotlin operators, so queries read like the SQL they generate.