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)