Scenario First

Three-step inheritance, one hard equality decision

The lab is not asking whether the objects look similar. It asks whether Java should treat them as the same value when collections, JPA, caches, and retries start using equals and hashCode.

Java default

Without an override, new TraderParty(...) and another separately created object are different references.

Domain expectation

The business may say same party, same employee, same role, same JPA row, or just related. Those are different rules.

Failure mode

Picking the wrong rule causes HashMap misses, HashSet merges, symmetry breaks, and Hibernate proxy surprises.

Inheritance map used by the lab

A, B, and C live on the same person/employee inheritance path. D is a different branch under the same root.

Root
Party
Step 1
IndividualParty
Step 2
EmployeeParty
Step 3 role leaves
A TraderPartyB ManagerPartyC EmployeePartyHibernateProxy
Other branch
InstitutionParty
D Institution counterparty

This branch shares the root class, but not the person/employee identity path.

E1-style all possibilities atlas

This is the same idea as the engineering note: one anchor record, several related records, and every possible meaning of "same".

Compact mobile view shows the same nodes as cards. Open this page on a tablet or desktop screen to see the full arrow map.

P1IndividualPartypartyNo=P-100, person only

Parent-side view of the same person.

E1ATraderPartypartyNo=P-100, employeeNo=E-42, role=Trader, desk=FX

Main employee role record. This is the anchor.

E1BManagerPartypartyNo=P-100, employeeNo=E-42, role=Manager, desk=Risk

Same person and employee, different role record.

E2ATraderPartypartyNo=P-200, employeeNo=E-77, role=Trader, desk=FX

Different person, same trader role and desk.

E1A2TraderPartypartyNo=P-100, employeeNo=E-42, role=Trader, desk=FX, title=Lead

Different Java object, same employment key, changed snapshot field.

ORG1InstitutionPartypartyNo=ORG-900, legalRegistration=HRB-900

Same root class family, different business branch.

P1 -> E1A: same person, parent-child view
E1A -> E1B: same employee, different role
E1A -> E2A: same role/desk, different person
E1A -> E1A2: same employment key, changed snapshot
E1A -> ORG1: same root only, different branch
P1IndividualPartypartyNo=P-100, person only
E1ATraderPartypartyNo=P-100, employeeNo=E-42, role=Trader, desk=FX
E1BManagerPartypartyNo=P-100, employeeNo=E-42, role=Manager, desk=Risk
E2ATraderPartypartyNo=P-200, employeeNo=E-77, role=Trader, desk=FX
E1A2TraderPartypartyNo=P-100, employeeNo=E-42, role=Trader, desk=FX, title=Lead
ORG1InstitutionPartypartyNo=ORG-900, legalRegistration=HRB-900
Comparison==Same party?Same employee?Same role?Same employment?Meaning
P1 vs E1Afalsetruenot applicablenot applicablenot applicableParent view and child role record can describe the same person.
E1A vs E1BfalsetruetruefalsedependsSame human and employee number; role-specific workflows may keep them separate.
E1A vs E2AfalsefalsefalsetruefalseSame role/desk is a relation or filter, not object equality.
E1A vs E1A2falsetruetruetruetrueTwo new Java objects can represent the same employment record.
E1A vs ORG1falsefalsefalsefalsefalseThey share the Party root only; equality should stay false.
RuleMeaningP1-E1AE1A-E1BE1A-E2AE1A-E1A2E1A-ORG1equals fit
Reference same / ==Same object instance in memoryfalsefalsefalsefalsefalseJava default
Default Object.equalsSame reference unless overriddenfalsefalsefalsefalsefalseOverride yoksa bu
Only partyNoSame person / partytruetruefalsetruefalseParent identity icin olabilir
employeeNo onlySame employee numberfalsetruefalsetruefalseBroad employee equality
employeeNo + roleSame role-specific employee recordfalsefalsefalsetruefalseRole-strict equality
role / desk onlySame responsibility or deskfalsefalsetruetruefalseDomain method, not equals
all fields snapshotSame immutable value snapshotfalsefalsefalsefalsefalseValue object ise
DB idSame persisted rowdependsfalsefalsetruefalseJPA lifecycle dikkat
partyNo OR roleSame person or same roletruetruetruetruefalseDanger
exact class + keyRuntime class and selected key must matchfalsefalsefalsetruefalseProxy riski var
composition keyDedicated identity value object decidesdesigndesigndesigndesigndesignUsually cleanest

Relationship graph

The arrows show why one equality rule is not enough. Same root, same employee, same JPA row, and different branch are separate ideas.

Compact mobile view shows the same nodes as cards. Open this page on a tablet or desktop screen to see the full arrow map.

ATraderPartyemployee E-42, role trader, dbId 501
BManagerPartysame employee E-42, role manager
CHibernate proxysame dbId 501, proxy target EmployeeParty
DInstitutionPartydifferent branch, legal registration key
same employee, different role
same persisted row if proxy-safe
same root only, different branch

What should happen?

This is the part that was missing: Java has a default answer, but the domain needs an explicit rule.

PairJava defaultDomain questionExpected decision
A vs BfalseSame person and employee number, but Trader role vs Manager role.Usually related, not equal, when role records must stay separate.
A vs CfalseA is a concrete role object; C is a Hibernate proxy for the employee row.Can be equal under proxy-safe JPA when the persisted id matches.
A vs DfalseA is a person/employee role; D is a legal institution branch.Not equal. A legal entity should not be merged by employee rules.

All rule outcomes for the scenario

This is the design table: each row is a possible implementation choice, and each pair shows the consequence.

RuleA-BA-CA-DWhen usefulRisk
Java referencedifferentdifferentdifferentOnly same object instance should be equal.Business duplicates never merge.
Party business keyequalequaldifferentEvery subtype is just another view of the same party.Trader and Manager roles can collapse too early.
Broad employee keyequalequaldifferentSame employee number means one employee value.Role-specific approval records can be lost.
Role-strict keyrelatedrelateddifferentSame human can have separate role records.People may expect same employee to mean equal.
JPA id, strict classdifferentdifferentdifferentConcrete entity class and non-null id must match.Hibernate proxy class can produce false negatives.
Proxy-safe JPA iddifferentequaldifferentHibernate proxy and real entity can represent the same row.Null ids still need special handling.
Institution legal keynot applicablenot applicabledifferentLegal entities are compared by registration/BIC style keys.Should not leak into employee/person equality.

Interactive Workbench

Build a domain equality scenario

Add or remove objects, choose the anchor, compare one or more targets, then switch strategies to see how Java behavior changes. Anchor is the left side; compare targets are the right side.

Local preview is active. Run API evaluation when you want to exercise the route.
AnchorA

The left-side object. Every selected target is compared against this object.

Compare targetsB, C

These are the right-side objects. More than one target can be selected.

StrategyRole strict

The selected equals/hashCode rule. Switching it recalculates the matrices.

Active resultInstant local engine

Run API sends the same payload through the Next route to the Spring Boot pod when configured.

Objects

A

A: Trader role for employee E-42

TraderParty

tenant=eupartyNo=P-100employeeNo=E-42role=traderdbId=501

Same employee/person line as B and C. The open question is whether role records should be equal or only related.

B

B: Manager role for employee E-42

ManagerParty

tenant=eupartyNo=P-100employeeNo=E-42role=managerdbId=502

Same employee number and party number as A, but a different role leaf under EmployeeParty.

C

C: Hibernate proxy for employee row

EmployeePartyHibernateProxy

tenant=eupartyNo=P-100employeeNo=E-42role=employeedbId=501

Proxy-like runtime class. Same database id as A, so proxy-safe JPA can treat it as the same persisted row.

D

D: Institution counterparty

InstitutionParty

tenant=eupartyNo=ORG-900role=institutiondbId=900legal=HRB-900

Different branch under Party. It is a legal entity, not a person or employee role.

Add object

Strategies

Visible result modules

Toggle modules to keep the workbench focused during explanation.

Selected object lineage

The cards below show exactly where each object sits in the inheritance tree.

A

A: Trader role for employee E-42

Anchor object

  1. Party
  2. IndividualParty
  3. EmployeeParty
  4. TraderParty

Same employee/person line as B and C. The open question is whether role records should be equal or only related.

B

B: Manager role for employee E-42

Compared object

  1. Party
  2. IndividualParty
  3. EmployeeParty
  4. ManagerParty

Same employee number and party number as A, but a different role leaf under EmployeeParty.

C

C: Hibernate proxy for employee row

Compared object

  1. Party
  2. IndividualParty
  3. EmployeeParty
  4. EmployeePartyHibernateProxy

Proxy-like runtime class. Same database id as A, so proxy-safe JPA can treat it as the same persisted row.

D

D: Institution counterparty

Available object

  1. Party
  2. InstitutionParty

Different branch under Party. It is a legal entity, not a person or employee role.

Request path

Browser workbench
Baran Kaplan Web
Next API route
Spring Boot pod or fallback
Strategy result

Role-strict employee key

A person can have multiple role-specific records that must not merge.

Needs clear domain wording because the same human can produce multiple values.

Pair matrix

A selected anchor object is compared with every checked object.

Pair==A.equals(target)target.equals(A)hashCodeVerdict
A vs Bfalsefalsefalsedifferent bucketRelated, not equalDomain relationship exists: same party business key, same employee key.
A vs Cfalsefalsefalsedifferent bucketRelated, not equalDomain relationship exists: same party business key, same employee key, same database id.

Collection matrix

This is where equals/hashCode bugs usually become visible.

Java operationResultReason
List.add(all)4 itemsList keeps every object unless you remove duplicates yourself.
HashSet.add(all)4 itemsNo object collapsed under the selected rule.
HashMap.get(new A' key)hitFresh key object A' uses the same visible fields as A.
stream().distinct()4 itemsKeeps labels: A, B, C, D.

Contract review

The lab checks the traps that code reviews and production bugs both care about.

Symmetry

A.equals(B) and B.equals(A) agree for selected pairs.

hashCode follows equals

Every equal selected pair lands in the same hash bucket.

Transient JPA id safety

No selected JPA rule is treating null ids as equal.

Mutable key warning

Business-key equality must use immutable or effectively immutable fields.

Spring and Java implementation guide

The selected rule should lead to code, tests, and naming decisions.

// selected strategy
Role-strict employee key

// rule intent
Same tenant, employee number, and role means same Java value.

// production risk
Needs clear domain wording because the same human can produce multiple values.
  • Include role or concrete role discriminator in equals/hashCode.
  • Prefer composition when roles can change independently from the person.
  • Add tests for TraderParty vs ManagerParty with the same employee number.