SQL.What are the problems of composite keys to equals / hash code in ORM ?

Why composite keys break equals() / hashCode() in ORM

ORMs rely on equals() / hashCode() to track entity identity.
Composite keys make identity mutable, incomplete, or unstable during an entity’s lifecycle.

That’s the root problem.


First: how ORM uses equals() / hashCode()

Hibernate uses them in:

  • Persistence Context (1st-level cache)
  • Set / Map collections
  • Dirty checking
  • Association management
  • Proxy resolution

Hibernate assumes:

  • Identity is stable
  • Identity exists from creation to deletion
  • equals() does not change over time

Composite keys violate these assumptions.

Problem 1️⃣: null values before persist (very common)

Composite key example

@Embeddable
class UserRoleId {
    Long userId;
    Long roleId;
}

Before persist():

userId = null
roleId = null

But equals() / hashCode() must already work.

What happens

  • hashCode() changes after IDs are assigned
  • Entity moves between hash buckets
  • ORM loses track of the entity

What happens

  • hashCode() changes after IDs are assigned
  • Entity moves between hash buckets
  • ORM loses track of the entity

Result

  • Entity “disappears” from Set
  • Duplicate entries appear
  • Updates silently fail

This bug is nightmare-level to debug.

Problem 2️⃣: Mutable identity (identity must be immutable)

Composite keys are built from:

  • Foreign keys
  • Business columns

These can change.

Example:

(user_id, role_id)

What if:

  • Role is reassigned
  • User is merged
  • FK updated

Now:

  • Primary key changes
  • equals() / hashCode() changes
  • ORM breaks identity tracking

Surrogate IDs never change.

Problem 3️⃣: Partial equality before flush

Entity lifecycle:

  1. New entity created
  2. Added to a Set
  3. Persisted
  4. Flushed → ID assigned

With composite key:

  • Step 2: equality based on nulls
  • Step 4: equality based on real values

This violates the Java contract:

If an object is in a HashSet, its hashCode() must not change.

Composite keys almost guarantee violation.

Problem 4️⃣: Proxy vs real object comparison

Hibernate often compares:

  • Proxy instance
  • Real entity instance

With composite PK:

  • Multiple fields to compare
  • More chances of mismatch
  • Broken lazy-loading equality

Example bug:

proxy.equals(entity) == false

This breaks collections and caches.

Problem 5️⃣: Developers implement equals/hashCode wrong

Typical mistakes:

  • Forgetting one key field
  • Including non-key fields
  • Using mutable fields
  • Using Lombok @Data blindly

With composite keys:

  • More fields = more risk
  • One mistake = silent corruption

Problem 6️⃣: Identity vs business equality confusion

Composite keys blur the line:

  • Is (userId, roleId) identity?
  • Or just a uniqueness rule?

ORM wants:

  • Identity = immutable technical key

Composite keys encode business rules into identity.

That’s a conceptual mismatch.


Concrete failure example (realistic)

Set<UserRole> roles = new HashSet<>();

UserRole ur = new UserRole();
ur.setUser(user);
ur.setRole(role);

roles.add(ur);   // hashCode based on nulls

entityManager.persist(ur);
entityManager.flush();  // IDs assigned → hashCode changes

roles.contains(ur); // false ❌

Why surrogate keys avoid all of this

@Id
Long id;
  • Assigned once
  • Immutable
  • Simple equality
  • Stable hash code

ORMs are optimized for this model.

One-sentence interview answer (perfect)

Composite keys make entity identity depend on multiple mutable or initially-null fields, which causes equals() and hashCode() to change during the entity lifecycle and breaks ORM caching and collections.

Final mental model (remember this)

ORM identity must be boring.
Composite keys are interesting — and that’s the problem.

This entry was posted in Без рубрики. Bookmark the permalink.