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
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

Low-Code Development: Leverage low and no code to streamline your workflow so that you can focus on higher priorities.

DZone Security Research: Tell us your top security strategies in 2024, influence our research, and enter for a chance to win $!

Launch your software development career: Dive head first into the SDLC and learn how to build high-quality software and teams.

Open Source Migration Practices and Patterns: Explore key traits of migrating open-source software and its impact on software development.

Related

  • Hibernate Validator vs Regex vs Manual Validation: Which One Is Faster?
  • How To Integrate NCache With JPA Hibernate for Caching in Spring Boot Applications
  • Querydsl vs. JPA Criteria, Part 6: Upgrade Guide To Spring Boot 3.2 for Spring Data JPA and Querydsl Project
  • Postgres Full-Text Search With Hibernate 6

Trending

  • Efficient Data Management With Offset and Cursor-Based Pagination in Modern Applications
  • Transforming Software Development With Low-Code and No-Code Integration
  • From Backlog Manager to Product Manager [Video]
  • OpenID Connect Flows: From Implicit to Authorization Code With PKCE and BFF
  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
Like (6)
Save
Tweet
Share
12.0K 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

  • Hibernate Validator vs Regex vs Manual Validation: Which One Is Faster?
  • How To Integrate NCache With JPA Hibernate for Caching in Spring Boot Applications
  • Querydsl vs. JPA Criteria, Part 6: Upgrade Guide To Spring Boot 3.2 for Spring Data JPA and Querydsl Project
  • Postgres Full-Text Search With Hibernate 6

Partner Resources


Comments

ABOUT US

  • About DZone
  • Send 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: