package com.ankurm.hibernatedemo.persistenceannotations;
import static org.assertj.core.api.Assertions.assertThat;
import com.ankurm.hibernatedemo.HibernateDemoApplication;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* The concrete failure: an entity with id-based equals()/hashCode() is added to a HashSet
* BEFORE flush (id == null), then persisted (id gets assigned). The same reference, looked up
* in the SAME set, is no longer found -- because its hash code changed after insertion, so
* HashSet is now probing the wrong bucket.
*
*
Docs: docs/05-jpa-persistence-annotations.md, Topic 2.
*/
@SpringBootTest(classes = HibernateDemoApplication.class)
class IdBasedEqualsHashSetTrapTest {
@Autowired
private EntityManagerFactory emf;
@Test
void idBasedEquals_addedToSetBeforeFlush_containsReturnsFalseAfterIdAssigned() {
Set set = new HashSet<>();
IdBasedEqualsEntity e = new IdBasedEqualsEntity("widget");
set.add(e); // id is null at this point -- hashCode() = Objects.hash((Long) null)
assertThat(set.contains(e)).isTrue(); // trivially true right now
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(e);
em.getTransaction().commit(); // id gets assigned HERE -- hashCode() changes
em.close();
assertThat(e.getId()).isNotNull();
boolean stillFound = set.contains(e);
System.out.println("RESULT[id-based-equals-hashset-trap]: after persist(), e.getId()=" + e.getId()
+ ", set.contains(e) = " + stillFound + " (same reference, same set, only the hash code changed)");
assertThat(stillFound)
.as("HashSet.contains() on the SAME reference must return false once the id-based hashCode changed after insertion")
.isFalse();
// Bonus: iterating the set and calling equals() manually still finds it (equals() itself is fine) --
// it's specifically the bucket lookup (which trusts the now-stale cached hash) that is broken.
boolean foundByIteration = set.stream().anyMatch(x -> x.equals(e));
System.out.println("RESULT[id-based-equals-hashset-trap]: manual iteration foundByIteration = " + foundByIteration
+ " -- confirms equals() itself still works; it's HashSet's bucket indexing that is now wrong.");
assertThat(foundByIteration).isTrue();
}
}