DEV Community

Livio Ribeiro
Livio Ribeiro

Posted on • Updated on

Mapping JPA entities with Kotlin

Kotlin is a very interesting programming language created by JetBrains that runs on the JVM. One of its features is data classes, which are classes whose main purpose is to hold data. One might think that data classes are perfect to map database entities, and they are actually very good, but you will to think a little more in order to use them correctly with JPA.

Data classes automatically implement toString(), equals() and hashCode() based on the properties defined in the primary constructor. Because of this, you need to think about what properties should be used to tell whether two objects are equal or not.

There is one caveat when using data classes with JPA: data classes require a primary constructor with at least one parameter and JPA requires a constructor without arguments. To help with this, Kotlin has the no-arg compiler plugin that generates a zero-argument constructor that can only be called using reflection. Kotlin also provides the jpa plugin that wrapps the no-arg plugin configured for JPA.

Let's take the following schema of a task list application:

    "name" VARCHAR(200) NOT NULL

    "task_list_id" INTEGER NOT NULL,
    "name" VARCHAR(200) NOT NULL,
    "description" TEXT,
    "due_date" DATE,

    FOREIGN KEY ("task_list_id") REFERENCES "task_list" ("id")

For this schema, we can see that we will need two entities: TaskList and Task. First let's map the TaskList:

package com.example.domain

import javax.persistence.*

data class TaskList(
    @Id @GeneratedValue
    val id: Int = 0,
    val name: String
) {
    @OneToMany(mappedBy = "taskList")
    val tasks: MutableSet<Task> = HashSet()

The id and name properties are defined in the primary constructor, but tasks is defined in the class body. This way, only id and name are used for equals(), hashCode() and toString(). Since tasks maps a relationship, it is better to left it out of the primary constructor.

Now to the Task:

package com.example.demo.domain

import java.time.LocalDate
import javax.persistence.*

data class Task(
    @Id @GeneratedValue
    val id: Int = 0,
    val name: String,
    val description: String? = null,
    val dueDate: LocalDate? = null,
    val done: Boolean = false
) {
    lateinit var taskList: TaskList

    constructor(name: String, taskList: TaskList) : this(name = name) {
        this.taskList = taskList

Same way with TaskList, but now we have the inverse relationship with Task. We define the attribute taskList as lateinit for two reasons: first, because it will be lazy loaded; and second, so we do not need to initialize it right away, but we assign it on the secondary constructor.

If we put all the attributes in the primary constructor, simply calling println(task) would cause our application to crash with a infinite recursion because Task::toString() would call TaskList::toString() that would call Task::toString() and so on.

Top comments (6)

tonyengineering profile image


Thanks for your article.

I wrote a code based on what you wrote, and have difficulties to persist an entity in the database.

javax.persistence.PersistenceException: No Persistence provider for EntityManager named PersistenceProviderMysql

    at javax.persistence.Persistence.createEntityManagerFactory(
    at javax.persistence.Persistence.createEntityManagerFactory(
    at KrakenConnector.saveLastPrice(KrakenConnector.kt:42)
    at KrakenConnector.getAndSaveLastPrice(KrakenConnector.kt:17)

I added a persistence.xml file at the following path: src/main/resources/META-INF

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
             xmlns="" xmlns:xsi=""
    <persistence-unit name="PersistenceProviderMysql" transaction-type="RESOURCE_LOCAL">
            <!-- Configuring The Database Connection Details -->
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpaDemoDb" />
            <property name="javax.persistence.jdbc.user" value="root" />
            <property name="javax.persistence.jdbc.password" value="" />

And the code to persist my entity is:

fun saveLastPrice(ohlcs: KrakenOHLCs) {
        val krakenOHLCsPersistable = KrakenOHLCsPersistable(ohlcs.ohlCs, 1)

        val emFactory: EntityManagerFactory = Persistence.createEntityManagerFactory("PersistenceProviderMysql")
        val entityManager = emFactory.createEntityManager()

I added "javax.persistence-api" version 2.2 as dependency in my pom.xml and also enabled the JPA support as described here

I am pretty sure the persistence.xml file is not detected because the error is the same when I delete it.

Could you provide the full working code of your example so I can try to reproduce it ?

Thanks in advance for your help,

felipebelluco profile image
Felipe Belluco • Edited

Shouldn't I have to declare @JoinColumn(name = "task_list_id")? I keep getting the following error:

ERRO: column planos0_.convenio_id_convenio does not exist

In my case, Plano is task and Convenio is TaskList.

00babe9 profile image

Hi, Livio! Great post. I'm interested in how to do deletions in onetomany relation. Im looking for help. I have User Role classes, and I want, when I deleting my Role, I want to kill relation link on User(role_id = null, like so on). How can I achieve this?

ksambhavjain profile image
Kumar Sambhav Jain

Short & sweet.
In most cases I find that common entity fields like id, verison, dateCreated, lastUpdated are pulled up in a super base entity class. Could you please explain that scenario too ?

livioribeiro profile image
Livio Ribeiro

Sorry for the late reply.

I did some tests and found out that inheritance with data classes can be tricky, specially if you have to set fields on the base class, since you cannot have arguments on the primary constructor that are not properties.

If the fields in the base class are auto generated (like Id, creation date), you can do something like this:

import java.time.LocalDate
import java.time.LocalDateTime

open class Base(
        val id: Int? = null,
        val dateCreated: LocalDateTime =,
        val lastUpdated: LocalDateTime =,

data class Task(
        var name: String,
        var description: String? = null,
        var dueDate: LocalDate? = null,
        var done: Boolean = false
) : Base()

For more complex cases, I believe it is better to use normal classes.

chrisvasqm profile image
Christian Vasquez

I love it every time I get a DEV post as a Google search result in the first 5 or so links!

Thanks for sharing this, Livio :)