Hibernate Proxies – Why they’re used and how to unproxy them
Get access to all my video courses, 2 monthly Q&A calls, monthly coding challenges, a community of like-minded developers, and regular expert sessions.
Join the Persistence Hub!
Hibernate generates proxies to provide lazy loading for to-one associations, and you can use them to improve the performance of some of your write operations. You might have seen the class names of these proxies in your debugger or some log messages. They consist of the name of your entity class and a postfix that depends on your Hibernate version and the bytecode manipulation library that Hibernate uses internally.
11:17:03,697 INFO TestSample:80 - com.thorben.janssen.sample.model.ChessPlayer$HibernateProxy$W2cPgEkQ
This article will show you how to detect if an object is a proxy, a common pitfall when working with a proxy object, how to initialize its attributes, and how to unproxy it to get the underlying entity object.
How Hibernate Generates Proxies
Hibernate generates the proxy class as a subclass of your entity class. Since version 5.3, Hibernate uses Byte Buddy to generate it at runtime. In older versions, Hibernate used Javassist or CGLIB.
The generated proxy intercepts all method invocations, checks if the proxied entity object has been initialized. If necessary, Hibernate executes a database query to initialize the entity before it calls the intercepted method on the entity object. If this happens without an active Hibernate Session, this causes a LazyInitializationException.
How to Get a Proxy Object
Let’s take a quick look at the 2 most common situations in which you’re working with a proxy object before I show you how to initialize and unproxy it.
Proxied Lazy To-One Associations
By default, the FetchType of all to-one associations is eager. That means Hibernate has to fetch the associated entity object when loading an entity. You can change that by setting the fetch attribute of your @OneToOne or @ManyToOne annotation to FetchType.LAZY.
@Entity public class ChessGame { @ManyToOne(fetch = FetchType.LAZY) private ChessPlayer playerWhite; @ManyToOne(fetch = FetchType.LAZY) private ChessPlayer playerBlack; ... }
Defining lazy loading for a to-one association introduces a challenge to your persistence provider. It has to find a way to get notified when your business code wants to use the association and fetch it from the database. For all to-many associations, Hibernate solves this by initializing the attribute with its own Collection implementations. But this doesn’t work for to-one associations. Your entities aren’t required to implement any interface that Hibernate could implement. This leaves Hibernate with 2 options:
- It can add some code to the getter method or
- it can generate a proxy class that’s a subclass of your entity.
The first option requires bytecode enhancement. That’s a topic for another article, and I’ll explain it in more detail in my Hibernate Performance Tuning Online Training. In this article, we will concentrate on the generated proxy.
Get a Proxy to Initialize an Association
You can also request a proxy object of an entity by calling the getReference method on the EntityManager or Hibernate Session. This gets you a proxy object that you can use to initialize a to-one association when persisting a new or updating an existing entity.
// get a proxy ChessTournament chessTournament = em.getReference(ChessTournament.class, tournamentId); ChessGame chessGame = new ChessGame(); chessGame.setRound(2); chessGame.setTournament(chessTournament); em.persist(chessGame);
As you can see in the following log output, the call of the getReference method doesn’t trigger a database query. Hibernate instantiates a proxy object and only sets the primary key attribute. Hibernate delays the execution of a query until you call a getter or setter method of any non-primary key attribute.
11:11:53,506 DEBUG SQL:144 - select nextval ('hibernate_sequence') 11:11:53,509 DEBUG SQL:144 - insert into ChessGame (chessTournament_id, date, playerBlack_id, playerWhite_id, round, version, id) values (?, ?, ?, ?, ?, ?, ?)
How to Detect a Proxy Object
Most of the time, a LazyInitializationException will make you aware that you’ve been working with a proxy object. Hibernate throws it if you call a getter method on any non-primary key attribute of an uninitialized proxy object.
11:19:54,433 ERROR TestSample:142 - org.hibernate.LazyInitializationException: could not initialize proxy [com.thorben.janssen.sample.model.ChessPlayer#101] - no Session
If you’re wondering if an object might be a proxy, you can check if it’s an instance of HibernateProxy. That’s one of Hibernate’s marker interfaces. And if you also want to check if the proxy has been initialized, you can provide it to the static isInitialized method of the Hibernate class.
I use both checks in the following code snippet on the playerWhite attribute, which is a lazily fetched to-one association.
ChessGame chessGame = em.find(ChessGame.class, this.chessGame.getId()); assertThat(chessGame.getPlayerWhite()).isInstanceOf(HibernateProxy.class); assertFalse(Hibernate.isInitialized(chessGame.getPlayerWhite()));
How to Initialize a Proxy
The easiest and most commonly used approach to initialize a proxy object is to call a getter or setter method of a non-primary key attribute. Hibernate then checks if the proxied entity object is already initialized. If it isn’t, Hibernate executes an SQL statement that fetches the entity before calling your getter or setter method.
ChessGame chessGame = em.find(ChessGame.class, this.chessGame.getId()); log.info(chessGame.getPlayerWhite().getClass().getName()); log.info("==== Test Assertions ===="); assertThat(chessGame.getPlayerWhite().getFirstName()).isEqualTo(player1.getFirstName());
The playerWhite attribute of the ChessGame entity models a lazily fetched to-one association. As you can see in the log output, Hibernate initialized it with a generated proxy object. When I then call the getFirstName() method on that proxy, Hibernate executes an additional SQL statement to initialize the proxy.
11:49:41,984 DEBUG SQL:144 - select chessgame0_.id as id1_0_0_, chessgame0_.chessTournament_id as chesstou5_0_0_, chessgame0_.date as date2_0_0_, chessgame0_.playerBlack_id as playerbl6_0_0_, chessgame0_.playerWhite_id as playerwh7_0_0_, chessgame0_.round as round3_0_0_, chessgame0_.version as version4_0_0_ from ChessGame chessgame0_ where chessgame0_.id=? 11:49:42,006 INFO TestSample:122 - com.thorben.janssen.sample.model.ChessPlayer$HibernateProxy$dWs3SOcI 11:49:42,006 INFO TestSample:126 - ==== Test Assertions ==== 11:49:42,006 DEBUG SQL:144 - select chessplaye0_.id as id1_1_0_, chessplaye0_.birthDate as birthdat2_1_0_, chessplaye0_.firstName as firstnam3_1_0_, chessplaye0_.lastName as lastname4_1_0_, chessplaye0_.version as version5_1_0_, gameswhite1_.playerWhite_id as playerwh7_0_1_, gameswhite1_.id as id1_0_1_, gameswhite1_.id as id1_0_2_, gameswhite1_.chessTournament_id as chesstou5_0_2_, gameswhite1_.date as date2_0_2_, gameswhite1_.playerBlack_id as playerbl6_0_2_, gameswhite1_.playerWhite_id as playerwh7_0_2_, gameswhite1_.round as round3_0_2_, gameswhite1_.version as version4_0_2_ from ChessPlayer chessplaye0_ left outer join ChessGame gameswhite1_ on chessplaye0_.id=gameswhite1_.playerWhite_id where chessplaye0_.id=?
Instead of calling a getter method, you can also call the static initialize method on the Hibernate class. But if you already know that you will use a lazily fetched association in your business code, I recommend initializing it in the same query that fetched your entity. I explained 5 options to initialize lazily fetched associations in a previous article.
Hibernate.initialize(chessGame.getPlayerWhite());
How to Unproxy your Proxy Object to get the Entity Object
Until Hibernate version 5.2.10, getting the real entity object from your proxy required a little bit of code. First, you had to cast your object to HibernateProxy to get access to its LazyInitializer, which you then used to get the entity object.
ChessPlayer playerWhite = chessGame.getPlayerWhite(); ChessPlayer unproxiedPlayer; if(playerWhite instanceof HibernateProxy) { HibernateProxy hibernateProxy = (HibernateProxy) playerWhite; LazyInitializer initializer = hibernateProxy.getHibernateLazyInitializer(); unproxiedPlayer = (ChessPlayer) initializer.getImplementation(); }
Since version 5.2.10, the static unproxy method of the Hibernate class provides the same functionality, which makes our job a lot easier.
ChessPlayer unproxiedPlayer = Hibernate.unproxy(playerWhite, ChessPlayer.class);
A Common Pitfall When Working With Proxies
As I explained earlier, Hibernate generates a proxy that’s a subclass of your entity class. This can become an issue if your to-one association references the superclass of an inheritance hierarchy. In that case, Hibernate generates another subclass of that superclass, and you can’t easily cast it to your subclass.
Let’s take a look at an example. The ChessGame entity defines a lazily fetched to-one association to a ChessTournament entity. And the ChessSwissTournament entity is a subclass of the ChessTournament entity.
When I load a ChessGame entity that’s associated with a ChessSwissTournament, Hibernate initializes the tournament attribute with a proxy object that’s a subclass of the ChessTournament entity and implements HibernateProxy. But it’s not an instance of ChessSwissTournament.
ChessGame chessGame = em.find(ChessGame.class, newChessGame.getId()); assertThat(chessGame.getTournament()).isInstanceOf(ChessTournament.class); assertThat(chessGame.getTournament()).isNotInstanceOf(ChessSwissTournament.class); assertThat(chessGame.getTournament()).isInstanceOf(HibernateProxy.class);
You need to unproxy the tournament to get an object of type ChessSwissTournament.
ChessGame chessGame = em.find(ChessGame.class, newChessGame.getId()); ChessTournament unproxiedEntity = Hibernate.unproxy(chessGame.getTournament(), ChessTournament.class); assertThat(unproxiedEntity).isInstanceOf(ChessTournament.class); assertThat(unproxiedEntity).isInstanceOf(ChessSwissTournament.class); assertThat(unproxiedEntity).isNotInstanceOf(HibernateProxy.class);
Summary
Hibernate uses generated proxy classes to support lazy loading of to-one associations, and you can use it to initialize associations to other entities. As soon as you call a getter or setter method of a non-primary key attribute, Hibernate executes an SQL statement to fetch the entity object.
The proxy class is a subclass of your entity class and implements the HibernateProxy interface. This enables you to use the proxy object in almost the same way as using the entity object. The only 2 limitations are:
- If you want to initialize the proxy object, you need to do that with an active Hibernate Session. Otherwise, Hibernate throws a LazyInitializationException.
- If you modeled a lazy to-one association to the superclass of an inheritance hierarchy, you can’t cast the proxy object to any of your subclasses. You need to unproxy the object first.