Careeroza
Proxies
What a Proxy Is
For a LAZY @ManyToOne or getReferenceById(id), Hibernate does not load the row. It returns a proxy: a generated subclass of your entity (made with ByteBuddy, class name like Category$HibernateProxy$Xy12) that knows only the id. The first call to a non-id getter loads the row.
OrderItem item = em.find(OrderItem.class, 10L);
Product p = item.getProduct(); // proxy, no SQL yet
p.getId(); // still no SQL: the id is known
Hibernate.isInitialized(p); // false
p.getName(); // select ... from products where id=?
Hibernate.isInitialized(p); // true
Product real = Hibernate.unproxy(p, Product.class); // the actual entity objectA Proxy in Plain Java
Example
Java's dynamic proxy shows the idea (Hibernate subclasses the entity, but the behaviour is the same): the id is free, other getters load the row once, and after the session closes loading fails:
import java.lang.reflect.*;
public class Main {
static boolean sessionOpen = true;
public static void main(String[] args) {
Product p = lazyReference(7L);
System.out.println("got reference, id=" + p.getId());
System.out.println("name=" + p.getName());
System.out.println("price=" + p.getPrice());
Product q = lazyReference(8L);
sessionOpen = false; // transaction ended
try {
q.getName();
} catch (IllegalStateException e) {
System.out.println("LazyInitializationException: " + e.getMessage());
}
}
static Product lazyReference(long id) {
InvocationHandler h = new InvocationHandler() {
Product target;
public Object invoke(Object proxy, Method m, Object[] a) throws Throwable {
if (m.getName().equals("getId")) return id;
if (target == null) {
if (!sessionOpen) throw new IllegalStateException("could not initialize proxy [Product#" + id + "] - no Session");
System.out.println(" select ... from products where id=" + id);
target = new RealProduct(id, "Prestige Cooker", 1899);
}
return m.invoke(target, a);
}
};
return (Product) Proxy.newProxyInstance(Product.class.getClassLoader(), new Class<?>[]{Product.class}, h);
}
}
interface Product { long getId(); String getName(); int getPrice(); }
record RealProduct(long id, String name, int price) implements Product {
public long getId() { return id; }
public String getName() { return name; }
public int getPrice() { return price; }
}Output
got reference, id=7 select ... from products where id=7 name=Prestige Cooker price=1899 LazyInitializationException: could not initialize proxy [Product#8] - no Session
Proxy Gotchas
p.getClass()is the proxy class, sogetClass() == Product.classinequalsfails. UseHibernate.getClass(p)orinstanceof.- Accessing fields directly (
other.name) on a proxy reads the empty proxy field, not the data. Use getters inequals. - Entity classes and their getters must not be
final, or Hibernate cannot subclass them (records cannot be entities for this reason). - A proxy of an inheritance root (
Payment) is not an instance of the subclass (UpiPayment):instanceof UpiPaymentis false until you unproxy.