Files
hibernate-demo/src/main/java/com/ankurm/hibernatedemo/scenario/GetVsLoadRunner.java
T

155 lines
6.5 KiB
Java
Executable File

package com.ankurm.hibernatedemo.scenario;
import com.ankurm.hibernatedemo.model.Book;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import org.hibernate.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
/**
* Backs ankurm.com post 4859 ("Hibernate 7: get() vs load()") and
* docs/01-get-vs-load.md. Captured verbatim into docs/output/get-vs-load.txt by
* {@code scripts/run.sh getvsload}.
*
* <p>Each step opens its own {@link EntityManager} deliberately, so the SQL log lines that
* bracket a step are unambiguously that step's own traffic &mdash; there is no shared session
* whose first-level cache could quietly answer a later {@code get()} for free.
*/
@Component
@Profile("getvsload")
public class GetVsLoadRunner implements CommandLineRunner {
private static final Logger DEMO = LoggerFactory.getLogger("DEMO");
private final EntityManagerFactory emf;
public GetVsLoadRunner(EntityManagerFactory emf) {
this.emf = emf;
}
@Override
public void run(String... args) {
Long existingId = seedOneBook();
long missingId = existingId + 999_000L;
step1_getExisting(existingId);
step2_getMissing(missingId);
step3_getReferenceExisting_noSelectUntilAccessed(existingId);
step4_getReferenceMissing_exceptionOnlyOnAccess(missingId);
step5_getReferenceThenSessionClosed_lazyInitException(existingId);
step6_proxyVsRealIdentity(existingId);
}
private Long seedOneBook() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Book book = new Book("Effective Java", "Joshua Bloch");
em.persist(book);
em.getTransaction().commit();
Long id = book.getId();
em.close();
DEMO.info("SEED: inserted Book id={}", id);
return id;
}
private void step1_getExisting(Long id) {
DEMO.info("--- Step 1: session.get() on an existing id ---");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Session session = em.unwrap(Session.class);
DEMO.info("about to call session.get(Book.class, {})", id);
Book book = session.get(Book.class, id);
DEMO.info("get() returned: {}", book);
em.getTransaction().commit();
em.close();
}
private void step2_getMissing(long missingId) {
DEMO.info("--- Step 2: session.get() on a missing id ---");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Session session = em.unwrap(Session.class);
DEMO.info("about to call session.get(Book.class, {})", missingId);
Book book = session.get(Book.class, missingId);
DEMO.info("get() returned: {} (no exception thrown)", book);
em.getTransaction().commit();
em.close();
}
private void step3_getReferenceExisting_noSelectUntilAccessed(Long id) {
DEMO.info("--- Step 3: session.getReference() on an existing id ---");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Session session = em.unwrap(Session.class);
Book proxy = session.getReference(Book.class, id);
DEMO.info("getReference() returned proxy of class {} -- no SELECT above this line", proxy.getClass().getName());
DEMO.info("now calling proxy.getTitle() ...");
String title = proxy.getTitle();
DEMO.info("getTitle() returned '{}' -- the SELECT for this ran just above this line", title);
em.getTransaction().commit();
em.close();
}
private void step4_getReferenceMissing_exceptionOnlyOnAccess(long missingId) {
DEMO.info("--- Step 4: session.getReference() on a missing id ---");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Session session = em.unwrap(Session.class);
Book proxy = session.getReference(Book.class, missingId);
DEMO.info("getReference() returned a proxy for a row that does not exist -- no exception yet: {}", proxy.getClass().getName());
try {
proxy.getTitle();
DEMO.info("no exception -- this line should be unreachable");
} catch (RuntimeException e) {
DEMO.info("accessing the proxy threw {}: {}", e.getClass().getName(), e.getMessage());
}
em.getTransaction().rollback();
em.close();
}
private void step5_getReferenceThenSessionClosed_lazyInitException(Long id) {
DEMO.info("--- Step 5: proxy accessed after its session is closed ---");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Session session = em.unwrap(Session.class);
Book proxy = session.getReference(Book.class, id);
em.getTransaction().commit();
em.close();
DEMO.info("session closed. proxy in hand: {}", proxy.getClass().getName());
try {
proxy.getTitle();
DEMO.info("no exception -- this line should be unreachable");
} catch (RuntimeException e) {
DEMO.info("accessing the proxy after close threw {}: {}", e.getClass().getName(), e.getMessage());
}
}
private void step6_proxyVsRealIdentity(Long id) {
DEMO.info("--- Step 6: proxy identity vs a real loaded instance ---");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Session session = em.unwrap(Session.class);
Book real = session.get(Book.class, id);
// second em/session so this is a genuinely separate proxy, not the same cached instance
EntityManager em2 = emf.createEntityManager();
em2.getTransaction().begin();
Session session2 = em2.unwrap(Session.class);
Book proxy = session2.getReference(Book.class, id);
DEMO.info("real.getClass() = {}", real.getClass().getName());
DEMO.info("proxy.getClass() = {}", proxy.getClass().getName());
DEMO.info("proxy instanceof Book.class: {}", Book.class.isInstance(proxy));
DEMO.info("real.getClass() == proxy.getClass(): {}", real.getClass() == proxy.getClass());
DEMO.info("real.equals(proxy) before proxy access: {}", real.equals(proxy));
em.getTransaction().commit();
em.close();
em2.getTransaction().commit();
em2.close();
}
}