English | 简体中文
Kronos is a modern ORM framework designed for Kotlin based on the compiler plugin, which is suitable for both backend and mobile applications, support multi-database. Powerful, high performance, easy to use.
Based on the KCP implementation of expression tree parsing and concatenation, Kronos has powerful performance, expressiveness, and a concise, semantic writing style, make the operation of the database has become more simple.
Official Website | Documentation | Getting Started (Repo) | Discord | QQ群
❓ Why Kronos?
- Leverages the full JVM ecosystem (drivers, logging, pools) and is designed with future Kotlin Multiplatform in mind.
- Compiler-plugin powered and coroutine-friendly. Absolutely no reflection at runtime for core operations, enabling high performance and low GC overhead.
- Concise, expressive Kotlin-first DSL using native operators
==,>,<,in, etc. (not.eq,.gt,.lt). Strong static typing everywhere. - Rich features out of the box: transactions, cross-database queries, schema create/sync (tables/indexes/comments), serialization/deserialization, and powerful cascading without FKs (one-to-one / one-to-many / many-to-many).
- Built-ins: logical deletion, optimistic lock, create/update timestamps; all customizable.
- Easy to integrate with any stack (Spring, Ktor, Vert.x, Solon, Android). See example projects below.
- Native SQL with named parameters when you need full control.
- Compile-time mappers to/from Map and meta APIs for KPojo with near-zero overhead.
- Looking for architecture diagrams, module internals, and deep‑dive guides? See the Developer Docs.
- Who should read this:
- Contributors and maintainers extending or modifying Kronos.
- Advanced users who want to understand internals and design trade‑offs.
- Integrators and plugin authors building logging/JDBC/codegen extensions or embedding Kronos into your platform.
Here are some of the simplest examples showing how to use Kronos-ORM for database operations.
// Define a data class extending KPojo
data class User(
@PrimaryKey(identity = true) val id: Long? = null,
val username: String? = null,
val age: Int? = null
) : KPojo
// We can use dataSource.table.createTable<User>() to create the table,
// or dataSource.table.syncTable<User>() to synchronize the table structure.
dataSource.table.createTable<User>()
// Insert a record into table `user` (username = "test", age = 18), and get the id of the inserted record
// we can use List<User>.insert().execute() to batch insert
val id = User(username = "test", age = 18).insert().execute().lastInsertId
// Query records from table `user` where `age` is 18
val listOfUser = User(age = 18).select().queryList()
// Update records in table `user` where `id` is lastInsertId, set `age` to 19
User(id = id, age = 19).update().by { it.id }.execute()
// Delete records from table `user` where `id` is lastInsertId
User(id = id).delete().execute()- JDK 8+
- Kotlin 2.1.0+
- Maven 3.6.3+ or Gradle 6.8.3+
Please make sure your kotlin plugin for ide supports kotlin 2.1.0 or higher.
If you built failed in Intellij IDEA and build with Maven, please try to enable the following setting: Settings / Build, Execution, Deployment / Build Tools / Maven / Runner / Delegate IDE build/run actions to Maven.
Details
plugins {
id("com.kotlinorm.kronos-gradle-plugin") version "0.0.5"
}
dependencies {
implementation("com.kotlinorm:kronos-core:0.0.5")
}Details
plugins {
id 'com.kotlinorm.kronos-gradle-plugin' version '0.0.5'
}
dependencies {
implementation 'com.kotlinorm:kronos-core:0.0.5'
}Details
<project>
<dependencies>
<dependency>
<groupId>com.kotlinorm</groupId>
<artifactId>kronos-core</artifactId>
<version>0.0.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<compilerPlugins>
<plugin>kronos-maven-plugin</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>com.kotlinorm</groupId>
<artifactId>kronos-maven-plugin</artifactId>
<version>0.0.5</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>Kronos-ORM supports multiple databases:
- MySQL
- PostgreSQL
- SQLite
- Oracle
- Microsoft SQL Server
- DB2
- Sybase
- H2
- OceanBase
- DM8
- GaussDB
Notes:
- Feature breadth may vary slightly by dialect. The core DSL and upsert/lock syntax provide cross-database compatibility where possible.
- The integration test module currently focuses on MySQL and PostgreSQL for CI examples.
You can set default data source by:
Kronos.init {
dataSource = { KronosBasicWrapper(SomeDataSource()) }
// for example:
//dataSource = { KronosBasicWrapper(MysqlDataSource("jdbc:mysql://localhost:3306/test", "root", "***")) }
//dataSource = { KronosBasicWrapper(HikariDataSource().apply { jdbcUrl = "jdbc:mysql://localhost:3306/test" ... }) }
//dataSource = { KronosBasicWrapper(BasicDataSource().apply { url = "jdbc:sqlite:path/to/db" ... }) }
}More details about connecting to the database and use dynamic data source or multiple data sources, please refer to the docs.
@Table(name = "tb_movie")
@TableIndex("idx_name", ["name"], Mysql.KIndexType.UNIQUE, Mysql.KIndexMethod.BTREE)
data class Movie(
@PrimaryKey(identity = true)
val id: Long? = null, // primary key and auto increment
@Necessary val name: String? = null, // movie name
val directorId: Long? = null, // director id
@Cascade(["directorId"], ["id"])
val director: Director? = null, // cascade table & one-to-many
val relations: List<MovieActorRelation>? = null, // reference list & many-to-many
@Serialize
val type: List<String>? = null, // deserialize from string
@Column("movie_summary")
val summary: String? = null, // summary with column name
@Version val version: Long? = null, // version for optimistic lock
@LogicDelete val deleted: Boolean? = null, // logic delete
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
@UpdateTime val updateTime: String? = null, // update time and date format
@CreateTime val createTime: LocalDateTime? = null // create time
) : KPojo { // KPojo is a marker interface
var actors: List<Actor>? by manyToMany(::relations) // many-to-many
}Coming soon... #10
//is table exists
dataSource.table.exists<Movie>()
// create table
dataSource.table.createTable<Movie>()
// drop table
dataSource.table.dropTable<Movie>()
// or
dataSource.table.dropTable("tb_movie")
//sync table structure
dataSource.table.syncTable<Movie>()// single query
val listOfUser: List<User> = User()
.select { it.id + it.username }
.where { it.id < 10 && it.age >= 18 }
.distinct()
.groupBy { it.id }
.orderBy { it.username.desc() }
.queryList()
// with Pagination
val (total, list) = User()
.select { it.id + it.username }
.where { it.id < 10 && it.username like "a%" }
.distinct()
.groupBy { it.id }
.orderBy { it.username.desc() }
.page(1, 10)
.withTotal()
.queryList()
// multi-table query
val listOfMap = User().join(UserRelation(), UserRole()) { user, relation, role ->
on { user.id == relation.userId && user.id == role.userId }
select {
user.id + user.username + relation.id.as_("relationId") +
role.role + f.count(1).as_("count")
}
where { user.id < 10 }
}.query()
// Using Native SQL Queries with Named Parameters
val result: Map<String, Any> = dataSource.query("select * from tb_user where id = :id", mapOf("id" to 1))// single insert
user.insert().execute()
// batch insert
listOfUser.insert().execute()// update by some conditions use `set`
user.update()
.set {
it.username = "123"
it.score += 10
}
.by { it.id }
.execute()
// update by some conditions, data from record
user.update { it.username + it.gender }
.by { it.id }
.execute()// upsert on some columns
user.upsert { it.username }
.on { it.id }
// .lock(PessimisticLock.X) // You can specify the type of lock, and pessimistic lock is used by default
.execute()
// upsert on duplicate key
user.upsert { it.username }
.onConflict() // Cross-database compatible upsert syntax
.execute()// delete rows by some conditions
user.delete()
.where { it.id == 1 }
.execute()- Kronos provides
KPojowith the ability to dynamically create instances based on type parameters through compile-time operations so that instances can be easily created. - Kronos provides functions to easily convert between
KPojoandMap. - Kronos provides classes inheriting the
KPojointerface with a number of functions includingkronosTableandkronosColumnsfor obtaining meta information such as table names and columns.
val instance = Movie::class.newInstance() // -> Movie() ,dynamic create KPojo instance by reference, NO REFLECTION used
fun <T: KPojo> dynamic(kClass: KClass<T>): T = kClass.newInstance() // dynamic create KPojo instance by reference, NO REFLECTION used
val movie = Movie(1)
val dataMap: Map<String, Any?> = movie.toDataMap() // Map("id" = 1), NO REFLECTION used
val movie2: Movie = dataMap.mapperTo<Movie>() // or dataMap.mapperTo(Movie::class), NO REFLECTION used
val tableName = movie.kronosTable() // "tb_movie", NO REFLECTION used
val columns = movie.kronosColumns() // [Field(id), Field(name), ...], NO REFLECTION used
// We actually provide more functions such as getting the table creation time, update time, logical deletion of field settings and so on
instance["fieldName"] // get field value by field name, NO REFLECTION used
instance["fieldName"] = "value" // set field value by field name, NO REFLECTION usedPlease refer to the following example projects for more information:
For more information, please visit the official website or the documentation.
Kronos-ORM is released under the Apache 2.0 license.
Please refer to the CONTRIBUTING.md for more.
We would like to express our gratitude to all the individuals who have already contributed to Kronos-ORM!
If you are interested in Kronos-ORM, please join our community and chat with us!
If you like Kronos-ORM, please give us a star ⭐️, thank you!
