Hibernate with Kotlin - powered by Spring Boot
In this post, I'd like to demonstrate what you need to consider when using Hibernate with Kotlin. Hibernate is probably the most famous framework for object-relational mapping (ORM) on the JVM, which is used to persistently store Plain Old Java Objects (POJOs) in relational databases. It also implements the Java Persistence API, a specification that "describes the management of relational data" on the JVM.
Summary (TL;DR)
- Put the
kotlin-noarg
compiler plugin on your build path, it will generate no-argument constructors for your Hibernate entities.- In Gradle, add the following to your
buildscript
dependencies:classpath("org.jetbrains.kotlin:kotlin-noarg:${kotlinVersion}")
- Further examples can be found here
- In Gradle, add the following to your
- Enable the
kotlin-jpa
plugin, which works on top ofkotlin-noarg
by enabling the no-arg generation for Hibernate annotated classes- In Gradle, activate the plugin like this:
apply plugin: "kotlin-jpa"
- Further examples can be found here
- In Gradle, activate the plugin like this:
- Put the
kotlin-allopen
compiler plugin on your build path, and configure it toopen
classes with entity annotations as Hibernate should not be used withfinal
classes- In Gradle, add the following to your buildscript dependencies:
classpath "org.jetbrains.kotlin:kotlin-allopen:${versions.kotlin}"
and add the following configuration:
allOpen { annotation("javax.persistence.Entity") annotation("javax.persistence.MappedSuperclass") annotation("javax.persistence.Embeddable") }
- Further examples can be found here
- In Gradle, add the following to your buildscript dependencies:
- Abstract your
hashCode
/equals
implementations in an abstract base class and define entities as ordinary classes inheriting from the abstract base class- Do not use data classes to define your
@Entity
classes - JPA doesn't work well with the generatedequals
/hashCode
functions.
- Do not use data classes to define your
Hibernate Entity Type
The most important thing we need to do when integrating Hibernate into an application is defining the entity types we want to persist, i.e. defining the mappings between tables and classes. The Hibernate documentation describes an "Entity
" as follows:
The entity type describes the mapping between the actual persistable domain model object and a database table row. To avoid any confusion with the annotation that marks a given entity type, the annotation will be further referred as
@Entity
.
Let's see how valid entity classes need to look like.
Hibernate Requirements for Entity classes
Hibernate imposes certain requirements on a valid Entity
type: An entity...
- ... must be annotated with the
javax.persistence.Entity
annotation (or be denoted as such in XML mapping, which we won't consider) - ... must have a public or protected (or package-private) no-argument constructor. It may define additional constructors as well
- ... should not be final. No methods or persistent instance variables of the entity class may be final (technically possible but not recommended)
- ... may extend non-entity classes as well as entity classes, and non-entity classes may extend entity classes. Both abstract and concrete classes can be entities
- ... may provide JavaBean-style properties. This is not a must, i.e. setters are not necessary
- ... must provide an identifier attribute (
@Id
annotation), recommended to use nullable, non-primitive, types - ... needs to provide useful implementations for
equals
andhashCode
(Why? find information here)
If we think about which kind of class in Kotlin best suits these requirements, one might say data
classes did. As it turns out though, this is probably not the best solution as discussed in the following.
The equals
/hashCode
dilemma: Don't use data
classes as Hibernate entities
It seems to be a good idea to use data classes for defining our Hibernate entities: They basically just need a concise primary constructor with annotated parameters, provide neat things like hashCode
, equals
, copy
, toString
out of the box and may be immutable (actually they can't be for Hibernate).
There's a problem though: We need to be very careful with auto-generated equals
/hashCode
functions when working with Hibernate, especially because the entity identifier may be set after the object has been constructed. Actually, using auto-generated IDs means that our classes can never be immutable. Consider the following scenario:
- Create an object of your entity
Person
- Put this object into a
HashSet
- Persist object via Hibernate (this leads to a generated and updated
Person::id
and thus changes itshashCode
) - Test if the object still exists in the
HashSet
will yieldfalse
since the hash code changed
This dilemma could be fixed by using natural keys (aka business keys) instead, i.e. we'd need to find a combination of properties that clearly identify an entity. For a person, this could be their name and address, which still might be insufficient. We don't have natural keys for every entity actually. Also, it's a bit cumbersome to implement such behavior with data classes since we'd have to put the natural key parts into the primary constructor and everything else in the class body, the caller would have to set properties after construction. This does not feel right, so let's not do it...
Hibernate Suggestion
What the Hibernate documentation suggests:
Although using a natural-id is best for
equals
andhashCode
, sometimes you only have the entity identifier that provides a unique constraint. It’s possible to use the entity identifier for equality check, but it needs a workaround:
- you need to provide a constant value for hashCode so that the hash code value does not change before and after the entity is flushed.
- you need to compare the entity identifier equality only for non-transient entities.
They say that we can use the Hibernate-generated ID for equality checks as long as we provide a "constant value" for hashCode
. This is because, reviewing the example scenario from earlier, the hash code should not change for an object once it's been put into hash-based collections. Using a constant value for hashCode
fixes this and still is a valid implementation according to its contract (taken from Oracle JavaDocs):
hashCode
Contract
The general contract of
hashCode
is:
- Whenever it is invoked on the same object more than once during an execution of a Java application, thehashCode
method must consistently return the same integer, provided no information used inequals
comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
- If two objects areequal
according to theequals(Object)
method, then calling thehashCode
method on each of the two objects must produce the same integer result.
- It is not required that if two objects are unequal according to theequals(Object)
method, then calling thehashCode
method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
So this is all good although we need to take a closer look at the last sentence of this contract:
However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables
hashCode
Performance Implications
If we decide to yield constant values from hashCode
for any object of a class, performance will suffer. You cannot expect hash collections to work as efficient as with properly distributed hash codes:
This implementation [HashMap] provides constant-time performance for the basic operations (
get
andput
), assuming the hash function disperses the elements properly among the buckets.
If you can work around these performance implications, you should be fine to follow the described approach. For us, this isn't considered problematic.
As a result, we want to let our entities' equals
be based on their identifier and provide a constant value for hashCode
. Also, since data
classes do not seem to be an adequate solution, we'll be using ordinary, more flexible, classes.
Implementing Hibernate Entities with Kotlin
As a starter, it feels appropriate to provide a generic base class for our entities that defines an auto-generated identifier and, based on that, implements equals
and the constant hashCode
:
@MappedSuperclass
abstract class AbstractJpaPersistable<T : Serializable> {
companion object {
private val serialVersionUID = -5554308939380869754L
}
@Id
@GeneratedValue
private var id: T? = null
override fun getId(): T? {
return id
}
override fun equals(other: Any?): Boolean {
other ?: return false
if (this === other) return true
if (javaClass != ProxyUtils.getUserClass(other)) return false
other as AbstractJpaPersistable<*>
return if (null == this.getId()) false else this.getId() == other.getId()
}
override fun hashCode(): Int {
return 31
}
override fun toString() = "Entity of type ${this.javaClass.name} with id: $id"
}
The class AbstractJpaPersistable
is pretty straightforward: It defines a generic nullable @Id
property, which is going to be auto-generated by Hibernate. The equals
and hashCode
look like discussed earlier. Now we can create our entities based on that class:
@Entity
class Person(
val name: String,
@OneToOne(cascade = [(CascadeType.ALL)], orphanRemoval = true, fetch = FetchType.EAGER)
val address: Address
) : AbstractJpaPersistable<Long>()
@Entity
class Address(
val street: String,
val zipCode: String,
val city: String
) : AbstractJpaPersistable<Long>()
We can see two rather simple entities: A Person
which has an associated Address
. Both @Entity
classes extend AbstractJpaPersistable<Long>
and therefore rely on an auto-generated id of type Long
.
Reviewing the entity requirements
As depicted earlier, we have a few requirements for entities that need to be considered. Let's review what the approach from above already takes care of:
The Entity...
- ... must be annotated with the
javax.persistence.Entity
annotation (or be denoted as such in XML mapping, which we won't consider) ✔️- ... must have a public or protected (or package-private) no-argument constructor. It may define additional constructors as well ❌
- ... should not be final. No methods or persistent instance variables of the entity class may be final (technically possible but not recommended) ❌
- ... may extend non-entity classes as well as entity classes, and non-entity classes may extend entity classes. Both abstract and concrete classes can be entities ✔️
- ... may provide JavaBean-style properties. This is not a must, i.e. setters are not necessary ✔️ (We accept to not have setters)
- ... must provide an identifier attribute (
@Id
annotation), recommended to use nullable, non-primitive, types ✔️- ... needs to provide useful implementations for
equals
andhashCode
✔️
We still have two things to fix:
1. Kotlin classes are final
by default, which is good practice in most cases but Hibernate does not really like that. Since it makes use of proxies that allow e.g. lazy-loading of entities, classes should not be final
if possible.
2. We did not provide a no-argument constructor so far.
The following will take care of both problems.
Writing a sample application
Setup
Now that we know how to abstract Hibernate entities properly, let's write a sample application and see if there are more things to consider. We'll use a Spring Boot base for our application which can easily be generated via start.spring.io:

(If you like to find out more about Spring and its fantastic Kotlin support, I encourage you to read this blog post as well.)
Fixing remaining Entity requirements
As discussed earlier, Hibernate expects a no-argument constructor defined for its entities. Since we don't want to provide one at compile time, we use a compiler plugin by JetBrains called kotlin-noarg, which "generates an additional zero-argument constructor for classes with a specific annotation. The generated constructor is synthetic so it can’t be directly called from Java or Kotlin, but it can be called using reflection."
In addition, we need to tell the tool which annotations it should apply the no-arg constructor rule on. This can be done manually or by adding the plugin kotlin-jpa
to our build, which is "wrapped on top of no-arg
. The plugin specifies @Entity
, @Embeddable
and @MappedSuperclass
no-arg annotations automatically."
Also, taking care of the final
classes problem, we configure the kotlin-allopen
plugin to remove the final
modifier from all compiled entity classes.
The Gradle build file
Altogether, the build script looks like this (Gradle Groovy DSL):
buildscript {
ext {
kotlinVersion = '1.2.60'
springBootVersion = '2.0.4.RELEASE'
h2 = '1.4.196'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
classpath("org.jetbrains.kotlin:kotlin-noarg:${kotlinVersion}")
}
}
apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: "kotlin-jpa"
group = 'com.kotlinexpertise'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
allOpen {
annotation("javax.persistence.Entity")
annotation("javax.persistence.MappedSuperclass")
annotation("javax.persistence.Embeddable")
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-web')
compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
compile("org.jetbrains.kotlin:kotlin-reflect")
// Database Drivers
compile("com.h2database:h2:$h2")
//Jackson Kotlin
compile('com.fasterxml.jackson.module:jackson-module-kotlin')
//Junit 5
testCompile('org.springframework.boot:spring-boot-starter-test') {
exclude module: 'junit'
}
testImplementation('org.junit.jupiter:junit-jupiter-api')
testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
We can also see some additional dependencies for Jackson, Junit5 (Jupiter) and an in-memory H2 database.
This results in a neat setup with valid Hibernate entities:
The Entity...
- ... must be annotated with the
javax.persistence.Entity
annotation (or be denoted as such in XML mapping, which we won't consider) ✔️- ... must have a public or protected (or package-private) no-argument constructor. It may define additional constructors as well ✔️
- ... should not be final. No methods or persistent instance variables of the entity class may be final (technically possible but not recommended) ✔️
- ... may extend non-entity classes as well as entity classes, and non-entity classes may extend entity classes. Both abstract and concrete classes can be entities ✔️
- ... may provide JavaBean-style properties. This is not a must, i.e. setters are not necessary ✔️ (We accept not to have setters)
- ... must provide an identifier attribute (
@Id
annotation), recommended to use nullable, non-primitive, types ✔️- ... needs to provide useful implementations for
equals
andhashCode
✔️
A simple repository
Thanks to Spring, the implementation for a repository that exposes Person
entities is quite easy:
interface PersonRepository : JpaRepository<Person, Long> {
fun getByAddressStreet(street: String): Person?
}
The interface org.springframework.data.jpa.repository.JpaRepository
defines common CRUD operations and we add custom ones by extending the interface with PersonRepository
. You can find out more about this mechanism here. As you might guess, the implementation of such an abstract repository definition happens via Spring.
You could now go on and inject this repository into controllers and expose a CRUD API to users. For simplicity, observe the following test case:
@ExtendWith(SpringExtension::class)
@SpringBootTest
class HibernateDemoApplicationTests(@Autowired val repo: PersonRepository) {
@Test
fun `basic entity checks`() {
val p = Person("Paul", Address("HelloStreet", "A-55", "Paris"))
val hashCodeBefore = p.hashCode()
val personSet = hashSetOf(p)
repo.save(p)
val hashCodeAfter = p.hashCode()
assertThat(repo.findAll()).hasSize(1)
assertThat(personSet).contains(p)
assertThat(hashCodeAfter).isEqualTo(hashCodeBefore)
}
}
This test runs on JUnit 5, which allows constructor injection for certain objects. The used SpringExtension
adds support for autowired dependencies and, as a result, we can inject the PersonRepository
into the test class.
In the test case itself, we create a sample Person
object, persist it by using the repository and then verify that it can be found via findAll
. Assertions are based on org.assertj
.
In addition, the test verifies that the hashCode
for a Person
does not change after it got persisted through Hibernate and that a HashSet
works properly with these entities.
Further Hibernate Topics
In this article, we focused mainly on defining Hibernate entity classes since this task is probably the most vital one to do. We saw that a few constraints need to be fulfilled and that compiler plugins help us with integrating Hibernate with Kotlin. All of the demonstrated stuff is just a tiny set of Hibernate. Thanks to Spring though, many things like querying and transaction handling can easily be abstracted, which we made use of here. If you have doubts, I suggest to read "How Hibernate Almost Ruined my Career" very carefully - Hibernate can cause many headaches obviously ;-).
The source code can be found in my hibernateOnKotlin repository on GitHub.
Please don't hesitate to reach out if you use different best practices in your applications.
Also, if you like, have a look at my Twitter account and other Kotlin related posts on this page and follow if you’re interested in more Kotlin stuff 🙂
Thanks a lot.
Simon is a software engineer with 9+ years of experience developing software on multiple platforms including the JVM and Serverless environments. He currently builds scalable distributed services for a decision automation SaaS platform. Simon is a self-appointed Kotlin enthusiast.
How would one go about updating an Person entity in this example?
Do all properties in Person class need to be changed to
var
? find an existing person from DB, update its properties to required changes and save?[…] Hibernate with Kotlin – powered by Spring Boot describes, of all things, using Kotlin, Hibernate, and Spring Boot together. (The summary has a good laundry list of things to pay attention to.) […]
So would you advice against using
AbstractPersistable
from Spring Data as base class for our entities? (https://github.com/spring-projects/spring-data-jpa/blob/master/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java)Exactly, since it doesn’t provide a constant
hashCode
[…] Simon Wirtz has a nice post on using Hibernate and Spring Boot with Kotlin […]
Hi man, thanks for such cool description. Maybe you may help me. I am struggling with @ManyToOne and @OneToMany bidirectional associations. But still, see additional selects before insert. For associated entities, I use getOne(id:UUID) method. But it instead of the reference (proxy) I get an instance of the entity. What I have missed, is it possible that Kotlin unwraps proxy?
I noticed that the entity fields are val, instead of var. How would you handle updates then?
Are you sure you really want every persistableobject hashcode to be the same value (31)? A lot of this article hinges on this and it seems wrong.
That part is described in “Hibernate Suggestion” which refers to the documentation of Hibernate. Did you see this part? What exactly are you concerned about?
Technically “it works”, but it’s certainly something I’d expect a team to address in code review. As it seems like you recognize (by this comment- “may improve the performance of hash tables.”) hashes perform better when used properly. but it’s not something I’d advise developers to leverage…unless they want db lookups to magically slow down.
My point here is do the work up-front. Easier than debugging it later on a distributed cluster of hanging spring servers.
Hi there, thanks for sharing it, really helpful. Just one question in regards to the hashcode contract. I suspect it might be a valid situation when you update the id and the hashcode changes, it seems to be a valid thing to do according to the docs.
It reads:
‘…the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified.’
And we modified a piece of information used in the equals after saving it.
Regards
Hey, if I have a secondary constructor, IDE warns about non-final property there. I assume I can just ignore it, since you have that same situation but you use primary constructor. see more https://discuss.kotlinlang.org/t/constructor-and-hibernate/13104
[…] to it. I studied and tried many approaches but in the end, I found Simon Wirtz’s solution in Hibernate with Kotlin – powered by Spring Boot is the most elegant. Though with small […]