Hibernate: using lazy initialization in developing a client-server application
When designing the domains of an application developed using Hibernate, the developer needs to make a choice: whether to initialize the domain properties corresponding to the collections of related domains immediately (FetchType = EAGER) or do this only when accessing them (FetchType = LAZY). In fact, in the case when the subject area has any kind of complex structure of relations between objects, the choice has already been made - to load half the database for one object, as it would be with FetchType = EAGER, to put it mildly, unreasonable. Therefore, lazy initialization in the case of collections is the most preferred initialization strategy for related objects.
However, not all so simple. Lazy initialization is achieved by creating a proxy object using the JDK Dynamic Proxy or the CGLIB library. In both cases, proxying the appropriate get methods comes down to accessing the Hibernate session to obtain the necessary data. The latter, in turn, means that access to the lazy properties of an object can only be done if there is a Hibernate session. Otherwise, an attempt to obtain an object property will result in an unforgettable exception "LazyInitializationException: could not initialize proxy - the owning Session was closed".
It is clear that having an open session at hand is far from always possible, which causes some inconvenience. So, for example, in order to use domains with lazy initialization in MVC application templates, you will have to resort to the “ OpenSessionInView ” method , the essence of which is to create a filter that ensures that the session opens at the beginning of the processing of the request and closes it at the end.
But what if the domains with the uploaded data need to be used outside the Hibernate session? For example, in the case of client-server architecture, when does the server need to transfer domains to the client? Of course, that there can be no talk of opening a session on the client side, if only because in the general case he does not know anything about the database. The only way out of the situation, in my opinion, is to “de-proxy” domain objects and initialize them with the necessary data before transferring them from the server to the client.
Imagine that the server part of the application consists of 3 layers:
The domain classes themselves are available to both the server and the client (which, in general, is logical).
In this scenario, the business logic layer can easily work with domain proxies within a Hibernate session. The role of the service layer in this case is reduced to obtaining the necessary data from the business logic layer and data composition for the client: creating DTO objects based on domain classes by copying a certain depth and detail.
The only question remains is how to carry out this “deproxying”. In principle, this can be done using Hibernate itself :
Such an approach initializes all collections of a given object and “pulls” it out of the proxy. However, it has a number of drawbacks: all collections in a row are initialized and only the parent object is deproxed (at least because it is unclear how much you need to go down the object's connection graph when deproxing).
The solution in this situation can be the creation of a small utility class that will perform domain deproxying by creating a new object of the same class and initializing properties corresponding to the Hibernate base classes . All other properties of the object will be initialized by the services layer as necessary.
I do not presume to say that this approach is optimal and / or the only right one. If you have any suggestions on how to solve the problem otherwise, I will be glad to hear them.
However, not all so simple. Lazy initialization is achieved by creating a proxy object using the JDK Dynamic Proxy or the CGLIB library. In both cases, proxying the appropriate get methods comes down to accessing the Hibernate session to obtain the necessary data. The latter, in turn, means that access to the lazy properties of an object can only be done if there is a Hibernate session. Otherwise, an attempt to obtain an object property will result in an unforgettable exception "LazyInitializationException: could not initialize proxy - the owning Session was closed".
It is clear that having an open session at hand is far from always possible, which causes some inconvenience. So, for example, in order to use domains with lazy initialization in MVC application templates, you will have to resort to the “ OpenSessionInView ” method , the essence of which is to create a filter that ensures that the session opens at the beginning of the processing of the request and closes it at the end.
But what if the domains with the uploaded data need to be used outside the Hibernate session? For example, in the case of client-server architecture, when does the server need to transfer domains to the client? Of course, that there can be no talk of opening a session on the client side, if only because in the general case he does not know anything about the database. The only way out of the situation, in my opinion, is to “de-proxy” domain objects and initialize them with the necessary data before transferring them from the server to the client.
Imagine that the server part of the application consists of 3 layers:
- service layer (objects that the client works with);
- a layer of business logic (objects that implement the business logic of the application);
- persistence layer (Hibernate, domains).
The domain classes themselves are available to both the server and the client (which, in general, is logical).
In this scenario, the business logic layer can easily work with domain proxies within a Hibernate session. The role of the service layer in this case is reduced to obtaining the necessary data from the business logic layer and data composition for the client: creating DTO objects based on domain classes by copying a certain depth and detail.
The only question remains is how to carry out this “deproxying”. In principle, this can be done using Hibernate itself :
public static T initializeAndUnproxy(T entity) {
if (entity == null) {
throw new
NullPointerException("Entity passed for initialization is null");
}
Hibernate.initialize(entity);
if (entity instanceof HibernateProxy) {
entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer()
.getImplementation();
}
return entity;
}
* This source code was highlighted with Source Code Highlighter. Such an approach initializes all collections of a given object and “pulls” it out of the proxy. However, it has a number of drawbacks: all collections in a row are initialized and only the parent object is deproxed (at least because it is unclear how much you need to go down the object's connection graph when deproxing).
The solution in this situation can be the creation of a small utility class that will perform domain deproxying by creating a new object of the same class and initializing properties corresponding to the Hibernate base classes . All other properties of the object will be initialized by the services layer as necessary.
I do not presume to say that this approach is optimal and / or the only right one. If you have any suggestions on how to solve the problem otherwise, I will be glad to hear them.