Add Hibernate 7 batches 2-6: mapping styles, JPA annotations, natural IDs, @Immutable, stored procedures, in-memory test databases, JNDI mocking, proxies, associations, temporal mapping, named queries, HQL, Criteria API and EntityManager bootstrapping (Hibernate 7.4.5.Final + Spring Boot 4.1.1 + JDK 25)
Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EQNA6DJ9VgCtW6zhCE8Xud
This commit is contained in:
Executable
+59
@@ -0,0 +1,59 @@
|
||||
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.
|
||||
*
|
||||
* <p>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<IdBasedEqualsEntity> 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user