Compiler Plugins

Configure Kronos compiler plugins and read user-visible DSL diagnostics.

Set the Kotlin project baseline

Kronos compile-time support is loaded by the Kotlin build. Use JDK 8+, Kotlin 2.4.21+, Maven 3.9+, or a Gradle version supported by Kotlin 2.4.21.

gradle(kts)

import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
    kotlin("jvm") version "2.4.21"
}

kotlin {
    compilerOptions {
        jvmTarget.set(JvmTarget.JVM_1_8)
    }
}

maven

<properties>
    <kotlin.version>2.4.21</kotlin.version>
    <maven.compiler.release>8</maven.compiler.release>
</properties>

The baseline build should compile before the Kronos data source and ORM code are added.

output

> Task :compileKotlin
BUILD SUCCESSFUL

Enable kronos-gradle-plugin

Add the Kronos Gradle plugin and kronos-core to a JVM Kotlin project. The Gradle plugin wires Kronos compile-time support into compileKotlin.

build.gradle.kts

plugins {
    kotlin("jvm") version "2.4.21"
    id("com.kotlinorm.kronos-gradle-plugin") version "0.4.0"
}

kotlin {
    compilerOptions {
        freeCompilerArgs.addAll("-Xcollection-literals", "-Xcontext-sensitive-resolution")
    }
}

dependencies {
    implementation("com.kotlinorm:kronos-core:0.4.0")
}

Run the Kotlin compilation task and check the plugin loading lines.

shell

./gradlew compileKotlin

output

Loaded Gradle plugin com.kotlinorm.compiler.plugin.KronosGradlePlugin version 0.4.0
Loaded Compiler plugin com.kotlinorm.kronos-compiler-plugin version 0.4.0
BUILD SUCCESSFUL

Enable kronos-maven-plugin

In Maven, add kronos-core as an application dependency and register kronos-maven-plugin inside kotlin-maven-plugin.

pom.xml

<project>
    <properties>
        <kotlin.version>2.4.21</kotlin.version>
        <kronos.version>0.4.0</kronos.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.kotlinorm</groupId>
            <artifactId>kronos-core</artifactId>
            <version>${kronos.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <version>${kotlin.version}</version>
                <extensions>true</extensions>
                <configuration>
                    <args>
                        <arg>-Xcollection-literals</arg>
                        <arg>-Xcontext-sensitive-resolution</arg>
                    </args>
                    <compilerPlugins>
                        <plugin>kronos-maven-plugin</plugin>
                    </compilerPlugins>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>com.kotlinorm</groupId>
                        <artifactId>kronos-maven-plugin</artifactId>
                        <version>${kronos.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
</project>

Compile with Maven and check the Kronos plugin line in the build output.

shell

mvn compile

output

Loaded Maven plugin com.kotlinorm.compiler.plugin.KronosMavenPlugin
[INFO] BUILD SUCCESS

Compile each Kronos source set

Apply the Kronos build plugin to every JVM source set that declares KPojo classes or uses Kronos DSL calls. A module that only consumes compiled entities can depend on that module normally, but a module that declares entities or query DSL must compile with the Kronos plugin active.

module layout

// :domain declares KPojo classes and must apply the Kronos Gradle or Maven plugin.
@Table("tb_user")
data class User(
    @PrimaryKey
    var id: Int? = null,
    var name: String? = null
) : KPojo

// :service writes DSL over User and must also compile with Kronos support.
val names = User()
    .select { [it.id, it.name] }
    .where { it.name like "A%" }
    .toList()

The fastest check is to compile the module that owns the source files.

shell

./gradlew :service:compileKotlin

output

[Kronos] Kronos compiler plugin K2 initialized
BUILD SUCCESSFUL

Check generated KPojo members

After the build plugin is active, a KPojo can expose generated metadata and map conversion methods during runtime checks.

User.kt

import com.kotlinorm.annotations.PrimaryKey
import com.kotlinorm.annotations.Table
import com.kotlinorm.interfaces.KPojo

@Table("tb_user")
data class User(
    @PrimaryKey
    var id: Int? = null,
    var name: String? = null
) : KPojo

fun main() {
    val user = User(7, "Ada")

    println(user.__tableName)
    println(user.toDataMap())
    println(user.__columns.map { it.name })
}

The output should show table metadata and the property values collected by Kronos.

output

tb_user
{id=7, name=Ada}
[id, name]

A message such as __tableName must be overridden by the compiler plugin means this source set was compiled without Kronos compile-time support.

Check the DSL SQL output

Use a small select query to confirm that Kotlin property access and condition expressions are converted into Kronos SQL.

kotlin

import com.kotlinorm.orm.select.select

val (sql, params) = User()
    .select { [it.id, it.name] }
    .where { it.id == 7 }
    .build()

println(sql)
println(params)

For a MySQL data source, the check prints the selected columns and named parameter.

output

SELECT `id`, `name` FROM `tb_user` WHERE `tb_user`.`id` = :id
{id=7}

For condition operators and query-by-example behavior, see 📚Conditions for Where, Having and On.

Use DSL rules from kronos-syntax

Kronos query, mutation, and DDL APIs all produce an inspectable SQL task before execution. Use build() when you want to review the SQL and parameter map without touching the database.

projection and condition

val task = User()
    .select {
        [
            it.id,
            it.name.alias("username"),
            f.length(it.name).alias("nameLength")
        ]
    }
    .where { (it.id in listOf(1, 2, 3)) && it.name like "A%" }
    .build()

println(task.sql)
println(task.paramMap)

output

SELECT `id`, `name` AS `username`, LENGTH(`name`) AS `nameLength`
FROM `tb_user`
WHERE `tb_user`.`id` IN (:idList) AND `tb_user`.`name` LIKE :name
{idList=[1, 2, 3], name=A%}

The same rules apply in where, having, and on: Kotlin comparisons become SQL predicates, && and || keep the boolean grouping, in accepts collections or selectable subqueries, and non-field select items need .alias("name").

derived projection

val nameLengths = User()
    .select { [it.id, f.length(it.name).alias("nameLength")] }

val rows = nameLengths
    .select { [it.id, it.nameLength] }
    .where { it.nameLength > 8 }
    .toList()

Projection, subquery, and generated result-shape examples are covered in 📚Projection and 📚Subqueries.

Read Kronos diagnostics

Kronos diagnostics appear in the IDE and in build output. The KRONOS_ code identifies the DSL rule, and the message describes the fix.

invalid select

import com.kotlinorm.orm.select.select

User()
    .select { [it.id, f.length(it.name)] }

This select item is a function expression, so the diagnostic asks for an explicit alias.

output

User.kt:6:23: error: Non-field select item must declare .alias("name")
KRONOS_SELECT_ITEM_REQUIRES_ALIAS

Add an alias and compile again.

fixed select

User()
    .select { [it.id, f.length(it.name).alias("nameLength")] }

Compiler plugin errors emitted by Kronos also start with [Kronos] and may include a Fix: line.

kronos output

User.kt:12:13: error: [Kronos] Unsupported field
  Fix: Use a property reference

Fix common diagnostics

Use the diagnostic code to find the DSL shape that needs to change.

DiagnosticWhat to checkFix
__tableName must be overridden by the compiler pluginThe source set was compiled without Kronos supportEnable the Gradle or Maven plugin for that module and recompile
KRONOS_GENERIC_KPOJO_NOT_SUPPORTEDA KPojo class declares class-level type parametersRemove the KPojo type parameters and use concrete property types
KRONOS_SELECT_ITEM_REQUIRES_ALIASFunction, aggregate, scalar subquery, window function, or raw SQL in select { ... }Add .alias("resultName")
KRONOS_DUPLICATE_PROJECTION_FIELDTwo selected items produce the same result propertyRemove one field or give one item a different alias
OPT_IN_USAGE_ERRORA selected alias replaces a still-present source field with the same logical nameRename the alias, remove the source field first, or explicitly acknowledge it with @OptIn(UnsafeProjectionOverride::class)
KRONOS_SCALAR_SUBQUERY_REQUIRES_LIMITA scalar subquery can return more than one rowAdd .limit(1) before using it as a value
KRONOS_SCALAR_SUBQUERY_REQUIRES_SINGLE_COLUMNA scalar subquery selects multiple fieldsSelect one field or split the expression
KRONOS_PREDICATE_SUBQUERY_COLUMN_COUNT_MISMATCHfield in query, ANY, SOME, ALL, or row-value IN has mismatched arityMake the left expression and right query return the same column count
KRONOS_ROW_VALUE_TUPLE_REQUIRES_MULTIPLE_FIELDSA single-field tuple such as [it.id] in query was usedWrite it.id in query
KRONOS_INSERT_SELECT_VALUE_COUNT_MISMATCHinsert<Target> { ... } value count differs from target fieldsSelect the same number of values as insertable target columns
KRONOS_INSERT_SELECT_VALUE_TYPE_MISMATCHInsert-select values do not match target field typesReorder values or cast the scalar expression to the expected Kotlin type

An explicit projection alias normally must not replace a source field with the same logical name. If the replacement is intentional, opt in with the standard Kotlin mechanism. The marker is com.kotlinorm.annotations.UnsafeProjectionOverride and has error severity; its message warns that the selected value and Kotlin type may change.

import com.kotlinorm.annotations.UnsafeProjectionOverride

@OptIn(UnsafeProjectionOverride::class)
val query = User().select { [it.id, f.length(it.name).alias("name")] }

Removing a field first makes the replacement intentional without opt-in:

val query = User().select { [it - it.name, f.length(it.name).alias("name")] }

For build setup, start from 📚Quick Start. For database execution and SQL logging, see 📚Connect to DB and 📚Logging. For IDE inspection behavior, see 📚IDE Support with KEFS.