Ktorm: a Kotlin ORM that starts at JDBC and stops there
A lightweight ORM framework for Kotlin with strong-typed SQL DSL and sequence APIs.
At a glance
- What is it?
- A lightweight ORM for Kotlin built directly on pure JDBC, with table objects as Kotlin classes, a typed SQL DSL, sequence-style queries, and entity binding that runs through KSP.
- Who is it for?
- Ktorm occupies a specific niche: teams on Kotlin who want query code that looks like Kotlin, compiles like Kotlin, and still maps one to one onto SQL they can read in a log. Three practical consequences follow from that.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 113 days ago.
- What is it written in?
- Mainly Kotlin, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A JDBC ORM that rules out everything else
Ktorm describes itself as a lightweight and efficient ORM framework for Kotlin directly based on pure JDBC. That qualifier does most of the work in the sentence. Many Kotlin data libraries sit on top of jOOQ, Hibernate, or Exposed, which means you inherit an abstraction layer whether or not it fits your database. Ktorm starts one level down, at the JDBC API, and adds Kotlin syntax on top of it.
The feature list opens by ruling things out rather than promising them: no configuration files, no XML, no annotations, even no third-party dependencies. Nothing is loaded from disk, nothing is scanned at startup. A schema is a set of Kotlin objects and a query is a Kotlin expression, so both are checked by the Kotlin compiler instead of being interpreted by a separate mapping layer.
The upside for a Kotlin team is direct. Renaming a column is a compile error, not a runtime surprise, and IDE completion covers query building the same way it covers any other function call. The cost is equally visible. You write SQL shaped Kotlin, and the framework does not attempt to hide that, which is why most of the README is spent on the DSL rather than on convenience layers.
Table objects are the whole schema definition
The Quick Start does not open with a connection. It opens with two table objects:
object Departments : Table<Nothing>("t_department") {
val id = int("id").primaryKey()
val name = varchar("name")
val location = varchar("location")
}`Departments` and `Employees` each extend `Table<Nothing>`, name the physical table in the constructor, and declare every column with a call that pairs a Kotlin type with a database column name. The `Nothing` type argument looks strange on first read and is worth pausing over. A table object here is not an entity, so it has no element type; `Table<Nothing>` states that the table never yields typed records by itself. Columns are the only thing it carries, which is exactly why they can be referenced inside a query with no entity class in existence anywhere.
`Employees` carries six columns in the sample, including `managerId`, which points back at `Employees.id`. That self reference is what makes the join example later in the README work, and it costs nothing to express because the column is a typed int with a name. Naming is manual as well: `hireDate` maps to `hire_date` because the string handed to `date()` says so. Nothing infers the convention and nothing has to.
Query code that maps one to one onto SQL
With tables in place, filtering is an infix expression rather than a string:
database
.from(Employees)
.select(Employees.name)
.where { (Employees.departmentId eq 1) and (Employees.name like "%vince%") }
.forEach { row ->
println(row[Employees.name])
}The README shows the generated SQL next to it, a select with an aliased column and two parenthesised predicates, and the claim it makes is that the output corresponds exactly to the Kotlin that produced it. That correspondence is the design argument. Because `eq`, `and`, and `like` are operators and functions rather than string fragments, a malformed query is a type error, and reading the generated SQL becomes a way to check what the code meant rather than a way to discover what it did.
Optional conditions get their own builder. The `whereWithConditions` example in the README accumulates predicates only when a flag is set, which is the readable answer to the usual complaint that dynamic filters turn into string concatenation. Aggregation, unions, and joins follow the same pattern: an aliased copy of the table feeds `groupBy` with a `having` clause on the aggregate, `unionAll` takes other query objects as arguments, and a join aliases `Employees` twice before selecting through `leftJoin`. Entity mapping is offered alongside this DSL rather than instead of it, with entity classes declared as interfaces extending `Entity<E>` and bound to a table object.
Entity binding runs through KSP, and 4.2 rewrote that part
The repository layout makes the entity story concrete. Alongside `ktorm-core` and `ktorm-global` there are four modules with the `ktorm-ksp` prefix: `ktorm-ksp-annotations`, `ktorm-ksp-compiler`, `ktorm-ksp-spi`, and `ktorm-ksp-compiler-maven-plugin`. Entity code is generated at build time by the Kotlin Symbol Processor rather than by a runtime proxy, so the KSP version you compile against is part of your toolchain contract.
Release v4.2.0, published 2026-05-17, is where that contract changed. The release notes list two items: an upgrade to Kotlin 2.0.21 and support for KSP2. Because Kotlin deprecated a large set of compiler APIs in 2.0, the maintainers rewrote the Maven plugin to support the new processor generation, and they call it out as a break change. Anyone using `ktorm-ksp-compiler-maven-plugin` has to replace their existing plugin block with a new one that names an execution id, a `generate-sources` goal, and a `processorOptions` section holding `ktorm.catalog`, `ktorm.schema`, and a `ktorm.dbNamingStrategy` value.
The follow-up release v4.2.1 on 2026-06-20 fixed an NPE in the KSP Maven plugin, and the repository's last recorded push carries the same date. That makes the plugin the piece to test first after an upgrade, and it also puts a floor under any claim about project activity: the most recent recorded work is a bug fix in the build tooling rather than a feature.
Five dialect modules, one version file, no XML
Database coverage is handled by sibling modules rather than by a driver configuration block: `ktorm-support-mysql`, `ktorm-support-oracle`, `ktorm-support-postgresql`, `ktorm-support-sqlite`, and `ktorm-support-sqlserver`. Five vendors, one module each, and a shared core that speaks plain JDBC. The README's own claim about dialects is that the design is extensible enough to write your own support module for a database that is not on that list.
The build is Gradle Kotlin DSL, with `build.gradle.kts` and `settings.gradle.kts` at the root, a `buildSrc` directory, a `detekt.yml` for static analysis, and a plain text file named `ktorm.version` that holds the version string shared across modules. That last file explains the `${ktorm.version}` placeholder you see in the documentation: it is not a documentation convention but a value the build reads.
Two details are easy to miss when scanning the tree. Documentation is triplicated, with `README.md` alongside `README_cn.md` and `README_jp.md`, and the README itself points readers to ktorm.org for anything past the quick start. The project also carries standard governance files, `CONTRIBUTING.md` and `CODE_OF_CONDUCT.md`, which is a reasonable signal for a library that other teams build on top of.
Where the README lags behind the repository
Three points of drift are worth naming before you copy anything out of the Quick Start. The Gradle snippet uses `compile "org.ktorm:ktorm-core:${ktorm.version}"`, and the `compile` configuration was removed from Gradle years ago, so that line will not run as printed in any current Gradle project. The claim of no third-party dependencies also sits awkwardly beside four KSP modules and a `ktorm-jackson` integration in the tree; the intent is that the core has no external dependencies, but the project as a whole plainly does. And the quick start queries a MySQL JDBC URL as its only example while the repository ships support modules for five databases.
The public numbers round out the picture. The repository has roughly 2,344 stars, 161 forks, and 103 open issues, which is a high open-issue count relative to that star count for a library at this size, though it is not unusual for a project of this age and activity level. Release cadence tells a related story: v4.1.1 landed on 2024-09-03, then nothing until v4.2.0 in May 2026, an interval of about twenty months, followed by the 4.2.1 plugin fix a month later. A long quiet stretch followed by a large migration change is the pattern to plan around, particularly if you depend on the Maven KSP plugin.
Editorial conclusion
Ktorm occupies a specific niche: teams on Kotlin who want query code that looks like Kotlin, compiles like Kotlin, and still maps one to one onto SQL they can read in a log. Three practical consequences follow from that. Schema definition lives in table objects, so a column rename is a compiler error rather than a production incident. Dialect work goes through the five `ktorm-support-*` modules, one per database, which is where you check before committing to a vendor. And entity binding is optional: the KSP toolchain exists for teams that want entities, but it is the part most likely to require maintenance work from you, since Ktorm 4.2.0 rewrote the Maven plugin for KSP2 and changed the configuration you have to write. The framework is Apache 2.0, published to Maven Central under the `org.ktorm` group, and its documentation site at ktorm.org is referenced from the README as the place to go beyond the quick start.
Frequently asked questions
What is Ktorm?
Ktorm is a lightweight ORM framework for Kotlin built directly on the pure JDBC API. It provides a strong-typed SQL DSL for writing queries and entity sequence APIs for reading results, and it generates the SQL statements rather than requiring you to write them by hand. It is open source under the Apache 2.0 license and published to Maven Central under the org.ktorm group.
Does Ktorm need XML mapping files or annotations?
No. The README lists the absence of configuration files, XML, and annotations among its headline features. Table schemas are declared as Kotlin objects extending Table, and queries are built from those objects through a DSL, so the compiler checks both instead of a mapping layer interpreting them at runtime.
Which databases does Ktorm support?
The repository ships one support module per vendor: ktorm-support-mysql, ktorm-support-oracle, ktorm-support-postgresql, ktorm-support-sqlite, and ktorm-support-sqlserver, all sitting on top of a shared ktorm-core. The README notes the design is extensible, so a dialect that is not listed can be added as a custom extension module.
What changed in Ktorm 4.2.0 for Maven users?
Version 4.2.0, published 2026-05-17, upgraded Kotlin to 2.0.21 and added support for KSP2. Because Kotlin deprecated many compiler APIs in 2.0, the KSP Maven plugin was rewritten, so users of ktorm-ksp-compiler-maven-plugin must change their plugin configuration in pom.xml, adding an execution with the generate-sources goal and a processorOptions section for catalog, schema, and naming strategy.
Does Ktorm support entity classes?
Yes, alongside the SQL DSL. Entity classes are declared as interfaces extending Entity<E> with a companion Entity.Factory, then bound to table objects. Code generation for that binding runs at build time through KSP, using the ktorm-ksp-annotations, ktorm-ksp-compiler, and ktorm-ksp-spi modules.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/kotlin-orm-ktorm)