Data Types

Overview

This section describes data type mapping between Kotlin and SQL.

Dialect data types

A dialect determines the default data type mapping between Kotlin and SQL.

User-defined data types

To map a user-defined Kotlin data type to a SQL data type, you must create and register a class that conforms to the Service Provider Interface specification.

For example, suppose you want to map the following Kotlin data type example.Age to an SQL INTEGER type.

package example

data class Age(val value: Int)

In the case of JDBC

Create a class that implements org.komapper.jdbc.spi.JdbcUserDefinedDataType to perform the mapping:

package example.jdbc

import example.Age
import org.komapper.jdbc.spi.JdbcUserDefinedDataType
import java.sql.JDBCType
import java.sql.PreparedStatement
import java.sql.ResultSet
import kotlin.reflect.KClass

class AgeType : JdbcUserDefinedDataType<Age> {
    override val name: String = "integer"

    override val klass: KClass<Age> = Age::class

    override val jdbcType: JDBCType = JDBCType.INTEGER

    override fun getValue(rs: ResultSet, index: Int): Age {
        return Age(rs.getInt(index))
    }

    override fun getValue(rs: ResultSet, columnLabel: String): Age {
        return Age(rs.getInt(columnLabel))
    }

    override fun setValue(ps: PreparedStatement, index: Int, value: Age) {
        // The second argument must be a type corresponding to the jdbcType property.
        ps.setInt(index, value.value)
    }

    override fun toString(value: Age): String {
        return value.value.toString()
    }
}

Register the above class in a file with the following name:

  • META-INF/services/org.komapper.jdbc.spi.JdbcUserDefinedDataType

This file contains the fully qualified name of the class as follows:

example.jdbc.AgeType

You can register multiple classes together by separating lines.

In the case of R2DBC

Create a class that implements org.komapper.r2dbc.spi.R2dbcUserDefinedDataType to perform the mapping:

package example.r2dbc

import example.Age
import io.r2dbc.spi.Row
import io.r2dbc.spi.Statement
import org.komapper.r2dbc.spi.R2dbcUserDefinedDataType
import kotlin.reflect.KClass

class AgeType : R2dbcUserDefinedDataType<Age> {

    override val name: String = "integer"

    override val klass: KClass<Age> = Age::class

    override val r2dbcType: Class<Int> = Int::class.javaObjectType

    override fun getValue(row: Row, index: Int): Age? {
        return row.get(index, Int::class.javaObjectType)?.let { Age(it) }
    }

    override fun getValue(row: Row, columnLabel: String): Age? {
        return row.get(columnLabel, Int::class.javaObjectType)?.let { Age(it) }
    }

    override fun setValue(statement: Statement, index: Int, value: Age) {
        // The second argument must be of the same type as the r2dbcType property.
        statement.bind(index, value.value)
    }

    override fun setValue(statement: Statement, name: String, value: Age) {
        // The second argument must be of the same type as the r2dbcType property.
        statement.bind(name, value.value)
    }

    override fun toString(value: Age): String {
        return value.value.toString()
    }
}

Register the above class in a file with the following name:

  • META-INF/services/org.komapper.r2dbc.spi.R2dbcUserDefinedDataType

This file contains the fully qualified name of the class as follows:

example.r2dbc.AgeType

You can register multiple classes together by separating lines.

Data type conversion

To convert a data type to another type, you must create and register a class that conforms to the Service Provider Interface specification.

For example, suppose you want to treat Int as the following example.PhoneNumber in your application.

package example

data class PhoneNumber(val value: Int)

Create a class that implements org.komapper.core.spi.DataTypeConverter to perform the conversion:

package example

import org.komapper.core.spi.DataTypeConverter
import kotlin.reflect.KClass

class PhoneNumberTypeConverter : DataTypeConverter<PhoneNumber, Int> {
    override val exteriorClass: KClass<PhoneNumber> = PhoneNumber::class
    override val interiorClass: KClass<Int> = Int::class

    override fun unwrap(exterior: PhoneNumber): Int {
        return exterior.value
    }

    override fun wrap(interior: Int): PhoneNumber {
        return PhoneNumber(interior)
    }
}

Register the above class in a file with the following name:

  • META-INF/services/org.komapper.core.spi.DataTypeConverter

This file contains the fully qualified name of the class as follows:

example.PhoneNumberTypeConverter

You can register multiple classes together by separating lines.

Value classes

When you use a value class, the inner type of the value class is used for mapping.

Alternate types

You can change the SQL type to be mapped by specifying a value class for the @KomapperColumn.alternateType property.

For example, if you want to map kotlin.String to CLOB or TEXT instead of VARCHAR or VARCHAR2, specify org.komapper.core.type.ClobString.

@KomapperColumn(alternateType = ClobString::class)
val description: String

The alternateType property allows users to specify their own value class. However, the user-defined data type corresponding to that value class must be created and registered.

Note that the value class must meet the following requirements:

  • the constructor must be public
  • the parameter property must be public and non-nullable
  • the parameter property type must match the entity property type annotated with @KomapperColumn

Support for kotlinx-datetime

Komapper supports following data types of kotlinx-datetime:

  • kotlinx.datetime.Instant
  • kotlinx.datetime.LocalDate
  • kotlinx.datetime.LocalDateTime

To use these types, declare kotlinx-datetime in the Gradle dependencies declaration as follows:

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.3.2")
}

Also, declare komapper-datetime-jdbc or komapper-datetime-r2dbc:

val komapperVersion: String by project
dependencies {
    runtimeOnly("org.komapper:komapper-datetime-jdbc:$komapperVersion")
}
Last modified November 23, 2023: Fix sample code (d7e4f7c)