Hibernate lazy Loading

Source: Internet
Author: User
Tags sessions

Reference: http://blog.csdn.net/s_good/article/details/7411642

1. About the lazy mechanism:

Lazy initialization errors are the most common errors when you use Hibernate to develop projects. If a deferred retrieval policy is configured on a class or collection, it must be initialized when the proxy class instance or proxy collection is persisted (that is, within the session scope). If it is initialized in a free state, a lazy initialization error occurs.

The following sets the lazy property of the <class> element of the Customer.hbm.xml file to True, indicating the use of a deferred retrieval strategy:

<class name= "Mypack. Customer "table=" CUSTOMERS "lazy=" true ">

When executing the session's load () method, Hibernate does not immediately execute a SELECT statement that queries the Customers table, returning only instances of the proxy class of the customer class, which is characterized by the following characteristics:

(1) dynamically generated by hibernate at run time, it extends the customer class, so it inherits all the properties and methods of the customer class, but its implementation is transparent to the application.
(2) When Hibernate creates an instance of the customer proxy class, it only initializes its OID property, and all other properties are null, so this proxy class instance consumes little memory.
(3) When an application accesses the customer proxy class instance for the first time (for example, by calling the Customer.getxxx () or Customer.setxxx () method), Hibernate initializes the proxy class instance, executes the SELECT statement during initialization, All data from the customer object is actually loaded from the database. One exception is that when an application accesses the GetID () method of the Customer proxy class instance, Hibernate does not initialize the proxy class instance because the OID exists when the proxy class instance is created and does not have to be queried in the database.

The following code first loads the customer object through the session's load () method, and then accesses its Name property:

tx = Session.begintransaction ();
Customer customer= (Customer) Session.load (Customer.class,new Long (1));
Customer.getname ();
Tx.commit ();

Hibernate does not execute any SELECT statements when running the Session.load () method, only returns an instance of the proxy class for the customer class, with an OID of 1, which is specified by the second parameter of the load () method. When the application calls the Customer.getname () method, Hibernate initializes the customer proxy class instance, loads the customer object's data from the database, and executes the following SELECT statement:

SELECT * from CUSTOMERS where id=1;
SELECT * from OrDERS where customer_id=1;

When the <class> element's Lazy property is true, it affects the various run-time behaviors of the session's load () method, as illustrated below.

1. If the loaded customer object does not exist in the database, the session's load () method does not throw an exception, and the following exception is thrown only when the Customer.getname () method is run:

ERROR lazyinitializer:63-exception Initializing Proxy
Net.sf.hibernate.ObjectNotFoundException:No row with Thegiven identifier exists:1, of class:
Mypack. Customer

2. If the application has not accessed the customer object throughout the session, the instance of the customer proxy class is never initialized and hibernate does not execute any SELECT statements. The following code attempts to access the customer free object after closing the session:

tx = Session.begintransaction ();
Customer customer= (Customer) Session.load (Customer.class,new Long (1));
Tx.commit ();
Session.close ();
Customer.getname ();

Since the instance of the customer proxy class referenced by the customer reference variable is never initialized within the session scope, hibernate throws the following exception when executing the Customer.getname () method:

ERROR lazyinitializer:63-exception Initializing Proxy
Net.sf.hibernate.hibernateexception:couldnotinitializeproxy-theowningsessionwasclosed

Thus, an instance of the customer proxy class can be initialized only within the current session scope.

3. The Initialize () static method of the Net.sf.hibernate.Hibernate class is used to explicitly initialize the proxy class instance within the session scope, and the isinitialized () method is used to determine whether the proxy class instance has been initialized. For example:

tx = Session.begintransaction ();
Customer customer= (Customer) Session.load (Customer.class,new Long (1));
if (! Hibernate.isinitialized (Customer))
Hibernate.initialize (customer);
Tx.commit ();
Session.close ();
Customer.getname ();

The above code explicitly initializes the customer proxy class instance within the session scope through the Hibernate class's Initialize () method, so the customer free object can be accessed normally when the session is closed.

4. Hibernate initializes the behavior of the proxy class instance when the application accesses the GetID () method of the proxy class instance, for example:

tx = Session.begintransaction ();
Customer customer= (Customer) Session.load (Customer.class,new Long (1));
Customer.getid ();
Tx.commit ();
Session.close ();
Customer.getname ();

When an application accesses the Customer.getid () method, the method returns the OID value of the customer proxy class instance directly, without querying the database. Because the reference variable customer always refers to an instance of the customer proxy class that is not initialized, Hibernate throws the following exception when the session closes and then executes the Customer.getname () method:

ERROR lazyinitializer:63-exception Initializing Proxy
Net.sf.hibernate.hibernateexception:couldnotinitializeproxy-theowningsessionwasclosed

2.Hibernate with lazy loading:

Hibernate Object Relational mappings provide deferred and non-deferred object initialization. Non-lazy loading reads an object in conjunction with all other objects related to the object. This can sometimes result in hundreds of (if not thousands) of SELECT statements being executed when the object is read. This problem sometimes occurs when a bidirectional relationship is used, which often results in the entire database being read at the initialization stage. Of course, you can take the trouble to check each object's relationship with other objects and put those most expensive deletions, but in the end, we might lose the convenience that we wanted to get in the ORM tool.


An obvious workaround is to use the lazy load mechanism provided by Hibernate. This initialization policy reads the relational object only when one of the objects calls it for a pair of many or many-to-many relationships. This process is transparent to the developer, and there are only a few requests for database operations, resulting in significant performance gains. One drawback of this technique is that a lazy-loading technique requires that a hibernate session be kept open while the object is in use. This becomes a major problem when abstracting the persistence layer by using the DAO pattern. In order to completely abstract the persistence mechanism, all database logic, including opening or closing sessions, cannot occur at the application level. Most commonly, some DAO implementation classes that implement simple interfaces encapsulate the database logic completely. A quick but clumsy solution is to discard the DAO pattern and add the database connection logic to the application layer. This may be useful for small applications, but in large systems, this is a serious design flaw that hinders the scalability of the system.

Lazy loading at the Web tier

Fortunately, the spring framework provides a convenient workaround for hibernate lazy loading and the integration of DAO patterns. For those of you who are not familiar with the integration of spring and hibernate, I will not discuss too much detail here, but I recommend that you understand Hibernate's integrated data access with spring. As an example of a Web application, Spring provides Opensessioninviewfilter and Opensessioninviewinterceptor. We are free to choose a class to implement the same functionality. The only difference between the two approaches is that interceptor runs in the spring container and is configured in the context of the Web application, and the filter runs before spring and is configured in XML. Whichever they are, they are requesting the hibernate session to be opened with the current session bound to the current (database) thread. Once bound to a thread, this open hibernate session can be used transparently in the DAO implementation class. This session remains open for a view that delays loading a value object in the database. Once this logical view is complete, the hibernate session is closed in the Dofilter method of the filter or the Posthandle method of the Interceptor. The following is a sample configuration for each component:


Configuration of the Interceptor:

<beans>
<bean id= "UrlMapping"
class= "Org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" >
<property name= "Interceptors" >
<list>
<ref bean= "Opensessioninviewinterceptor"/>
</list>
</property>
<property name= "Mappings" >

</bean>

<bean name= "Opensessioninviewinterceptor"
class= "Org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor" >
<property name= "sessionfactory" ><ref bean= "Sessionfactory"/></property>
</bean>
</beans>

Configuration of filter

<web-app>

<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
Org.springframework.orm.hibernate.support.OpenSessionInViewFilter
</filter-class>
</filter>

<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*. Spring </url-pattern>
</filter-mapping>

</web-app>


It is easy to implement Hibernate's DAO interface to use open sessions. In fact, if you've already used the spring framework to implement your Hibernate Dao, chances are you don't need to change anything. The convenient hibernatetemplate common component makes accessing the database a piece of cake, and the DAO interface can only access the database through this component. The following is an example of DAO:


public class Hibernateproductdao extends Hibernatedaosupport implements Productdao {

Public Product getproduct (Integer productId) {
Return (Product) gethibernatetemplate (). Load (Product.class, productId);
}

Public Integer saveproduct (product product) {
Return (Integer) gethibernatetemplate (). Save (product);
}

public void UpdateProduct (product product) {
Gethibernatetemplate (). Update (product);
}
}

Using lazy loading in the business logic layer

Even outside the view, the spring framework makes it easy to implement lazy loading by using the AOP interceptor Hibernateinterceptor. This hibernate interceptor transparently intercepts requests for methods that are configured in a business object in the context of the spring application, opens a hibernate session before invoking the method, and then closes the session after the method finishes executing. Let's look at a simple example, assuming we have an interface Bussinessobject:


Public interface Businessobject {
public void Dosomethingthatinvolvesdaos ();
}
Class Businessobjectimpl implements the Businessobject interface:

public class Businessobjectimpl implements Businessobject {
public void Dosomethingthatinvolvesdaos () {
Lots of logic that calls
DAO Classes which access
Data Objects lazily
}
}


With some configuration in the context of the spring application, we can intercept the method that calls Businessobject, and then make its method support lazy loading. Take a look at one of the following program fragments:


<beans>
<bean id= "Hibernateinterceptor" class= "Org.springframework.orm.hibernate.HibernateInterceptor" >
<property name= "Sessionfactory" >
<ref bean= "Sessionfactory"/>
</property>
</bean>
<bean id= "Businessobjecttarget" class= "Com.acompany.BusinessObjectImpl" >
<property name= "Somedao" ><ref bean= "Somedao"/></property>
</bean>
<bean id= "Businessobject" class= "Org.springframework.aop.framework.ProxyFactoryBean" >
<property name= "target" ><ref bean= "Businessobjecttarget"/></property>
<property name= "Proxyinterfaces" >
<value>com.acompany.BusinessObject</value>
</property>
<property name= "Interceptornames" >
<list>
<value>hibernateInterceptor</value>
</list>
</property>
</bean>
</beans>

When Businessobject is called, Hibernateinterceptor opens a hibernate session and passes the call request to the Businessobjectimpl object. When the Businessobjectimpl execution completes, the session is closed transparently by Hibernateinterceptor. The application layer's code does not need to know any persistence layer logic or implement deferred loading. The instance variable of the employee is loaded by hibernate, and it is possible that hibernate itself will be given the instance variable in the form of load.

3. Test the lazy load in the unit test


Finally, we need to use j-unit to test our Lazy loader program. We can easily implement this requirement by rewriting the setup and Teardown methods in the TestCase class. I prefer to use this handy abstract class as the base class for all my test classes.


Public abstract class Mylazytestcase extends TestCase {

Private Sessionfactory sessionfactory;
Private session session;

public void SetUp () throws Exception {
Super.setup ();
Sessionfactory sessionfactory = (sessionfactory) getbean ("Sessionfactory");
Session = Sessionfactoryutils.getsession (Sessionfactory, true);
Session s = sessionfactory.opensession ();
Transactionsynchronizationmanager.bindresource (Sessionfactory, New Sessionholder (s));

}

Protected Object Getbean (String beanname) {
Code to get objects from Spring application context
}

public void TearDown () throws Exception {
Super.teardown ();
Sessionholder holder = (sessionholder) transactionsynchronizationmanager.getresource (sessionfactory);
Session s = holder.getsession ();
S.flush ();
Transactionsynchronizationmanager.unbindresource (sessionfactory);
Sessionfactoryutils.closesessionifnecessary (S, sessionfactory);
}
}



We first say that using lazy=false to solve the problem, this method is very useful, but in the actual process is not practical, if you have an object associated with several or even more than 10 self-object, then every time you load a word to execute a lot of HQL statements, you can imagine this efficiency problem, especially when using the list. So it is not practical, and in the above mentioned "using lazy loading in the business Logic layer" is also the case.

Many friends who use the web like to use Opensessionview to load the problem. Good I also like to use, simple and convenient. But there are a few things to say:

1. Opensessionview mode is not always available in quartz

2. In the process of blocking after spring, it is not possible to take the sub-objects in the interception object using Opensessionview

There are a lot of things that can't be used, so how to solve them. Some people say that is lazy=false, you want to solve a simple quartz when the entire Web project to adjust the load seems inappropriate.

What to do, I think this time can consider the use of SQL statements to reload the proxy class, so that when you want to invoke the manual loading is more appropriate and convenient, but estimated to be more than a few lines of code, think about

4. Workaround

There are three simple ways to say it:
1. Set the lazy to false, the most 2 way, but the simplest, simple is beautiful, there will still be a lot to use this way to solve similar problems.

Hibernate4 defaults to lazy-loading by setting lazy to true. So you need to manually change the value of the lazy property yourself.

2. Add Opensessioninviewfilter to Web. xml

<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
Org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
</filter-class>
</filter
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping> 3. Query statements before using the left join fetch or INNER JOIN FETCH syntax: HQL = "from employee"; After: HQL = "from employee left Jo In fetch ";

Hibernate lazy Loading

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.