155 lines
6.5 KiB
Java
Executable File
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 — 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();
|
|
}
|
|
}
|