DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • How Spring and Hibernate Simplify Web and Database Management
  • Enhanced Query Caching Mechanism in Hibernate 6.3.0
  • Multi-Tenancy and Its Improved Support in Hibernate 6.3.0
  • Implement Hibernate Second-Level Cache With NCache

Trending

  • Docker Model Runner: Streamlining AI Deployment for Developers
  • Intro to RAG: Foundations of Retrieval Augmented Generation, Part 1
  • It’s Not About Control — It’s About Collaboration Between Architecture and Security
  • How the Go Runtime Preempts Goroutines for Efficient Concurrency
  1. DZone
  2. Coding
  3. Java
  4. CGLib Proxies and Hibernate Lazy Fetching

CGLib Proxies and Hibernate Lazy Fetching

Lazy fetching with Hibernate is a common source of exceptions (and confusion). This article delves into CGLib proxies to reveal why lazy loading works the way it does.

By 
Alan Hohn user avatar
Alan Hohn
·
Dec. 31, 15 · Tutorial
Likes (6)
Comment
Save
Tweet
Share
13.2K Views

Join the DZone community and get the full member experience.

Join For Free

In a previous article I discussed creating dynamic classes using the functionality built into the standard Java library. However, it suffers from an important limitation, as it can only create dynamic classes that proxy interfaces. In order to provide services such as container-managed transactions (as done by the Spring Framework) or transparent lazy fetching of data (as done by Hibernate) it is necessary to create dynamic classes that appear to be an instance of a concrete class. In this article I will show how these frameworks use CGLib to create these dynamic classes.

Proxying for a concrete class is more challenging, because while there can be many classes that implement an interface, there is only one version of a concrete class per class loader. In order to work around this, CGLib creates a dynamic child class of the class being proxied. Of course, this only works if the class is not final.

In the previous article I gave an example of needing to perform audit logging whenever a service is called. As before, we'll assume the existence of some auditing logic that we want to use everywhere:

public class Auditor {

  public void audit(String service, String extraData) {
    // ... Do the auditing
  }

}

In the previous example I created a calculator service that implements an interface. But what if the service is just a plain concrete class?

public class Calculator {
  public int add(int left, int right) {
    return left + right;
  }
}

With CGLib this is almost as easy as the JDK proxy example. We start, as before, by creating a class that handles any method call and performs the auditing behavior we want, before delegating to the original method call:

public class AuditingInterceptor implements MethodInterceptor {

  private Auditor auditor;
  private String service;

  public AuditingInterceptor(Auditor auditor, String service) {
    this.auditor = auditor;
    this.service = service;
  }

  public Object intercept(Object target, Method method, 
    Object[] args, MethodProxy proxy) throws Throwable {

    auditor.audit(service, "before " + method.getName());
    targetReturn = proxy.invokeSuper(target, args);
    auditor.audit(service, "after " + method.getName());

    return targetReturn;
  }
}

The style of this code is very similar to our previous example, except that the method name to invoke the "real" method is invokeSuper()because we are creating a dynamic proxy that will pretend to be a child class of the class we are proxying.

With this class in place we can create our proxied calculator (our "enhanced" class in CGLib terms):

Auditor auditor = ...;
AuditingInterceptor interceptor = new AuditingInterceptor(auditor, "calculator");
Enhancer e = new Enhancer();
e.setSuperclass(Calculator.class);
e.setCallback(interceptor);
Calculator calc = (Calculator)e.create();
calc.add(2, 2); // Will be audited

Unlike the JDK proxy, there is no need to create an instance of the "real" class, because we are creating an instance of its subclass. Of course, this also means that when we call e.create() the constructor of the proxied class will be called. If the class we are proxying has a no-arg constructor, it will be used; otherwise, we need to pass constructor arguments when we call e.create() so the Enhancer can properly set things up.

Earlier, I mentioned Hibernate's use of CGLib to create a proxy for lazy initialization. Hopefully, now that we've looked under the covers of CGLib, this behavior makes more sense. Consider a simple example where two Hibernate entities are dependent on one another (I will use the JPA annotations):

@Entity
@Table(name = "A")
public class A {
  private B b;
  // ... More fields

  @ManyToOne(fetch = FetchType.LAZY)
  public B getB() {
      return b;
  }

  public void setB(B b) {
    this.b = b;
  }
}

@Entity
@Table(name= "B")
public class B {
  // ... Some fields
}

For this simple example, eager fetching might be fine from a performance standpoint. But if B has many fields or itself requires a number of joins to fetch, we may not want it fetched when we fetch A.

When we annotate the field as LAZY, Hibernate creates a proxy using CGLib for B and populates A with the proxy. This proxy has a reference back to the Hibernate session. Whenever a method is called on the proxy, it checks to see if the proxy has been initialized. If it has not, it uses the Hibernate session to create a new query to the database and populates the object. After this, it just delegates all method calls to the populated object. The result is that users of A are able to ignore B, in which case it won't be fetched, or use B as normal, in which case its data will be invisibly fetched (assuming low latency and an available database). This is much easier than having to manually load B when it is needed.

This behavior from Hibernate provides important functionality that makes Object Relational Mapping (ORM) more performant, but it also introduces some issues. First, object serialization doesn't work as expected when Hibernate proxies are in the way. Second, if the Hibernate session is closed before the proxy gets a chance to initialize, it cannot fetch data from the database, and the dreaded LazyInitializationException ensues, a common issue people have when using Hibernate. 

One final note about CGLib proxies. This example shares an issue that was discussed in the previous article. Nothing prevents anyone from creating instances of the Calculator class directly, in which case they won't get any of the enhanced behavior. Since we often use interceptors for important things like validation and authorization, it is important to ensure that unenhanced instances of objects are not being created or exposed. Fortunately, for our Hibernate example, it is responsible for instantiating objects fetched from the database, so it can ensure that the proxies are in place as needed.

This is a very simple example of CGLib. A slightly more advanced example will allow us to create "observable" objects that automatically call listeners when their properties change. In the next article I will cover that example.

Hibernate

Opinions expressed by DZone contributors are their own.

Related

  • How Spring and Hibernate Simplify Web and Database Management
  • Enhanced Query Caching Mechanism in Hibernate 6.3.0
  • Multi-Tenancy and Its Improved Support in Hibernate 6.3.0
  • Implement Hibernate Second-Level Cache With NCache

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!