Remote Lazy Loading in Hibernate

[复制链接]
查看11 | 回复9 | 2007-1-24 12:56:49 | 显示全部楼层 |阅读模式
Lazy loading in Hibernate means fetching and loading the data, only when it is needed, from a persistent storage like a database. Lazy loading improves the performance of data fetching and significantly reduces the memory footprint.
When Hibernate initializes the data object, actually it creates a reference (of the data) to the data object and doesn't load the data as such. Hibernate then intercepts the method calls to this reference and loads the actual data. In order to intercept and load the data, Hibernate requires the data object be associated with a Hibernate Session.
Problems might arise when these "lazy loaded" data objects (containing the reference) are transferred to other application layers, especially to remote client. These objects get serialized/de-serialized on their way to the remote client and there by detaching itself from the Hibernate Session. Accessing this detached reference will always lead to some exception.
What if these lazy loaded objects can still maintain their references even at the remote client layer (where there is no Hibernate Session) and still be able to lazy load data? This is quite possible and this concept of lazy loading data even from a remote client is called remote lazy loading.
In this article we'll discuss the solution by extending Hibernate's lazy loading framework. We'll use 3.2.x version of Hibernate library.
回复

使用道具 举报

千问 | 2007-1-24 12:56:49 | 显示全部楼层
Types of Lazy Loaders
In Hibernate, there are two main components which can be lazy loaded. They are namely Entities and Collections. An Entity represents a relational data in database, whereas a collection represents collection of children for an entity.
In this article, we'll go through the solutions for lazy loading Entities and Collections separately.
Lazy Loading Entities
Hibernate's lazy loading framework contains two main components namely LazyInitializer and Tuplizer. These components are necessary for creating the reference and initializing the data for this reference. In Hibernate terminology, these reference objects are called Proxy Objects.
LazyInitializer and Tuplizer
LazyInitializer is a Hibernate component that helps to generate proxy objects for Entities and can also be used to fetch the underlying entities of these proxy objects. Hibernate provides a couple of abstract implementations for LazyInitializer such as AbstractLazyInitializer and BasicLazyInitializer, to lazy load or fetch data from inside the Hibernate Session. We'll extend the BasicLazyInitializer and create our own initializer component to fetch data from remote client. Before extending it, let's look at some important methods for lazy loading, in the abstract implementation of LazyInitializer component.
* getImplementation() - this method fetches the entity and returns a persistent object (data object in our case) for the fetched entity.
* getSerializableObject() - this method returns a serializable proxy replacement object for your entity. (We'll discuss more on proxy replacement object in coming sections.)
Tuplizers in Hibernate helps to define contract for certain Hibernate based components. The contract tells how to create such components and play around with the component's property values. These components can either be an Entity or a Mapped component (eg: Composite Identifiers). Hibernate have implementations for these components and are called as EntityTuplizer and ComponentTuplizer respectively. These tuplizers demands you to create Proxy Factory for generating proxy objects of Entities.
As we are using Entity in our solution approach we'll use Entity based tuplizers. Now we need to define own EntityTuplizer and also need to provide a representation called as Entity-Mode for it. Entity modes are actually a representation of the domain model in memory, it can be a POJO, DOM4J or Map based representation. As our data objects are going to POJOs, we'll use POJO based representation.
Hibernate already supports such representation through a class called POJOEntityTuplizer. We'll extend this tuplizer and build a proxy factory which will be used to create proxy object representing our Entity. This proxy object will be of type HibernateProxy as Hibernate would expect.
HibernateProxy is a serializable marker interface that expects you to return a LazyInitializer (as talked above) and a serializable Proxy Replacement object during serialization. This Proxy Replacement object is the one which will be shared with remote client as a lazy entity reference.
Overall our aim is to create a Custom LazyInitializer, a Custom Tuplizer, a Custom Proxy Factory, a Custom Hibernate Proxy object and finally a Custom Proxy Replacement object.
回复

使用道具 举报

千问 | 2007-1-24 12:56:49 | 显示全部楼层
Approach
This flow diagram shows how Lazy initialization component is being extended for lazy loading from a client layer. We'll go through the implementation approach on each component.
回复

使用道具 举报

千问 | 2007-1-24 12:56:49 | 显示全部楼层
Flow diagram for remotely lazy loading an Entity
When Hibernate initializes the application configuration for the first time, it creates a CustomTuplizer and then builds a LazyProxyFactory for our Entity. In-order to build a proxy factory we need to create a CustomTuplizer class of type POJOEntityTuplizer and override the buildProxyFactory() method to create and return our own ProxyFactory implementation.
This LazyProxyFactory will implement Hibernate's ProxyFactory interface. The creation of the LazyProxyFactory via CustomTuplizer is shown below.
@Override
protected ProxyFactory buildProxyFactory(
PersistentClass persistentClass,
Getter idGetter,
Setter idSetter) {

//Add HibernateProxy as CustomProxyInterface
HashSet proxyInterfaces = new HashSet();
proxyInterfaces.add(HibernateProxy.class);

//Add any other interfaces

...

ProxyFactory cpf = new LazyProxyFactory();

//initialize the ProxyFactory
cpf.postInstantiate(

getEntityName(),

persistentClass.getMappedClass(),

proxyInterfaces,

idGetter.getMethod(),

idSetter.getMethod(),
null /*set this if the entity has a composite type identifier */);

return cpf;
}
}
回复

使用道具 举报

千问 | 2007-1-24 12:56:49 | 显示全部楼层
Building a LazyProxyFactory via CustomTuplizer
The getProxy() method of LazyProxyFactory is dynamically called by Hibernate to get the proxy representation of our Entity. Now we need to create a Proxy object to represent our Entity. The createProxyClass() method creates a Javassist based Class Object with HibernateProxy as its interface and its super type as Entity's persistent class. Later this Javassist based Class Object's instance will be returned as a Proxy object.
Next step is to create an interceptor to intercept the calls to this Proxy Object. In-order to do this, we'll add a call back handler that is capable of returning a Proxy replacement object that will be shared with our remote client. As we know our CustomLazyInitializer is capable of returning a replacement object (via its super implementation), we'll add this class as our callback handler.
//Create Proxy Factory using javassist
private void createProxyClass(persistentClass, interfaces) {

javassist.util.proxy.ProxyFactory factory = new
javassist.util.proxy.ProxyFactory();

factory.setSuperclass(persistentClass);

factory.setInterfaces( interfaces ); //comes from buildProxyFactory

// set method filters on this factory

...

this.customProxy = factory.createClass();
}
public HibernateProxy getProxy(Serializable id, SessionImplementor session)
throws HibernateException {

//create customProxyInstance = this.customProxy.newInstance()
//and set the callback/handler to our CustomLazyInitializer
((ProxyObject) customProxyInstance).
setHandler(new CustomLazyInitializer(...));
//return customProxyInstance
...
}
回复

使用道具 举报

千问 | 2007-1-24 12:56:49 | 显示全部楼层
A LazyProxyFactory creating and returning a CustomProxy object
The setHandler() method accepts only a MethodHandler interface. So we'll add MethodHandler Interface to our CustomLazyInitializer and implement invoke() method in CustomLazyInitializer, which is the actual callback method.
Whenever CustomProxy object's methods are invoked, the method calls are intercepted and the invocation is passed to this invoke() method of the callback handler i.e. CustomLazyInitializer.
//callback method
public Object invoke(final Object proxy,

final Method method,

final Method nextMethod,

final Object[] args) throws Throwable {

Object result = super().invoke(method, args, proxy);

//if the result is to get proxy replacement object...

if (result == INVOKE_IMPLEMENTATION) {

Object target = super().getImplementation();

Object returnValue;

try {

returnValue = method.invoke(target, args);

} catch (InvocationTargetException e) {

throw e.getTargetException();

}

return returnValue == target ? proxy : returnValue;

}

...
}
回复

使用道具 举报

千问 | 2007-1-24 12:56:49 | 显示全部楼层
CallBack method on CustomLazyInitializer
In the above code the getImplementation() method of BasicInitializer calls the abstract getSerializableProxy() method internally. So, we'll implement getSerializableProxy() method to return our Proxy Replacement Object as shown below. Our Proxy Replacement Object is called as LazyEntityRef here.
protected Object serializableProxy() {

//if initialized already, return from memory

...

//if not ...

LazyEntityRef lazyEntityRef = new LazyEntityRef();

lazyEntityRef.setClassName(getPersistentClass().getName());

lazyEntityRef.setId(getIdentifier());
...

return lazyEntityRef;
}
回复

使用道具 举报

千问 | 2007-1-24 12:56:49 | 显示全部楼层
Implemented method to return Proxy Replacement Object.
The advantage of using Proxy Replacement Object (LazyEntityRef) is that the same class can be re-used for other entities in an application.
Our first part of the implementation is complete. The LazyEntityRef for our Entity is created now and is ready to be shared with the remote client. Now again the remote client has to intercept any calls to this LazyEntityRef object and lazy load the data. One way of intercepting the method calls to LazyEntityRef is to enhance this class again using javassist library and set a handler, whose callback method will lazy load our entity. Let's do this.
Since we need to return an Enhanced Proxy class to the client, we have to do this before the raw proxy object (LazyEntityRef) reaches the client. The best way to do this is to override the de-serialization process and return the enhanced class. Overriding a de-serialization process can be done via readResolve() method of an Serializable Object.
Object readResolve() throws ObjectStreamException {

//create and return an Enhanced Proxy object for client access
}
回复

使用道具 举报

千问 | 2007-1-24 12:56:49 | 显示全部楼层
Overriding the de-serialization process of our LazyEntityRef
Here is how to enhance LazyEntityRef object as LazyEntity,
Class clazz = Class.forName(

lazyEntityRef.getClassName(),

false,

Thread.currentThread().getContextClassLoader());
ProxyFactory pf = new ProxyFactory();
pf.setSuperclass(clazz);
pf.setInterfaces(...);//Optionally set a Serializable marker interface to override the serialization process this class
pf.setHandler(new ProxyInterceptor(lazyEntityRef));
...//set Method Filters
//Return Enhanced Class = LazyEntity
return pf.createClass().newInstance();
回复

使用道具 举报

千问 | 2007-1-24 12:56:49 | 显示全部楼层
Creating Enhanced Proxy Object via ClientProxy
Once our LazyEntity object is created, we need intercept the calls to this object. This interceptor will looks-up our remote bean and fetches the actual data for our entity. So, let's add a call back handler (called ProxyInterceptor) to our LazyEntity object.
Let's create a ProxyInterceptor class implementing javassist's MethodHandler interface and implement the callback method - invoke(). This ProxyInterceptoris then added to LazyEntity object.
public Object invoke(Object arg0, Method method, Method arg2, Object[] args) throws Throwable {
//handle serialization callback if a Serializable interface is added above in the LazyEnity proxy
try {

if (entity == null) {

entity =
LazyInitializerRemote.lazyInitializeEntity(
lazyEntityRef.getClassName(),
ref.getId(),
ref.getToken());

}

return method.invoke(entity, args);
} catch (InvocationTargetException e) {

throw e;
}
}
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题

0

回帖

4882万

积分

论坛元老

Rank: 8Rank: 8

积分
48824836
热门排行