Discussion hibernate lazy loading

Hibernate lazy initialization error is the use of development projects in the most common mistakes. If the configuration of a class or collection of search strategy of delay, it must be when the agent or agent class instance is a collection of persistent state (that is in Session range) time to initialize it. Initialized in the free state only if it will produce delayed initialization error.

The following elements of the Customer.hbm.xml file the lazy attribute set <class> true, that the use of delayed search strategies:

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

When the Executive Session of the load () method, Hibernate does not immediately execute the query CUSTOMERS table select statement, only the Customer class returns an instance of the proxy class, the proxy class with the following characteristics:

(1) from Hibernate dynamically generated at run time, it extends the Customer class Customer class so it inherits all the properties and methods, but its implementation is transparent to applications.
(2) When Hibernate creates a proxy class instance of Customer, just initialize its OID attribute, other attributes are all null, so the proxy class instance takes very little memory.
(3) When the application first visit to the Customer agent class instance (for example, call customer.getXXX () or customer.setXXX () method), Hibernate will initialize the proxy class instance, in the select statement to perform the initialization process, Customer real object loaded from the database all the data. But an exception, and that is when the application instance of the proxy class to access Customer getId () method, Hibernate does not initialize the proxy class instance, because when creating the proxy class instance OID to exist, do not have to query the database .

Tip: Hibernate uses CGLIB tool to generate proxy classes persistent class. CGLIB is a powerful Java byte code generation tool, it can expand dynamically generated runtime Java class or Java interface to implement the proxy class. More knowledge about the CGLIB, please refer to: http://cglib.sourceforge.net/.

The following code to pass Session of the load () method loads the Customer object, then access its name attribute:

tx = session.beginTransaction ();
Customer customer = (Customer) session.load (Customer.class, new Long (1));
customer.getName ();
tx.commit ();

Running session.load () method, Hibernate does not execute any select statement, only the Customer class returns an instance of the proxy class, it's OID is 1, which is the load () method of the second parameter specified. When an application calls customer.getName () method, Hibernate initializes an instance of the proxy class Customer, Customer object is loaded from the database data, perform the following select statement:

select * from CUSTOMERS where
select * from ORDERS where CUSTOMER_ID = 1;

When the elements of the lazy attribute <class> true, would affect the Session of the load () method of the various run-time behavior, the following example.

1. If you load the Customer object does not exist in the database, Session of the load () method does not throw an exception, only when running customer.getName () method will throw the following exception:

ERROR LazyInitializer: 63 - Exception initializing proxy
net.sf.hibernate.ObjectNotFoundException: No row with the given identifier exists: 1, of class:
mypack.Customer

2. If the Session range, the application not visited Customer object, then the Customer instance of the proxy class has not been initialized, Hibernate will not execute any select statement. The following code attempts to close the Session object after free access to Customer:

tx = session.beginTransaction ();
Customer customer = (Customer) session.load (Customer.class, new Long (1));
tx.commit ();
session.close ();
customer.getName ();

As Customer reference variable customer references an instance of the proxy class within the Session has not been initialized, so in the implementation of customer.getName () method, Hibernate throws the following exception:

ERROR LazyInitializer: 63 - Exception initializing proxy
net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed

Thus, Customer proxy instance of the class only in the context of the current Session to be initialized.

3. net.sf.hibernate.Hibernate class initialize () static method is used within the Session explicitly initialize proxy class instance, isInitialized () method is used to determine whether an instance of the proxy class 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 in the Session through the Hibernate classes within the initialize () method to explicitly initialize the Customer agent class instance, so when the Session is closed, you can normally access the Customer free object.

4. When an application to access the proxy class instance getId () method, Hibernate does not initialize the proxy class instance trigger the behavior, such as:

tx = session.beginTransaction ();
Customer customer = (Customer) session.load (Customer.class, new Long (1));
customer.getId ();
tx.commit ();
session.close ();
customer.getName ();

When an application to access customer.getId () method, the method simply returns an instance of the proxy class OID Customer value, without having to query the database. Customer is always the reference variable refers to the Customer agent has not been initialized class instance, so when the Executive Session closed after customer.getName () method, Hibernate throws the following exception:

ERROR LazyInitializer: 63 - Exception initializing proxy
net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed

Solution:

Because hibernate using lazy = true, so that when you use hibernate query to return the actual proxy class using cglib enhanced, but did not actually fill; when you in the front, use it to value (getXXX), the time Hibernate will execute the query to the database, and populate the object, but at this time if the proxy class associated with this session has been closed off, it will have kind of error.
When doing one to many, sometimes "could not initialize proxy - clothe owning Session was sed, this seems to hibernate cache issue. Problem-solving: the need to set the <many-to-one> where lazy =" false ". But may cause another exception called

failed to lazily initialize a collection of role: XXXXXXXX, no session or session was closed

Solution: Add in 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>
Can;

Reference:
Hibernate with lazy loading:

Hibernate object-relational mapping to provide the delay and non-delay object initialization. Non-lazy when it reads an object and the object will all read it together with other objects. This sometimes leads to hundreds (if not thousands of words) select statement is executed when the object is read. This problem sometimes occurs when using the two-way relationship, often resulting in the entire database in the initialization phase is read out. Of course, you can take the trouble to check each object and other objects of the relationship, and to remove the most expensive, but in the end, we may lose this to the ORM tools available facilities.

One obvious solution is to use lazy loading mechanism provided by Hibernate. This strategy is only one object initialization call it-to-many or many to many relationships between objects only to read out. This process is transparent to the developer, and only got a few requests for database operations, it will be more obvious in performance. One drawback of this technique is to delay load the technical requirements of a Hibernate session when the object is used to has been open. This will be by using the DAO pattern to abstract the persistence layer when a major problem. Persistence mechanisms in order to completely abstract, all the database logic, including open or close the session, can not appear in the application layer. The most common is a simple interface that implement DAO implementation class to encapsulate the database logic completely. A fast but clumsy solution is to abandon DAO mode, add the database connection logic to the application layer. This may be an effective small applications, but in large systems, this is a serious design flaw prevented the system scalability.

Delays in loading Web layer

Fortunately, Spring framework Hibernate lazy loading with the integration of DAO pattern provides a convenient solution. For those not familiar with Spring and Hibernate integration of people, I will not discuss too many details here, but I suggest you go to learn Hibernate and Spring integration of data access. A Web application as an example, Spring provides a OpenSessionInViewFilter and OpenSessionInViewInterceptor. We can choose a class to achieve the same functionality. The only difference the two methods is that interceptor in the Spring container and configured to run in the context of web applications, and Filter before running in the Spring and configured in the web.xml. Regardless of what they are requesting the current session with the current (database) when you open the thread-bound Hibernate session. Once bound to the thread, this opens a Hibernate session in DAO implementation class can be transparently used. This session will load the database values for the delay in view of the object remains open. Once completed this logical view, Hibernate session in the Filter's doFilter method or methods Interceptor's postHandle be closed. Here is an example configuration of each component:

Interceptor configuration:

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

Filter configuration

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

Hibernate Dao implementation of the interface to use to open the session is very easy. In fact, if you have used the Spring framework to achieve your Hibernate Dao, you probably do not need to change anything. Public HibernateTemplate convenience to access the database component into a piece of cake, but only through the DAO interface, this component can access to the database. Here is an example of the 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);
)
)

Use lazy loading in the business logic layer

Even in view of the outside, Spring framework also HibernateInterceptor through the use of AOP interceptors to make it easy to become lazy. The Hibernate interceptor will be called transparently in the Spring application context configuration in the business object method to intercept the request down, before calling the method to open a Hibernate session, and then after completing the implementation of the method will then close. Let's look at a simple example, suppose we have an interface BussinessObject:

public interface BusinessObject (
public void doSomethingThatInvolvesDaos ();
)
BusinessObject class BusinessObjectImpl implements the interface:

public class BusinessObjectImpl implements BusinessObject (
public void doSomethingThatInvolvesDaos () (
/ / Lots of logic that calls
/ / DAO classes Which access
/ / Data objects lazily
)
)

Spring application context by some of the configuration, we can intercept calls BusinessObject way down, and then make its way to support lazy loading. A look at the following program fragment:

<beans>
<bean>
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</ Property>
</ Bean>
<bean>
<property name="someDAO"> <ref bean="someDAO"/> </ property>
</ Bean>
<bean>
<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 invoked, HibernateInterceptor open a Hibernate session, and call the request object passed to the BusinessObjectImpl. When BusinessObjectImpl after implementation, HibernateInterceptor transparent closed session. The application layer of code do not understand any persistence logic, or to achieve a lazy loading.

Delay in the loading unit test to test

Finally, we need to use the J-Unit test our lazy loading process. We can easily TestCase class by overriding the setUp and tearDown methods to achieve this requirement. I prefer to use the convenience abstract class as my base class for all tests.

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

分类:Java 时间:2010-10-01 人气:615
分享到:
blog comments powered by Disqus

相关文章

iOS 开发

Android 开发

Python 开发

JAVA 开发

开发语言

PHP 开发

Ruby 开发

搜索

前端开发

数据库

开发工具

开放平台

Javascript 开发

.NET 开发

云计算

服务器

Copyright (C) codeweblog.com, All Rights Reserved.

CodeWeblog.com 版权所有 黔ICP备15002463号-1

processed in 0.765 (s). 12 q(s)