Generated banner for Kotlin Backend Jpa Entity Mapping
Generated logo for Kotlin Backend Jpa Entity Mapping

Kotlin Backend Jpa Entity Mapping

JetBrainsOfficialCreated by JetBrainsDistributed by JetBrains

  • Skill
  • Guidelines
  • Software development
  • Apache-2.0
  • No issues found

From the author

Model Kotlin persistence code correctly for Spring Data JPA and Hibernate. Covers entity design, identity and equality, uniqueness constraints, relationships, fetch plans, and common ORM (Object-Relational Mapping) traps specific to Kotlin. Use when creating or reviewing JPA (Java Persistence API) entities, diagnosing N+1 or LazyInitializationException, placing indexes and uniqueness rules, or preventing Kotlin-specific bugs such as data class entities and broken equals/hashCode.
— the description by JetBrains

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides essential guidelines for implementing JPA entities in Kotlin applications using Spring Data JPA and Hibernate. It addresses the specific challenges of using Kotlin with ORM frameworks, focusing on correct identity management, equality implementation, and database constraints. It helps developers avoid common bugs related to Kotlin's data classes, lazy loading, and bidirectional associations, ensuring robust persistence layer design.

What it does

  • Defines rules for correct JPA entity design in Kotlin.
  • Explains why data classes are unsuitable for JPA entities.
  • Provides patterns for stable equals and hashCode implementations.
  • Guides the implementation of database-level and application-level uniqueness constraints.
  • Offers strategies for diagnosing and fixing N+1 query issues.
  • Outlines best practices for managing lazy loading and fetch plans.

When to use it

  • When designing new JPA entities in a Kotlin project.
  • When debugging issues like LazyInitializationException or unexpected data corruption.
  • When reviewing entity code for potential ORM traps.
  • When implementing uniqueness constraints for idempotent operations.
  • When optimizing query performance and fetch strategies.

Advantages

  • Prevents common Kotlin-specific ORM bugs.
  • Ensures consistent and stable entity identity.
  • Reduces the risk of data corruption in collections.
  • Improves application reliability through proper constraint handling.
  • Provides clear patterns for avoiding N+1 performance issues.

Usage examples

“How should I implement equals and hashCode for a JPA entity in Kotlin?”
The skill explains why data class generated methods are dangerous and provides a template for a regular class using ID-based equality and a constant hashCode.
“Why am I getting a LazyInitializationException in my Kotlin entity?”
The skill identifies common triggers like toString or JSON serialization and provides strategies for managing fetch plans and transaction boundaries.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/jetbrains/kotlin-backend-jpa-entity-mapping/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip kotlin-backend-jpa-entity-mapping.zip -d .agents/skills

Documentation by JetBrains

JPA Entity Mapping for Kotlin

Kotlin's data class is natural for DTOs but dangerous for JPA entities. Hibernate relies on identity semantics that data class breaks: equals/hashCode over all fields corrupts Set/Map membership after state changes, and auto-generated copy() creates detached duplicates of managed entities.

This skill teaches correct entity design, identity strategies, and uniqueness constraints for Kotlin + Spring Data JPA projects.

Entity Design Rules

  • Never use data class for JPA entities. Use a regular class. Keep data class for DTOs.

  • Keep transport DTOs and persistence entities separate unless the project clearly uses a shared model.

  • Model required columns as non-null only when object construction and persistence lifecycle make it safe.

  • Use lateinit only when the project already accepts that tradeoff and the lifecycle is safe.

  • Verify kotlin("plugin.jpa") or equivalent no-arg support when JPA entities exist.

  • Verify classes and members are compatible with proxying where needed.

Identity and Equality

  • Never accept all-field equals/hashCode generated by data class on an entity.

  • Follow project conventions when they already define an identity strategy.

  • If no convention exists, use ID-based equality with a stable hashCode.

  • Be explicit about mutable fields and lazy associations when discussing equality.

Broken: data class Entity
// WRONG: data class generates equals/hashCode from ALL fields
data class Order(
    @Id @GeneratedValue val id: Long = 0,
    var status: String,
    var total: BigDecimal
)
// BUG: order.status = "SHIPPED"; set.contains(order) → false (hash changed)
// BUG: Hibernate proxy.equals(entity) → false (proxy has lazy fields uninitialized)
Correct: Regular Class with ID-Based Identity
@Entity
@Table(name = "orders")
class Order(
    @Column(nullable = false)
    var status: String,

    @Column(nullable = false)
    var total: BigDecimal
) {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long = 0

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is Order) return false
        return id != 0L && id == other.id
    }

    override fun hashCode(): Int = javaClass.hashCode()

    // toString must NOT reference lazy collections
    override fun toString(): String = "Order(id=$id, status=$status)"
}

Key rules:

  • equals compares by ID only — stable under dirty tracking and proxy unwrapping

  • hashCode returns class-based constant — avoids Set/Map corruption after persist

  • toString excludes lazy-loaded relations — prevents LazyInitializationException

  • Constructor params are mutable entity fields; id is val with default

Uniqueness Constraints

When an API must be idempotent (e.g., "reserve stock for order X"), enforce uniqueness at both layers: database constraint for correctness, application check for clean errors.

Broken: No Duplicate Guard
@Service
class ReservationService(private val repo: ReservationRepository) {
    @Transactional
    fun createReservation(variantId: Long, orderId: String, qty: Int): Reservation {
        // BUG: no check — duplicates silently accumulate
        return repo.save(Reservation(variantId = variantId, orderId = orderId, quantity = qty))
    }
}
Correct: Database Constraint + Application Guard
@Entity
@Table(
    name = "reservations",
    uniqueConstraints = [
        UniqueConstraint(columnNames = ["variant_id", "order_id"])
    ]
)
class Reservation(
    @Column(name = "variant_id", nullable = false)
    val variantId: Long,

    @Column(name = "order_id", nullable = false)
    val orderId: String,

    @Column(nullable = false)
    var quantity: Int
) {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long = 0
}

interface ReservationRepository : JpaRepository<Reservation, Long> {
    fun findByVariantIdAndOrderId(variantId: Long, orderId: String): Reservation?
}

@Service
class ReservationService(private val repo: ReservationRepository) {
    @Transactional
    fun createReservation(variantId: Long, orderId: String, qty: Int): Reservation {
        repo.findByVariantIdAndOrderId(variantId, orderId)?.let {
            throw IllegalStateException(
                "Reservation already exists for variant=$variantId, order=$orderId"
            )
        }
        return repo.save(Reservation(variantId = variantId, orderId = orderId, quantity = qty))
    }
}

Key rules:

  • Database constraint is mandatory — application checks alone have race conditions

  • Application check provides clean error messages — without it, users get raw DataIntegrityViolationException

  • Both layers together: application catches the common case, database catches the race

  • Spring Data derives findByXAndY queries automatically

Query and Fetch Rules

  • Diagnose N+1 by looking at actual query count or SQL logs, not by guessing from annotations.

  • Prefer targeted fetch solutions: @EntityGraph, JOIN FETCH, batch fetching, or DTO projection.

  • Be careful with collection fetch joins plus pagination — call out the tradeoff.

  • Use indexes and uniqueness constraints to support real query patterns.

Common ORM Traps

  • Bidirectional associations: maintain both sides in domain methods. Half-updated graphs cause subtle bugs.

  • orphanRemoval vs cascade remove: not interchangeable. Explain lifecycle semantics before choosing.

  • Lazy load triggers: toString, debug logging, JSON serialization, and IDE inspection can all trigger lazy loads.

  • Bulk updates/deletes: bypass persistence context and lifecycle callbacks. Subsequent reads may be stale.

  • Multiple bag fetches: can cause Cartesian explosion. Verify the ORM can execute collection-heavy fetch plans safely.

  • Set + mutable equality: collection membership can break after entity state changes.

  • @Version: the clearest optimistic concurrency mechanism when concurrent updates matter.

  • open-in-view disabled: DTO mapping touching lazy fields must happen inside a transaction boundary.

Guardrails

  • Do not use data class for JPA entities.

  • Do not recommend FetchType.EAGER everywhere to silence lazy loading symptoms.

  • Do not expose entities directly through API responses by default.

  • Do not claim an N+1 fix without explaining how the fetch plan changes query behavior.

Files

ATTRIBUTION.md

Attribution

This folder is an unmodified copy of the Agent Skill "kotlin-backend-jpa-entity-mapping", distributed by AutoGineer (https://autogineer.com/agent-skills).

The skill's own files are unmodified copies. AutoGineer is not affiliated with or endorsed by the author or the publisher.

Files added by AutoGineer

  • ATTRIBUTION.md: this file, written by AutoGineer

  • LICENSE: the SPDX text of Apache-2.0 (spdx/license-list-data v3.29.0 (31ba1a50e5397e00a304dbadc76531740e89ee48))

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
JetBrains
Publisher
JetBrains (Official)
Distributed by
JetBrains
Source
JetBrains/skills · kotlin-backend-jpa-entity-mapping · commit 3f53782
Licence
Apache License 2.0 (redistribution allowed)
  • LICENSE (written by AutoGineer)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by JetBrains.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download kotlin-backend-jpa-entity-mapping.zip