Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
kotlin avatar

Kotlin Backend Jpa Entity Mapping

  • 883 installs
  • 983 repo stars
  • Updated July 21, 2026
  • kotlin/kotlin-agent-skills

kotlin-backend-jpa-entity-mapping is a JetBrains v1.0.0 Claude Code skill that generates and reviews Kotlin JPA entity classes for Spring Data JPA and Hibernate while avoiding data-class equals pitfalls, N+1 queries, and

About

kotlin-backend-jpa-entity-mapping is an official JetBrains skill (version 1.0.0, Apache-2.0) from kotlin/kotlin-agent-skills for modeling Kotlin persistence with Spring Data JPA and Hibernate. It covers entity design, identity and equality, uniqueness constraints, relationships, fetch plans, and ORM traps specific to Kotlin such as data class entities and broken equals/hashCode. Developers reach for kotlin-backend-jpa-entity-mapping when creating or reviewing JPA entities, diagnosing N+1 or LazyInitializationException, placing indexes, or preventing Kotlin-specific Hibernate bugs. The skill applies whenever JPA entity mapping correctness matters more than generic Kotlin syntax help.

  • Prevents data class equals/hashCode corruption of persistent collections
  • Guides correct identity strategy, uniqueness constraints and index placement
  • Defines safe use of lateinit, non-null properties and fetch plans
  • Separates persistence entities from transport DTOs
  • Diagnoses and resolves N+1 queries and LazyInitializationException

Kotlin Backend Jpa Entity Mapping by the numbers

  • 883 all-time installs (skills.sh)
  • +40 installs in the week ending Aug 5, 2026 (Skillselion tracking)
  • Ranked #457 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/kotlin/kotlin-agent-skills --skill kotlin-backend-jpa-entity-mapping

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs883
repo stars983
Last updatedJuly 21, 2026
Repositorykotlin/kotlin-agent-skills

How do you map Kotlin JPA entities correctly?

Generate and review correct JPA entity classes in Kotlin that avoid Hibernate and Spring Data pitfalls.

Who is it for?

Kotlin backend developers using Spring Data JPA and Hibernate who hit data-class entity or lazy-loading pitfalls.

Skip if: Non-JVM stacks, raw SQL-only persistence layers, or frontend Kotlin Multiplatform UI work without JPA.

When should I use this skill?

User creates JPA entities in Kotlin, reports N+1 or LazyInitializationException, or asks about Hibernate relationship and index mapping.

What you get

Reviewed or generated Kotlin JPA entity classes with indexes, relationships, fetch plans, and safe equals/hashCode.

  • Kotlin JPA entity classes
  • ORM mapping review notes

By the numbers

  • JetBrains official skill version 1.0.0 under Apache-2.0 license

Files

SKILL.mdMarkdownGitHub ↗

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.
  • For DB-generated IDs, model the unsaved state with nullable var id: Long? = null

and a protected set; do not use 0L as a sentinel value.

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

Broken: data class Entity

// WRONG: data class generates equals/hashCode from ALL fields,
// and the generated ID uses a 0 sentinel instead of null
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)
    var id: Long? = null
        protected set

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is Order) return false
        return id != null && 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; DB-generated id is nullable with a protected setter

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)
    var id: Long? = null
        protected set
}

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.

Related skills

FAQ

Why avoid Kotlin data classes for JPA entities?

kotlin-backend-jpa-entity-mapping warns that Kotlin data class entities break JPA identity and equals/hashCode semantics under Hibernate. The skill recommends entity designs that preserve stable persistence identity across managed and detached states.

What ORM issues does kotlin-backend-jpa-entity-mapping address?

kotlin-backend-jpa-entity-mapping covers N+1 queries, LazyInitializationException, uniqueness constraints, fetch plans, and relationship mapping for Spring Data JPA. It targets Kotlin-specific traps that generic Java JPA guides omit.

Backend & APIsbackendintegrations

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.