Add Hibernate 7 batches 2-6 and batch 7: mapping styles, JPA annotations, natural IDs, @Immutable, stored procedures, in-memory test databases, JNDI mocking, proxies, associations, temporal mapping, named queries, HQL, Criteria API, EntityManager bootstrapping, Ehcache 3 L2 cache configuration, HikariCP connection pooling, and Hibernate Validator CDI integration (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:
@@ -0,0 +1,87 @@
|
||||
package com.ankurm.hibernatedemo.jndi;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NameAlreadyBoundException;
|
||||
import org.h2.jdbcx.JdbcDataSource;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Backs docs/10-mocking-jndi-datasources.md, chapter "'Name already bound' across two tests in the same JVM".
|
||||
* Simple-JNDI's {@code MemoryContextFactory}, with {@code org.osjava.sj.jndi.shared=true},
|
||||
* backs its bindings with a single JVM-wide static map (see
|
||||
* {@code JndiDataSourceResolutionTest}'s javadoc). If test A binds a name and never unbinds it,
|
||||
* test B's attempt to bind the SAME name in the SAME JVM fails with
|
||||
* {@code NameAlreadyBoundException} -- even though the two tests share no code, no fixture, no
|
||||
* Spring context. {@link MethodOrderer.OrderAnnotation} pins the order here deliberately, so the
|
||||
* pollution is forced to happen rather than left to JUnit's default (effectively random)
|
||||
* ordering.
|
||||
*/
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class CrossTestPollutionTest {
|
||||
|
||||
private static final Logger DEMO = LoggerFactory.getLogger("DEMO");
|
||||
private static final String SHARED_NAME = "jdbc/SharedAcrossTests";
|
||||
|
||||
static {
|
||||
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.osjava.sj.MemoryContextFactory");
|
||||
System.setProperty("org.osjava.sj.jndi.shared", "true");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
void testA_bindsAName_andDeliberatelyDoesNotCleanUp() throws Exception {
|
||||
JdbcDataSource ds = new JdbcDataSource();
|
||||
ds.setURL("jdbc:h2:mem:pollution-a;DB_CLOSE_DELAY=-1");
|
||||
Context ctx = new InitialContext();
|
||||
ctx.createSubcontext("jdbc");
|
||||
ctx.bind(SHARED_NAME, ds);
|
||||
DEMO.info("testA bound {} -- no @AfterEach unbind on purpose, to force the pollution", SHARED_NAME);
|
||||
// If this assertion passes, the bind succeeded.
|
||||
assertThat(new InitialContext().lookup(SHARED_NAME)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
void testB_bindingTheSameNameAgain_throwsNameAlreadyBoundException() throws Exception {
|
||||
JdbcDataSource ds2 = new JdbcDataSource();
|
||||
ds2.setURL("jdbc:h2:mem:pollution-b;DB_CLOSE_DELAY=-1");
|
||||
Context ctx = new InitialContext();
|
||||
|
||||
NameAlreadyBoundException ex = assertThrows(NameAlreadyBoundException.class,
|
||||
() -> ctx.bind(SHARED_NAME, ds2));
|
||||
DEMO.info("testB's bind() of the SAME name testA left behind failed verbatim with: {}: {}",
|
||||
ex.getClass().getName(), ex.getMessage());
|
||||
|
||||
// The fix: rebind() instead of bind() is idempotent for this exact failure mode --
|
||||
// it replaces whatever is there instead of throwing.
|
||||
ctx.rebind(SHARED_NAME, ds2);
|
||||
Object rebound = new InitialContext().lookup(SHARED_NAME);
|
||||
assertThat(rebound).isSameAs(ds2);
|
||||
DEMO.info("ctx.rebind() instead of ctx.bind() -- the standard fix -- succeeded: {}", rebound);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
void testC_theRealFix_isAfterEachUnbindNotRebind() throws Exception {
|
||||
// rebind() papers over the symptom but each test still leaks its binding into the next
|
||||
// one's JNDI namespace unless something cleans up. The actual fix is symmetry: whatever
|
||||
// a test binds, that same test unbinds, in @AfterEach, unconditionally.
|
||||
try {
|
||||
new InitialContext().unbind(SHARED_NAME);
|
||||
DEMO.info("cleanup: unbound {} so it does not leak into any test that runs after this class", SHARED_NAME);
|
||||
} catch (Exception e) {
|
||||
DEMO.info("cleanup: nothing to unbind ({})", e.toString());
|
||||
}
|
||||
assertThrows(javax.naming.NameNotFoundException.class,
|
||||
() -> new InitialContext().lookup(SHARED_NAME));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user