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.
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.
Parent-side view of the same person.
Main employee role record. This is the anchor.
Same person and employee, different role record.
Different person, same trader role and desk.
Different Java object, same employment key, changed snapshot field.
Same root class family, different business branch.
| Comparison | == | Same party? | Same employee? | Same role? | Same employment? | Meaning |
|---|---|---|---|---|---|---|
| P1 vs E1A | false | true | not applicable | not applicable | not applicable | Parent view and child role record can describe the same person. |
| E1A vs E1B | false | true | true | false | depends | Same human and employee number; role-specific workflows may keep them separate. |
| E1A vs E2A | false | false | false | true | false | Same role/desk is a relation or filter, not object equality. |
| E1A vs E1A2 | false | true | true | true | true | Two new Java objects can represent the same employment record. |
| E1A vs ORG1 | false | false | false | false | false | They share the Party root only; equality should stay false. |
| Rule | Meaning | P1-E1A | E1A-E1B | E1A-E2A | E1A-E1A2 | E1A-ORG1 | equals fit |
|---|---|---|---|---|---|---|---|
| Reference same / == | Same object instance in memory | false | false | false | false | false | Java default |
| Default Object.equals | Same reference unless overridden | false | false | false | false | false | Override yoksa bu |
| Only partyNo | Same person / party | true | true | false | true | false | Parent identity icin olabilir |
| employeeNo only | Same employee number | false | true | false | true | false | Broad employee equality |
| employeeNo + role | Same role-specific employee record | false | false | false | true | false | Role-strict equality |
| role / desk only | Same responsibility or desk | false | false | true | true | false | Domain method, not equals |
| all fields snapshot | Same immutable value snapshot | false | false | false | false | false | Value object ise |
| DB id | Same persisted row | depends | false | false | true | false | JPA lifecycle dikkat |
| partyNo OR role | Same person or same role | true | true | true | true | false | Danger |
| exact class + key | Runtime class and selected key must match | false | false | false | true | false | Proxy riski var |
| composition key | Dedicated identity value object decides | design | design | design | design | design | Usually 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.
What should happen?
This is the part that was missing: Java has a default answer, but the domain needs an explicit rule.
| Pair | Java default | Domain question | Expected decision |
|---|---|---|---|
| A vs B | false | Same person and employee number, but Trader role vs Manager role. | Usually related, not equal, when role records must stay separate. |
| A vs C | false | A 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 D | false | A 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.
| Rule | A-B | A-C | A-D | When useful | Risk |
|---|---|---|---|---|---|
| Java reference | different | different | different | Only same object instance should be equal. | Business duplicates never merge. |
| Party business key | equal | equal | different | Every subtype is just another view of the same party. | Trader and Manager roles can collapse too early. |
| Broad employee key | equal | equal | different | Same employee number means one employee value. | Role-specific approval records can be lost. |
| Role-strict key | different | Same human can have separate role records. | People may expect same employee to mean equal. | ||
| JPA id, strict class | different | different | different | Concrete entity class and non-null id must match. | Hibernate proxy class can produce false negatives. |
| Proxy-safe JPA id | different | equal | different | Hibernate proxy and real entity can represent the same row. | Null ids still need special handling. |
| Institution legal key | not applicable | not applicable | different | Legal 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.
The left-side object. Every selected target is compared against this object.
These are the right-side objects. More than one target can be selected.
The selected equals/hashCode rule. Switching it recalculates the matrices.
Run API sends the same payload through the Next route to the Spring Boot pod when configured.
Objects
A: Trader role for employee E-42
TraderParty
Same employee/person line as B and C. The open question is whether role records should be equal or only related.
B: Manager role for employee E-42
ManagerParty
Same employee number and party number as A, but a different role leaf under EmployeeParty.
C: Hibernate proxy for employee row
EmployeePartyHibernateProxy
Proxy-like runtime class. Same database id as A, so proxy-safe JPA can treat it as the same persisted row.
D: Institution counterparty
InstitutionParty
Different branch under Party. It is a legal entity, not a person or employee role.
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: Trader role for employee E-42
Anchor object
- Party
- IndividualParty
- EmployeeParty
- TraderParty
Same employee/person line as B and C. The open question is whether role records should be equal or only related.
B: Manager role for employee E-42
Compared object
- Party
- IndividualParty
- EmployeeParty
- ManagerParty
Same employee number and party number as A, but a different role leaf under EmployeeParty.
C: Hibernate proxy for employee row
Compared object
- Party
- IndividualParty
- EmployeeParty
- EmployeePartyHibernateProxy
Proxy-like runtime class. Same database id as A, so proxy-safe JPA can treat it as the same persisted row.
D: Institution counterparty
Available object
- Party
- InstitutionParty
Different branch under Party. It is a legal entity, not a person or employee role.
Request path
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) | hashCode | Verdict |
|---|---|---|---|---|---|
| A vs B | false | false | false | different bucket | Domain relationship exists: same party business key, same employee key. |
| A vs C | false | false | false | different bucket | Domain relationship exists: same party business key, same employee key, same database id. |
Collection matrix
This is where equals/hashCode bugs usually become visible.
| Java operation | Result | Reason |
|---|---|---|
| List.add(all) | 4 items | List keeps every object unless you remove duplicates yourself. |
| HashSet.add(all) | 4 items | No object collapsed under the selected rule. |
| HashMap.get(new A' key) | hit | Fresh key object A' uses the same visible fields as A. |
| stream().distinct() | 4 items | Keeps 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.