ORM for Kotlin: Comparison and Examples (Exposed, Ktorm, Hibernate)

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

ORM для Kotlin: сравнение и примеры



When developing in Kotlin, choosing a suitable ORM (Object-Relational Mapping) is a key step that affects performance, code readability, and maintainability. In this article, we will compare three popular solutions: Exposed (JetBrains official library), Ktorm (lightweight DSL-oriented ORM), and Hibernate (classic heavyweight). We will analyze their strengths, features, and provide working examples.



1. Exposed — JetBrains' Choice



Exposed is an ORM developed specifically for Kotlin by JetBrains. It offers two approaches: DSL (type-safe) and DAO (Data Access Objects). The main advantage is full integration with the language: tables are described as Kotlin objects, and queries are checked at compile time.



Example (DSL approach):

import org.jetbrains.exposed.sql.*import org.jetbrains.exposed.sql.transactions.transaction

object Users : Table() { val id = integer("id").autoIncrement() val name = varchar("name", 50) val email = varchar("email", 100).uniqueIndex() override val primaryKey = PrimaryKey(id)}

fun main() { Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver") transaction { SchemaUtils.create(Users) Users.insert { it[name] = "Ivan" it[email] = "ivan@example.com" } val result = Users.selectAll().map { "${it[Users.name]} - ${it[Users.email]}" } println(result) // [Ivan - ivan@example.com] }}


Features:

  • Type-safe queries (fewer runtime errors).
  • No need for reflection (faster than Hibernate).
  • Migration support via exposed-migration.
  • No need for XML configuration or annotations — everything is in code.


2. Ktorm — Lightweight and Flexible



Ktorm is a minimalist ORM that uses Kotlin DSL for building SQL queries. Unlike Exposed, it does not generate SQL automatically but allows writing queries almost like pure SQL, with type checking.



Example:

import me.liuwj.ktorm.database.Databaseimport me.liuwj.ktorm.entity.*import me.liuwj.ktorm.schema.*

interface User : Entity<User> { companion object : Entity.Factory<User>() val id: Int val name: String val email: String}

object Users : Table<User>("users") { val id = int("id").primaryKey().bindTo { it.id } val name = varchar("name").bindTo { it.name } val email = varchar("email").bindTo { it.email }}

fun main() { val database = Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver") database.sequenceOf(Users).toList() // Get all users // Insertion: val user = User { name = "Petr" email = "petr@example.com" } database.users.add(user)}


Features:

  • Very close to SQL — easy to optimize queries.
  • Small JAR size (~200 KB).
  • Extension support: ktorm-jackson, ktorm-spring-support.
  • Requires explicit field binding (bindTo), which increases boilerplate.


3. Hibernate — Proven Standard



Hibernate is a powerful but heavyweight ORM from the Java world. In Kotlin, it is used via annotations or XML. Hibernate provides full control over caching, lazy loading, and schema generation. However, due to reflection and proxy classes, there may be issues with data class (requires open).



Example:

import javax.persistence.*

@Entity@Table(name = "users")data class User( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Int = 0, @Column(nullable = false) val name: String, @Column(nullable = false, unique = true) val email: String)

Recommendations