Question
Kotlin and JPA Entities: Default Constructors, Immutability, and Practical Patterns
Question
In JPA, @Entity classes must provide a no-argument constructor so the framework can create objects when loading them from the database.
In Kotlin, it is very natural to define properties in the primary constructor, for example:
class Person(val name: String, val age: Int)
However, if you add the required no-argument constructor as a secondary constructor, you must still pass values to the primary constructor:
@Entity
class Person(val name: String, val age: Int) {
private constructor() : this("", 0)
}
This becomes awkward when the properties are complex, non-nullable types rather than simple values like String or Int. Supplying placeholder values can make the code harder to read and maintain. It is especially problematic if the primary constructor or init blocks contain real logic, because that logic will run during construction even though JPA may later replace property values through reflection.
Also, val properties cannot be reassigned after construction, so using JPA in this way appears to conflict with immutability.
How should Kotlin code be designed to work well with JPA without duplicating code, inventing "magic" default values, or giving up immutability unnecessarily?
Also, is it true that Hibernate, beyond the JPA specification itself, can instantiate objects even if they do not have a default constructor?
Short Answer
By the end of this page, you will understand why JPA expects entity classes to have a no-argument constructor, why this clashes with idiomatic Kotlin constructor-based design, and what patterns Kotlin developers commonly use in real projects. You will also learn when val properties are problematic in JPA entities, how Hibernate differs from the JPA spec, and how Kotlin compiler plugins can remove much of the boilerplate.
Concept
JPA was designed around Java-style classes that frameworks can instantiate and then populate. Because of that, JPA expects entity classes to have a no-argument constructor that is at least protected or public.
Kotlin encourages a different style:
- properties in the primary constructor
- non-null types
valfor immutability- initialization logic close to the constructor
These are excellent language features, but JPA entity management works differently.
Why JPA wants a no-arg constructor
When JPA loads data from the database, it needs a way to create an empty object instance first. After that, it fills in the fields. A no-argument constructor gives the framework a reliable way to do that.
Why this feels awkward in Kotlin
In Kotlin, entity classes are often written like regular domain classes:
class Person(val name: String, val age: Int)
But JPA does not naturally work with that style because:
- it may create the object before values are available
- it may inject values after construction
- proxies and lazy loading often require classes and properties to be mutable or overridable
The real mismatch
The core issue is not just the constructor. It is that:
- Kotlin favors constructor-initialized, immutable objects
Mental Model
Think of a JPA entity like a house being prepared by a moving company.
In idiomatic Kotlin, you usually want to build the whole house fully furnished from the start:
- all required values supplied in the constructor
- everything fixed in place
- immutable where possible
JPA works more like this:
- it first creates an empty house shell
- then it moves furniture into the rooms
- sometimes it leaves some rooms as placeholders for lazy loading
That means JPA needs a class it can create first and fill later.
So a JPA entity is less like a perfectly locked immutable value object, and more like a framework-managed container whose state is assembled over time.
If you need true immutability, it is often better to map the entity to a separate domain object after loading.
Syntax and Examples
A common Kotlin + JPA entity style looks like this:
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
@Entity
class Person(
var name: String,
var age: Int
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null
protected constructor() : this("", 0)
}
What this example shows
@Entitymarks the class as a JPA entity- JPA can use the no-arg constructor
- properties are
varso the framework can set them idis nullable because it may not exist before persistence
This works, but the placeholder values are not ideal.
Better approach: use the Kotlin JPA plugin
A more idiomatic approach is to let the compiler generate the no-arg constructor for entity classes.
For Gradle Kotlin DSL:
Step by Step Execution
Consider this entity:
@Entity
class Person(
var name: String,
var age: Int
) {
@Id
var id: Long? = null
protected constructor() : this("", 0)
}
Now imagine JPA loads one row from the database.
Step-by-step
1. JPA creates the object
It calls the no-argument constructor:
Person()
At this point, the object temporarily contains:
name = ""age = 0id = null
2. JPA populates fields
Using reflection or its internal access strategy, JPA sets the real values from the database row.
For example, after loading:
name = "Alice"age = 30
Real World Use Cases
This concept appears in many real applications.
Database-backed web applications
In a Spring Boot app, entities represent database tables. JPA loads records by instantiating entity objects internally, so a no-arg constructor is needed.
Admin panels and CRUD systems
When users edit rows such as products, users, or orders, JPA reads and updates entity instances repeatedly. Mutable entity fields make this practical.
Lazy loading of relationships
An entity may have related objects such as:
Order->CustomerPost->AuthorInvoice->LineItems
JPA providers often use proxies or delayed initialization, which fits better with framework-managed mutable entities than with fully immutable constructor-only objects.
Domain model separation
In larger codebases, teams often use:
- JPA entities for persistence
- immutable domain models for business logic
- DTOs for API input/output
This avoids forcing one class to solve persistence, validation, and API concerns at the same time.
Real Codebase Usage
In real projects, developers rarely put complex constructor logic directly in JPA entities.
Common patterns
1. Simple entity, richer service layer
Entities are kept lightweight:
@Entity
class Account(
var email: String,
var status: String
) {
@Id
@GeneratedValue
var id: Long? = null
protected constructor() : this("", "NEW")
}
Validation and rules happen in services:
fun createAccount(email: String): Account {
require(email.contains("@"))
return Account(email, "NEW")
}
2. Guard clauses outside the entity
Instead of relying on constructor validation, applications validate inputs before creating or saving entities.
3. Entity-to-domain mapping
Persistence model:
@Entity
(
username: String,
active:
) {
id: ? =
}
Common Mistakes
1. Using val for fields that JPA must populate
Broken or risky example:
@Entity
class Person(
val name: String,
val age: Int
)
Why it is a problem:
- JPA may need to assign values after construction
- immutable properties do not fit many provider behaviors
Safer approach:
@Entity
class Person(
var name: String,
var age: Int
) {
protected constructor() : this("", 0)
}
2. Putting validation in init that rejects placeholder construction
Broken example:
@Entity
class Person(
var name: String
) {
init {
require(name.isNotBlank())
}
protected constructor() : ()
}
Comparisons
| Approach | Pros | Cons | Best use |
|---|---|---|---|
| Secondary no-arg constructor with placeholder values | Simple, explicit, no plugin required | Boilerplate, fake values, constructor logic may misbehave | Small demos or simple entities |
Kotlin jpa/no-arg plugin | Clean entity code, less duplication | Requires build configuration | Most Kotlin + JPA projects |
val constructor properties | Immutable style, concise | Often conflicts with JPA population and proxies | Domain models, DTOs, value objects |
var entity properties | Works well with JPA lifecycle | Less strict immutability | JPA entities |
| Separate entity and domain model |
Cheat Sheet
Quick rules for Kotlin + JPA entities
- JPA entities should have a no-arg constructor.
- In Kotlin, use the
kotlin("plugin.jpa")plugin when possible. - Prefer
varovervalfor entity properties. - Avoid complex logic in entity constructors and
initblocks. - Keep entity classes simple and persistence-focused.
- Use separate domain models if you want strong immutability.
- IDs are often nullable before persistence.
- Be aware that Kotlin classes are final by default; JPA/Hibernate may need open classes for proxies.
Typical entity shape
@Entity
class Product(
var name: String,
var price: BigDecimal
) {
@Id
@GeneratedValue
var id: Long? = null
protected constructor() : this(BigDecimal.ZERO.toString(), BigDecimal.ZERO)
}
Better with plugin
@Entity
class Product(
var name: String,
var price: BigDecimal
) {
id: ? =
}
FAQ
Why does JPA require a no-argument constructor?
JPA needs a simple way to instantiate entity objects before populating their fields from database data.
Can I use val properties in Kotlin JPA entities?
Sometimes it may compile, but it is usually not a good fit. JPA often expects entity state to be assignable after construction, so var is safer.
Is the Kotlin JPA plugin the recommended solution?
Yes, in many Kotlin + JPA projects it is the cleanest way to avoid writing boilerplate no-arg constructors manually.
Should I put validation in an entity constructor?
Usually only very carefully. Constructor and init logic can conflict with how JPA creates and hydrates entities.
Are Kotlin data classes good JPA entities?
Usually not by default. Data classes are designed for value semantics, while JPA entities have identity, lifecycle, and proxy concerns.
Can Hibernate instantiate entities without a default constructor?
Hibernate has provider-specific internals and may support more than plain JPA in some cases, but relying on that reduces portability. The safer practice is still to provide what JPA expects.
How do I keep immutability if JPA entities are mutable?
A common solution is to keep entities mutable for persistence and convert them to immutable domain models or DTOs in the service layer.
Mini Project
Description
Build a small persistence model for a user registry using Kotlin and JPA. The project demonstrates how to define a JPA entity that works with framework requirements while keeping your business-facing model separate and immutable. This mirrors a common real-world pattern used in backend services.
Goal
Create a mutable JPA entity and map it to an immutable domain model without using constructor validation that breaks JPA loading.
Requirements
- Create a
UserEntityJPA class withid,username, andemailfields. - Make the entity compatible with JPA construction requirements.
- Create a separate immutable
Userdomain model. - Write mapping functions between
UserEntityandUser. - Add a simple factory function that validates input before creating the entity.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.
Android AlarmManager Example: Scheduling Tasks with AlarmManager
Learn how to use Android AlarmManager to schedule tasks, set alarms, and handle broadcasts with a simple beginner example.