Primary Keys

This chapter will introduce the concept and usage of primary key types in Kronos.

Declare a primary key

Use @PrimaryKey on one KPojo property. With no generation flag, the field maps to PrimaryKeyType.DEFAULT. Without @PrimaryKey, the field maps to PrimaryKeyType.NOT.

The examples on this page use these imports:

model

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

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

The generated field metadata is:

metadata

id.primaryKey -> PrimaryKeyType.IDENTITY
id.nullable -> false
username.primaryKey -> PrimaryKeyType.NOT

Use one generation flag on a primary key field: identity, uuid, snowflake, or custom.

PrimaryKeyType reference

TypeAnnotation shapeInsert behaviorDDL behavior
NOTNo @PrimaryKeyNormal column.Not a primary key.
DEFAULT@PrimaryKeyUses the value already on the KPojo.Primary key column.
IDENTITY@PrimaryKey(identity = true)Omits the key column when the key value is null.Database identity/autoincrement primary key.
UUID@PrimaryKey(uuid = true)Writes UUIDGenerator.nextId() before insert.Primary key column.
SNOWFLAKE@PrimaryKey(snowflake = true)Writes SnowflakeIdGenerator.nextId() before insert.Primary key column.
CUSTOM@PrimaryKey(custom = true)Writes customIdGenerator?.nextId() before insert.Primary key column.

DEFAULT Manual primary key

Use @PrimaryKey without flags when your application or database supplies the key value.

kotlin

@Table("tb_region")
data class Region(
    @PrimaryKey
    var code: String? = null,
    var name: String? = null
) : KPojo

Region(code = "CN", name = "China").insert().build()

Mysql

INSERT INTO `tb_region` (`code`, `name`) VALUES (:code, :name)

params

code -> "CN"
name -> "China"

Table creation treats the field as a normal primary key column.

DDL

`code` VARCHAR(255) NOT NULL PRIMARY KEY

IDENTITY Database-generated key

Use identity = true for an Int or Long key generated by the database.

kotlin

@Table("tb_user")
data class IdentityUser(
    @PrimaryKey(identity = true)
    var id: Int? = null,
    var username: String? = null
) : KPojo

IdentityUser(username = "Ada").insert().build()

When id is null, Kronos removes the identity field from the insert column list.

Mysql

INSERT INTO `tb_user` (`username`) VALUES (:username)

params

username -> "Ada"

The MySQL table column renders as:

DDL

`id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT

If you provide an identity value yourself, Kronos keeps the key column in the INSERT. Use 📚Last Insert ID when an insert should read the database-generated value.

UUID UUID string key

Use uuid = true for a String key. Kronos calls UUIDGenerator.nextId() before building the insert SQL.

kotlin

@Table("tb_uuid_user")
data class UuidUser(
    @PrimaryKey(uuid = true)
    var id: String? = null,
    var username: String? = null
) : KPojo

UuidUser(username = "Ada").insert().build()

Mysql

INSERT INTO `tb_uuid_user` (`id`, `username`) VALUES (:id, :username)

params

id -> "550e8400-e29b-41d4-a716-446655440000"
username -> "Ada"

Table creation still renders a normal primary key column.

DDL

`id` VARCHAR(255) NOT NULL PRIMARY KEY

SNOWFLAKE Snowflake key

Use snowflake = true for a Long key generated in the application process.

kotlin

@Table("tb_order")
data class OrderRecord(
    @PrimaryKey(snowflake = true)
    var id: Long? = null,
    var status: String? = null
) : KPojo

OrderRecord(status = "created").insert().build()

Mysql

INSERT INTO `tb_order` (`id`, `status`) VALUES (:id, :status)

params

id -> 1912549819912192000
status -> "created"

Configure node identifiers before inserts when multiple application nodes generate Snowflake IDs.

config

import com.kotlinorm.beans.generator.SnowflakeIdGenerator

fun configureSnowflakeIds() {
    SnowflakeIdGenerator.datacenterId = 1
    SnowflakeIdGenerator.workerId = 1
}

datacenterId and workerId accept values from 0 to 31.

CUSTOM Custom generator key

Use custom = true when the key value should come from your own KIdGenerator.

generator

import com.kotlinorm.beans.generator.customIdGenerator
import com.kotlinorm.interfaces.KIdGenerator

object TicketIdGenerator : KIdGenerator<String> {
    override fun nextId(): String = "ticket-${System.currentTimeMillis()}"
}

fun configureTicketIds() {
    customIdGenerator = TicketIdGenerator
}

model

@Table("tb_ticket")
data class Ticket(
    @PrimaryKey(custom = true)
    var id: String? = null,
    var title: String? = null
) : KPojo

Ticket(title = "Login issue").insert().build()

Mysql

INSERT INTO `tb_ticket` (`id`, `title`) VALUES (:id, :title)

params

id -> "ticket-1912549819912"
title -> "Login issue"

Set customIdGenerator before inserting a custom primary key. If it is not set, the generated key parameter is null.

Global primary-key strategy

The recommended mapping is to put @PrimaryKey on the key property. Use Kronos.primaryKeyStrategy only for unannotated models that share the same key field name.

config

import com.kotlinorm.Kronos
import com.kotlinorm.beans.config.KronosCommonStrategy
import com.kotlinorm.beans.dsl.Field
import com.kotlinorm.enums.PrimaryKeyType

fun configurePrimaryKeyStrategy() {
    Kronos.primaryKeyStrategy = KronosCommonStrategy(
        enabled = true,
        field = Field("id", "id", primaryKey = PrimaryKeyType.IDENTITY)
    )
}

model

@Table("tb_audit_log")
data class AuditLog(
    var id: Int? = null,
    var message: String? = null
) : KPojo

For table creation and visible field metadata, prefer the property annotation:

recommended

@PrimaryKey(identity = true)
var id: Int? = null