Hibernate 對象關系映射提供延遲的與非延遲的對象初始化。非延遲加載在讀取一個對象的時候會將與這個對象所有相關的其他對象一起讀取出來。這有時會導致成百的(如果不是成千的話) select 語句在讀取對象的時候執行。這個問題有時出現在使用雙向關系的時候,經常會導致整個數據庫都在初始化的階段被讀出來了。當然,你可以不厭其煩地檢查每一個對象與其他對象的關系,并把那些最昂貴的刪除,但是到最后,我們可能會因此失去了本想在 ORM 工具中獲得的便利。
一個明顯的解決方法是使用
Hibernate
提供的延遲加載機制。這種初始化策略只在一個對象調用它的一對多或多對多關系時才將關系對象讀取出來。這個過程對開發者來說是透明的,而且只進行了很少的數據庫操作請求,因此會得到比較明顯的性能提升。這項技術的一個缺陷是延遲加載技術要求一個
Hibernate
會話要在對象使用的時候一直開著。這會成為通過使用
DAO
模式將持久層抽象出來時的一個主要問題。為了將持久化機制完全地抽象出來,所有的數據庫邏輯,包括打開或關閉會話,都不能在應用層出現。最常見的是,一些實現了簡單接口的
DAO
實現類將數據庫邏輯完全封裝起來了。一種快速但是笨拙的解決方法是放棄
DAO
模式,將數據庫連接邏輯加到應用層中來。這可能對一些小的應用程序有效,但是在大的系統中,這是一個嚴重的設計缺陷,妨礙了系統的可擴展性。
在
Web
層進行延遲加載
幸運的是,
Spring
框架為
Hibernate
延遲加載與
DAO
模式的整合提供了一種方便的解決方法。對那些不熟悉
Spring
與
Hibernate
集成使用的人,我不會在這里討論過多的細節,但是我建議你去了解
Hibernate
與
Spring
集成的數據訪問。以一個
Web
應用為例,
Spring
提供了
OpenSessionInViewFilter
和
OpenSessionInViewInterceptor
。我們可以隨意選擇一個類來實現相同的功能。兩種方法唯一的不同就在于
interceptor
在
Spring
容器中運行并被配置在
web
應用的上下文中,而
Filter
在
Spring
之前運行并被配置在
web.xml
中。不管用哪個,他們都在請求將當前會話與當前(數據庫)線程綁定時打開
Hibernate
會話。一旦已綁定到線程,這個打開了的
Hibernate
會話可以在
DAO
實現類中透明地使用。這個會話會為延遲加載數據庫中值對象的視圖保持打開狀態。一旦這個邏輯視圖完成了,
Hibernate
會話會在
Filter
的
doFilter
方法或者
Interceptor
的
postHandle
方法中被關閉。下面是每個組件的配置示例:
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>
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>
實現
Hibernate
的
Dao
接口來使用打開的會話是很容易的。事實上,如果你已經使用了
Spring
框架來實現你的
Hibernate Dao,
很可能你不需要改變任何東西。方便的
HibernateTemplate
公用組件使訪問數據庫變成小菜一碟,而
DAO
接口只有通過這個組件才可以訪問到數據庫。下面是一個示例的
DAO
:
Example 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);
}
}
在業務邏輯層中使用延遲加載
即使在視圖外面,
Spring
框架也通過使用
AOP interceptor HibernateInterceptor
來使得延遲加載變得很容易實現。這個
Hibernate interceptor
透明地將調用配置在
Spring
應用程序上下文中的業務對象中方法的請求攔截下來,在調用方法之前打開一個
Hibernate
會話,然后在方法執行完之后將會話關閉。讓我們來看一個簡單的例子,假設我們有一個接口
BussinessObject
:
public interface BusinessObject {
public void doSomethingThatInvolvesDaos();
}
The class BusinessObjectImpl implements BusinessObject:
public class BusinessObjectImpl implements BusinessObject {
public void doSomethingThatInvolvesDaos() {
// lots of logic that calls
// DAO classes Which access
// data objects lazily
}
}
通過在
Spring
應用程序上下文中的一些配置,我們可以讓將調用
BusinessObject
的方法攔截下來,再令它的方法支持延遲加載。看看下面的一個程序片段:
<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>
當
businessObject
被調用的時候,
HibernateInterceptor
打開一個
Hibernate
會話,并將調用請求傳遞給
BusinessObjectImpl
對象。當
BusinessObjectImpl
執行完成后,
HibernateInterceptor
透明地關閉了會話。應用層的代碼不用了解任何持久層邏輯,還是實現了延遲加載。
在單元測試中測試延遲加載
最后,我們需要用 J-Unit 來測試我們的延遲加載程序。我們可以輕易地通過重寫 TestCase 類中的 setUp 和 tearDown 方法來實現這個要求。我比較喜歡用這個方便的抽象類類作為所有我的測試類的基類。
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);
}
}
由于要求在項目中使用泛型的DAO,所以上網Google了一下,找到了IBM的一篇文章。文章講得不錯,但是有些地方不清楚,如果完全按照那篇文章可能還會遇到一些困難。所以寫了這篇文章,解釋如何在項目中加入泛型的DAO實現。
首先是總的類關系的UML圖:
然后是在配置文件中的關系圖:
其中,IStaffDao是我們自己定義的接口,這個接口類似:
public ListlistAll();
public StaffgetByLogonAndId(Stringlogon,Integerid);
// more
}
GenericDao<T , PK extends Serilizable>
是泛型的
Dao
接口:
*2006-11-22
*范型DAO接口
* @author ZouAng
*Contact<ahref="mailto:richardeee@gmail.com">ZouAng</a>
*/
public interface GenericDao < T,PK extends Serializable > {
/**
*保存一個對象到數據庫
* @param newInstance需要保存的對象
* @return
*/
PKcreate(TnewInstance);
/**
*從數據庫讀取一個對象
* @param id主鍵
* @return
*/
Tread(PKid);
/**
*更新一個對象
* @param transientObject被更新的對象
*/
void update(TtransientObject);
/**
*刪除一個對象
* @param transientObject被刪除的對象
*/
void delete(TtransientObject);
}
GenericDaoHibernateImpl
是
GenericDao
接口的泛型實現
:
/**
*2006-11-22
*范型DAO實現
* @author ZouAng
*Contact<ahref="mailto:richardeee@gmail.com">ZouAng</a>
*/
public class GenericDaoHibernateImpl < T,PK extends Serializable >
extends HibernateDaoSupport
implements GenericDao < T,PK > ,FinderExecutor {
private Class < T > type;
private FinderNamingStrategynamingStrategy = new SimpleFinderNamingStrategy(); // Default.Canoverrideinconfig
private FinderArgumentTypeFactoryargumentTypeFactory = new SimpleFinderArgumentTypeFactory(); // Default.Canoverrideinconfig
public GenericDaoHibernateImpl(Class < T > type) {
this .type = type;
}
/* (non-Javadoc)
*@seecom.gdnfha.atcs.common.service.dao.GenericDao#create(java.lang.Object)
*/
public PKcreate(TnewInstance) {
return (PK)getHibernateTemplate().save(newInstance);
}
/* (non-Javadoc)
*@seecom.gdnfha.atcs.common.service.dao.GenericDao#delete(java.lang.Object)
*/
public void delete(TtransientObject) {
getHibernateTemplate().delete(transientObject);
}
/* (non-Javadoc)
*@seecom.gdnfha.atcs.common.service.dao.GenericDao#read(java.io.Serializable)
*/
public Tread(PKid) {
return (T)getHibernateTemplate().get(type,id);
}
/* (non-Javadoc)
*@seecom.gdnfha.atcs.common.service.dao.GenericDao#update(java.lang.Object)
*/
public void update(TtransientObject) {
getHibernateTemplate().update(transientObject);
}
public List < T > executeFinder(Methodmethod, final Object[]queryArgs)
{
final QuerynamedQuery = prepareQuery(method,queryArgs);
return (List < T > )namedQuery.list();
}
public Iterator < T > iterateFinder(Methodmethod, final Object[]queryArgs)
{
final QuerynamedQuery = prepareQuery(method,queryArgs);
return (Iterator < T > )namedQuery.iterate();
}
private QueryprepareQuery(Methodmethod,Object[]queryArgs)
{
final StringqueryName = getNamingStrategy().queryNameFromMethod(type,method);
final QuerynamedQuery = getSession().getNamedQuery(queryName);
String[]namedParameters = namedQuery.getNamedParameters();
if (namedParameters.length == 0 )
{
setPositionalParams(queryArgs,namedQuery);
} else {
setNamedParams(namedParameters,queryArgs,namedQuery);
}
return namedQuery;
}
private void setPositionalParams(Object[]queryArgs,QuerynamedQuery)
{
// Setparameter.UsecustomHibernateTypeifnecessary
if (queryArgs != null )
{
for ( int i = 0 ;i < queryArgs.length;i ++ )
{
Objectarg = queryArgs[i];
TypeargType = getArgumentTypeFactory().getArgumentType(arg);
if (argType != null )
{
namedQuery.setParameter(i,arg,argType);
}
else
{
namedQuery.setParameter(i,arg);
}
}
}
}
private void setNamedParams(String[]namedParameters,Object[]queryArgs,QuerynamedQuery)
{
// Setparameter.UsecustomHibernateTypeifnecessary
if (queryArgs != null )
{
for ( int i = 0 ;i < queryArgs.length;i ++ )
{
Objectarg = queryArgs[i];
TypeargType = getArgumentTypeFactory().getArgumentType(arg);
if (argType != null )
{
namedQuery.setParameter(namedParameters[i],arg,argType);
}
else
{
if (arg instanceof Collection) {
namedQuery.setParameterList(namedParameters[i],(Collection)arg);
}
else
color:
發表評論
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

評論