4.9 Struts與Hibernate的整合策略
前面介紹了Hibernate的一些相關(guān)知識點,距離Hibernate進(jìn)入實際開發(fā)還有一段路要走。Hibernate作為持久層解決方案,必須與其他表現(xiàn)層技術(shù)組合在一起才可形成一個J2EE開發(fā)框架。經(jīng)常看到網(wǎng)上一些朋友給出的Hibernate入門示例,居然在JSP頁面中訪問Hibernate Configuratioin對象。甚至看到某些所謂的精通J2EE書籍,也居然在JSP頁面中訪問Hibernate的Configuration對象——這種現(xiàn)狀非常讓人擔(dān)憂,Hibernate并不是萬金油,并不是說項目中使用Hibernate就怎么了不起了,而是通過使用Hibernate,可以讓J2EE應(yīng)用架構(gòu)更科學(xué),可以讓開發(fā)者以更好的面向?qū)ο蟮姆绞竭M(jìn)行項目開發(fā)。
反過來說,即使不使用Hibernate,而使用普通的JDBC持久化解決方案,也不應(yīng)該在JSP(表現(xiàn)層)訪問到JDBC API(持久層API)。下面介紹如何讓Hibernate和Struts進(jìn)行整合,整合Spring部分將在后面章節(jié)介紹。
4.9.1 工廠模式介紹
工廠模式是指當(dāng)應(yīng)用程序中A組件需要B組件協(xié)助時,并不是直接創(chuàng)建B組件的實例,而是通過B組件的工廠——該工廠可以生成某一個類型組件的實例。在這種模式下,A組件無須與B組件以硬編碼方式耦合在一起,而只需要與B組件的工廠耦合。
對于A組件而言,它只關(guān)心工廠生產(chǎn)的實例是否滿足某種規(guī)范,即實現(xiàn)了某個接口(滿足接口規(guī)范,即可供自己正常調(diào)用)。這種模式提供了對象之間清晰的角色劃分,降低了程序的耦合。
接口產(chǎn)生的全部實例通常實現(xiàn)相同接口,接口里定義全部實例共同擁有的方法,這些方法在不同的實現(xiàn)類中實現(xiàn)方式不同。程序調(diào)用者無須關(guān)心方法的具體實現(xiàn),從而降低了系統(tǒng)異構(gòu)的代價。
下面是工廠模式的示例代碼:
//Person接口定義
public interface Person
{??
??? /**
??? * @param name 對name打招呼
??? * @return 打招呼的字符串
??? */
??? public String sayHello(String name);
??? /**
??? * @param name 對name告別
??? * @return 告別的字符串
??? */
??? public String sayGoodBye(String name);
}
該接口定義Person的規(guī)范,該接口必須擁有兩個方法:能打招呼、能告別。規(guī)范要求實現(xiàn)該接口的類必須具有這兩個方法:
//American類實現(xiàn)Person接口
public class American implements Person
{
??? /**
??? * @param name 對name打招呼
??? * @return 打招呼的字符串
??? */
??? public String sayHello(String name)
??? {
??????? return name + ",Hello";
??? }
??? /**
??? * @param name 對name告別
??? * @return 告別的字符串
??? */
??? public String sayGoodBye(String name)
??? {
??????? return name + ",Good Bye";
??? }
}
下面是實現(xiàn)Person接口的另一個實現(xiàn)類Chinese
public class Chinese implements Person
{
??? /**
??? * @param name 對name打招呼
??? * @return 打招呼的字符串
??? */
??? public String sayHello(String name)
??? {
??????? return name + ",您好";
??? }
??? /**
??? * @param name 對name告別
??? * @return 告別的字符串
??? */
??? public String sayGoodBye(String name)
??? {
??????? return name + ",下次再見";
??? }
}
然后看Person工廠的代碼:
public class PersonFactory
{
??? /**
??? * 獲得Person實例的工廠方法
??? * @ param ethnic 調(diào)用該實例工廠方法傳入的參數(shù)
??? * @ return返回Person實例
??? */
??? public Person getPerson(String ethnic)
??? {
??????? //根據(jù)參數(shù)返回Person接口的實例
??????? if (ethnic.equalsIgnoreCase("chin"))
??????? {
??????????? return new Chinese();
??????? }
??????? else
??????? {
??????????? return new American();
??????? }
??? }
}
最簡單的工廠模式的框架基本如上所示。
主程序部分僅僅需要與工廠耦合,而無須與具體的實現(xiàn)類耦合在一起。下面是主程序部分:
public class FactroyTest
{
??? public static void main(String[] args)
??? {
??????? //創(chuàng)建PersonFactory的實例,獲得工廠實例
??????? PersonFactory pf = new PersonFactory();
??????? //定義接口Person的實例,面向接口編程
??????? Person p = null;
??????? //使用工廠獲得Person的實例
??????? p = pf.getPerson("chin");
??????? //下面調(diào)用Person接口的方法
??????? System.out.println(p.sayHello("wawa"));
??????? System.out.println(p.sayGoodBye("wawa"));
??????? //使用工廠獲得Person的另一個實例
??????? p = pf.getPerson("ame");
??????? //再次調(diào)用Person接口的方法
??????? System.out.println(p.sayHello("wawa"));
??????? System.out.println(p.sayGoodBye("wawa"));
??? }
}
主程序從Person接口的具體類中解耦出來,而且程序調(diào)用者無須關(guān)心Person的實例化過程,角色劃分清晰。主程序僅僅與工廠服務(wù)定位結(jié)合在一起,獲得工廠的引用,程序?qū)⒖色@得所有工廠產(chǎn)生的實例。具體類的變化,重要接口不發(fā)生任何改變,調(diào)用者程序代碼部分幾乎無須發(fā)生任何改動。
4.9.2 使用DAO模式
第1章介紹了J2EE應(yīng)用的架構(gòu),最上面的表現(xiàn)層,表現(xiàn)層與MVC框架的控制器交互,控制器負(fù)責(zé)調(diào)用業(yè)務(wù)邏輯組件的業(yè)務(wù)邏輯方法來處理用戶請求,而業(yè)務(wù)邏輯組件則依賴于DAO組件提供的數(shù)據(jù)庫原子操作,這種模式也被稱為DAO模式。
由上面關(guān)于J2EE應(yīng)用架構(gòu)的介紹可見,控制器總是依賴于業(yè)務(wù)邏輯組件,而業(yè)務(wù)邏輯組件總是依賴于DAO組件。也就是說,控制器需要調(diào)用業(yè)務(wù)邏輯組件的方法,而業(yè)務(wù)邏輯組件需要調(diào)用DAO組件的方法。
DAO模式的分層非常清晰,持久層訪問被封裝在DAO層下,而決不會擴(kuò)散到業(yè)務(wù)邏輯層,更不會在JSP頁面(表現(xiàn)層)中進(jìn)行持久層訪問。
注意:即使在早期的Model 1(使用JSP + JavaBean創(chuàng)建應(yīng)用的模式,沒有使用MVC設(shè)計模式)模式下,持久層訪問也被封裝在JavaBean中完成,而不是直接在JSP頁面中進(jìn)行數(shù)據(jù)庫訪問。對于直接在JSP中訪問持久層API的做法,可以說根本不了解J2EE開發(fā)。
那么控制器采用怎樣的方式訪問業(yè)務(wù)邏輯組件呢?應(yīng)該采用工廠模式,讓控制器與業(yè)務(wù)邏輯組件的實現(xiàn)類分離,僅與業(yè)務(wù)邏輯工廠耦合;同樣,業(yè)務(wù)邏輯組件也應(yīng)該采用工廠模式訪問DAO模式,而不是直接與DAO實現(xiàn)類耦合。
后面的案例部分會介紹更實際的整合策略,此處僅僅介紹DAO模式下兩個工廠模式策略。
4.9.3 DAO組件的工廠模式
在J2EE應(yīng)用開發(fā)中,可擴(kuò)展性是一個隨時需要關(guān)注的問題。而DAO組件是經(jīng)常需要增加的項目組件,如果每次需要增加一個DAO組件都需要修改代碼是相當(dāng)讓人沮喪的事情。為了避免這種情況,采用XML配置文件來管理所有的DAO組件,這種DAO組件配置文件的代碼如下:
<?xml version="1.0" encoding="GBK"?>
<daoContext>
??? <!-- 配置應(yīng)用需要的sonDao組件 -->
??? <dao id="sonDao" class="org.yeeku.dao.impl.SonDaoImpl"/>
??? <!-- 配置應(yīng)用需要的personDao組件 -->
??? <dao id="personDao" class="org.yeeku.dao.impl.PersonDaoImpl"/>
</daoContext>
查看上面的配置文件可以看出,應(yīng)用中有配置了兩個DAO組件,因為每個DAO組件在J2EE應(yīng)用中僅需要一個實例就足夠了,因此DAO工廠類提供了一個緩存池來緩存每個DAO實例,并負(fù)責(zé)在應(yīng)用啟動時創(chuàng)建所有的DAO。
下面是DAO工廠類的代碼:
public class DaoFactory
{
??? //用于緩存DAO實例的Map對象
??? private Map<String, Dao> daoMap = new HashMap<String , Dao>();
??? //將DAO工廠寫成單態(tài)模式
??? private static DaoFactory df;
??? //DAO工廠的構(gòu)造器
??? private DaoFactory()throws Exception
??? {
??????? //使用SAXReader來負(fù)責(zé)解析daoContext.xml配置文檔
??? ??? Document doc = new SAXReader().read(new File(ConstantsUtil.realPath
??????? + "\\daoContext.xml"));
??????? //獲取文檔的根文檔
??? ??? Element root = doc.getRootElement();
??????? //獲取daoContext根元素的所有子元素
??? ??? List el = root.elements();
??? ??? for (Iterator it = el.iterator();it.hasNext() ; )
??? ??? {
??????????? //每個子元素對應(yīng)一個DAO組件
??????? ??? Element em = (Element)it.next();
??????? ??? String id = em.attributeValue("id");
??? ??????? //獲取實現(xiàn)類
??????? ??? String impl = em.attributeValue("class");
??????????? //通過反射,根據(jù)類名創(chuàng)建DAO組件的實例
??????? ??? Class implClazz = Class.forName(impl);
??????? ??? Dao d = (Dao)implClazz.newInstance();
??????????? //將創(chuàng)建的DAO組件放入緩存池中
??????? ??? daoMap.put(id, d);
??? ??? }
??? }
??? //單態(tài)模式必須提供一個入口方法來創(chuàng)建DAO工廠的方法
??? public static DaoFactory instance()throws Exception
??? {
??????? //如果DAO工廠還未創(chuàng)建
??? ??? if (df == null)
??? ??? {
??????? ??? df = new DaoFactory();
??? ??? }
??? ??? return df;
??? }
??? //下面的方法用于根據(jù)DAO組件ID獲取DAO組件
??? public Dao getDao(String id)
??? {
??? ??? return daoMap.get(id);
??? }
}
通過上面的工廠類代碼可以看出,DAO工廠負(fù)責(zé)初始化所有的DAO組件。系統(tǒng)每增加一個DAO組件,無須再修改任何代碼,僅僅需要在daoContext.xml配置文件中增加配置即可。
注意:這種整合策略非常優(yōu)秀??蓴U(kuò)展性很好,如果應(yīng)用需要增加一個DAO組件,只需要修改配置文件,并提供相應(yīng)的DAO組件實現(xiàn)即可。而且,如果有一天需要重構(gòu)DAO組件,只須提供修改過的DAO組件實現(xiàn)類,而業(yè)務(wù)邏輯組件無須任何改變。
業(yè)務(wù)邏輯組件代碼無須與DAO實現(xiàn)類耦合,業(yè)務(wù)邏輯組件的代碼面向DAO組件的接口編程,將業(yè)務(wù)邏輯組件和DAO組件的耦合降低到接口層次。
4.9.4 業(yè)務(wù)邏輯組件的工廠模式
與此類似的是,業(yè)務(wù)邏輯組件完全可以采用這種編程模式,業(yè)務(wù)邏輯組件的配置文件代碼如下:
<?xml version="1.0" encoding="GBK"?>
<appContext>
??? <!-- 配置應(yīng)用需要的業(yè)務(wù)邏輯組件,每個業(yè)務(wù)邏輯組件對應(yīng)一個app元素 -->
??? <app id="wawa" class="org.yeeku.service.impl.WawaServiceImpl"/>
</appContext>
業(yè)務(wù)邏輯組件工廠同樣可根據(jù)該配置文件來初始化所有業(yè)務(wù)邏輯組件,并將業(yè)務(wù)邏輯組件放入緩存池中,讓控制器與業(yè)務(wù)邏輯組件的耦合降低到接口層次。業(yè)務(wù)邏輯組件的工廠類代碼如下:
public class AppFactory
{
??? private Map<String , Object> appMap = new HashMap<String , Object>();
??? //業(yè)務(wù)邏輯組件工廠采用單態(tài)模式
??? private static AppFactory df;
??? //業(yè)務(wù)邏輯組件工廠的私有構(gòu)造器
??? private AppFactory()throws Exception
??? {
??????? //使用SAXReader來負(fù)責(zé)解析appContext.xml配置文檔
??????? Document doc = new SAXReader().read(new File(ConstantsUtil.realPath
??????? + "\\appContext.xml"));
??????? //獲取文檔的根文檔
??????? Element root = doc.getRootElement();
??????? //獲取appContext根元素的所有子元素
??????? List el = root.elements();
??????? for (Iterator it = el.iterator();it.hasNext() ; )
??????? {
??????????? //每個app元素對應(yīng)一個業(yè)務(wù)邏輯組件
??????????? Element em = (Element)it.next();
??????????? String id = em.attributeValue("id");
??????????? //根據(jù)配置文件指定的業(yè)務(wù)邏輯組件實現(xiàn)類來創(chuàng)建業(yè)務(wù)邏輯組件實例
??????????? String impl = em.attributeValue("class");
??????????? Class implClazz = Class.forName(impl);
??????????? Object d = implClazz.newInstance();
??????????? //將業(yè)務(wù)邏輯組件放入緩存池中
??????????? appMap.put(id , d);
??????? }
??? }
??? //單態(tài)模式必須提供入口方法,用于創(chuàng)建業(yè)務(wù)邏輯組件工廠
??? public static AppFactory instance()throws Exception
??? {
??????? //如果業(yè)務(wù)邏輯組件工廠為空
??????? if (df == null)
??????? {
??????????? df = new AppFactory();
??????? }
??????? return df;
??? }
??? //根據(jù)業(yè)務(wù)邏輯組件的id屬性獲取業(yè)務(wù)邏輯組件
??? public Object getApp(String id)
??? {
??????? //直接從緩存池中取出業(yè)務(wù)邏輯組件,并返回
??????? return appMap.get(id);
??? }
}
從某種程度上來講,這種方式與后來Spring的控制反轉(zhuǎn)(Inversion of Control,IoC)容器有異曲同工之妙,但Spring的IoC容器則提供了更多的功能。
上面的兩個類中都用到了一個ConstantsUtil,它僅用于保存一個全局變量,有一個public static的realPath屬性,該屬性用于保存應(yīng)用在服務(wù)器中的路徑。
posted @
2009-07-19 10:08 jadmin 閱讀(69) |
評論 (0) |
編輯 收藏
4.8 事 件 機(jī) 制
通常,Hibernate執(zhí)行持久化過程中,應(yīng)用程序無法參與其中。所有的數(shù)據(jù)持久化操作,對用戶都是透明的,用戶無法插入自己的動作。
通過事件框架,Hibernate允許應(yīng)用程序能響應(yīng)特定的內(nèi)部事件,從而允許實現(xiàn)某些通用的功能,或?qū)ibernate功能進(jìn)行擴(kuò)展。
Hibernate的事件框架由兩個部分組成:
?? ● 攔截器機(jī)制,對于特定動作攔截,回調(diào)應(yīng)用中的特定動作。
?? ● 事件系統(tǒng),重寫Hibernate的事件監(jiān)聽器。
4.8.1 攔截器
通過Interceptor接口,可以從Session中回調(diào)應(yīng)用程序的特定方法,這種回調(diào)機(jī)制可讓應(yīng)用程序在持久化對象被保存、更新、刪除或加載之前,檢查并修改其屬性。
通過Interceptor接口,可以在數(shù)據(jù)進(jìn)入數(shù)據(jù)庫之間,對數(shù)據(jù)進(jìn)行最后的檢查,如果數(shù)據(jù)不符合要求,可以修改數(shù)據(jù),從而避免非法數(shù)據(jù)進(jìn)入數(shù)據(jù)庫。當(dāng)然,通常無須這樣做,只是在某些特殊的場合下,才考慮使用攔截器完成檢查功能。
使用攔截器可按如下步驟進(jìn)行:
(1)定義實現(xiàn)Interceptor接口的攔截器類;
(2)通過Session啟用攔截器,或者通過Configuration啟用全局?jǐn)r截器。
下面是一個攔截器的示例代碼,該攔截器沒有進(jìn)行任何實際的操作,僅僅打印出標(biāo)志代碼:
public class MyInterceptor extends EmptyInterceptor
{
??? //更新的次數(shù)
??? private int updates;
??? //插入的次數(shù)
??? private int creates;
??? //刪除數(shù)據(jù)時,將調(diào)用onDelete方法
??? public void onDelete(Object entity,Serializable id,Object[]
??? state,String[] propertyNames, Type[] types)
??? {
??????? //do nothing
??? }
??? //同步Session和數(shù)據(jù)庫中的數(shù)據(jù)
??? public boolean onFlushDirty(Object entity, Serializable id, Object[]
??? currentState, Object[] previousState, String[] propertyNames, Type[]
??????????????????????? ??? types)
??? {
??????? //每同步一次,修改的累加器加1
??????? updates++;
??????? for ( int i=0; i < propertyNames.length; i++ )
??????? {
??????????? if ( "lastUpdateTimestamp".equals( propertyNames[i] ) )
??????????? {
??????????????? currentState[i] = new Date();
??????????????? return true;
??????????? }
??????? }
??????? return false;
??????? }
??? //加載持久化實例時,調(diào)用該方法
??? public boolean onLoad(Object entity,Serializable id,Object[]
??? state,String[] propertyNames,Type[] types)
??? {
??????? System.out.println("========================");
??????? for ( int i=0; i < propertyNames.length; i++ )
??????? {
??????????? if ( "name".equals( propertyNames[i] ) )
??????????? {
??????????????? System.out.println(state[i]);
??????????????? state[i] = "aaa";
??????????????? return true;
??????????? }
??????? }
??????? return false;
??? }
??? //保存持久化實例時,調(diào)用該方法
??? public boolean onSave(Object entity,Serializable id,Object[]
??? state,String[] propertyNames,Type[] types)
??? {
??????? creates++;
??????? for ( int i=0; i<propertyNames.length; i++ )
??????? {
??????????? if ( "createTimestamp".equals( propertyNames[i] ) )
??????????? {
??????????????? state[i] = new Date();
??? ??????????? return true;
??????????? }
??????? }
??????? return false;
??? }
??? //提交刷新
??? public void postFlush(Iterator entities)
??? {
??????? System.out.println("創(chuàng)建的次數(shù): " + creates + ", 更新的次數(shù): " +
??? updates);
??? }
??? public void preFlush(Iterator entities)
??? {
??????? updates=0;
??????? creates=0;
??? }
??? //事務(wù)提交前,觸發(fā)該方法
??? public void beforeTransactionCompletion(Transaction tx)
??? {
??????? System.out.println("事務(wù)即將結(jié)束");
??? }
??? //事務(wù)提交后,觸發(fā)該方法
??? public void afterTransactionCompletion(Transaction tx)
??? {
??????? System.out.println("事務(wù)已經(jīng)結(jié)束");
??? }
}
在上面的攔截器實現(xiàn)類中,實現(xiàn)了很多方法,這些方法都是在Hibernate執(zhí)行特定動作時自動調(diào)用。
完成了攔截器的定義,下面是關(guān)于攔截器的使用。攔截器的使用有兩種方法:
?? ● 通過SessionFactory的openSession(Interceptor in)方法打開一個帶局部攔截器的Session。
?? ● 通過Configuration的setInterceptor(Interceptor in)方法設(shè)置全局?jǐn)r截器。
下面是使用局部攔截器的示例代碼:
public class HibernateUtil
{
??? //靜態(tài)類屬性 SessionFactory
??? public static final SessionFactory sessionFactory;
??? //靜態(tài)初始化塊,完成靜態(tài)屬性的初始化
??? static
??? {
??????? try
??????? {
??????????? //采用默認(rèn)的hibernate.cfg.xml來啟動一個Configuration的實例
??????????? Configuration configuration=new Configuration().configure();
??????????? //由Configuration的實例來創(chuàng)建一個SessionFactory實例
??????????? sessionFactory = configuration.buildSessionFactory();
??????? }
??????? catch (Throwable ex)
??????? {
??????????? System.err.println("初始化sessionFactory失敗." + ex);
??????????? throw new ExceptionInInitializerError(ex);
??????? }
??? }
??? //ThreadLocal是隔離多個線程的數(shù)據(jù)共享,不存在多個線程之間共享資源,因此不再需要
??? 對線程同步???
??? public static final ThreadLocal session = new ThreadLocal();
??? //不加攔截器的打開Session方法
??? public static Session currentSession() throws HibernateException
??? {
??????? Session s = (Session) session.get();
??????? //如果該線程還沒有Session,則創(chuàng)建一個新的Session
??????? if (s == null)
??????? {
??????????? s = sessionFactory.openSession();
??????????? //將獲得的Session變量存儲在ThreadLocal變量的Session里
??????????? session.set(s);
??????? }
??????? return s;
??? }
??? //加攔截器的打開Session方法
??? public static Session currentSession(Interceptor it) throws
??? HibernateException
??? {
??????? Session s = (Session) session.get();
??????? //如果該線程還沒有Session,則創(chuàng)建一個新的Session
??????? if (s == null)
??????? {
??????????? //以攔截器創(chuàng)建Session對象
??????? ??? s = sessionFactory.openSession(it);
??????????? //將獲得的Session變量存儲在ThreadLocal變量的Session里
??????????? session.set(s);
??????????? }
??????? return s;
??? }
??? //關(guān)閉Session對象
??? public static void closeSession() throws HibernateException
??? {
??????? Session s = (Session) session.get();
??????? if (s != null)
??? ??????? s.close();
??????? session.set(null);
??? }
}
上面的Hibernate工具類提供了兩個currentSession方法,分別用于不使用攔截器獲取Session對象和使用攔截器獲取Session對象。
下面是主程序使用攔截器的代碼片段:
private void testUser()
{
??? //以攔截器開始Session
??? Session session = HibernateUtil.currentSession(new MyInterceptor());
??? //開始事務(wù)
??? Transaction tx = session.beginTransaction();
??? //執(zhí)行下面的代碼時,可以看到系統(tǒng)回調(diào)onSave等方法
??? /*
??? User u = new User();
??? u.setName("Yeeku Lee");
??? u.setAge(28);
??? u.setNationality("中國");
??? session.persist(u);
??? u.setAge(29);
??? u.setAge(30);
??? session.persist(u);
??? */
??? //執(zhí)行下面的代碼時,可以看到系統(tǒng)回調(diào)onLoad等方法
??? Object o = session.load(User.class , new Integer(1));
??? System.out.println(o);
??? User u = (User)o;
??? System.out.println(u.getName());
??? //提交事務(wù)時,可以看到系統(tǒng)回調(diào)事務(wù)相關(guān)方法
??? tx.commit();
??? HibernateUtil.closeSession();
}
4.8.2 事件系統(tǒng)
Hibernate 3的事件系統(tǒng)是功能更強(qiáng)大的事件框架,事件系統(tǒng)可以替代攔截器,也可以作為攔截器的補(bǔ)充來使用。
基本上,Session接口的每個方法都有對應(yīng)的事件。如LoadEvent和FlushEvent等。當(dāng)Session調(diào)用某個方法時,Hibernate Session會生成對應(yīng)的事件,并激活對應(yīng)的事件監(jiān)聽器。
系統(tǒng)默認(rèn)監(jiān)聽器實現(xiàn)的處理過程,完成了所有的數(shù)據(jù)持久化操作,包括插入和修改等操作。如果用戶定義了自己的監(jiān)聽器,則意味著用戶必須完成對象的持久化操作。
例如,可以在系統(tǒng)中實現(xiàn)并注冊LoadEventListener監(jiān)聽器,該監(jiān)聽器負(fù)責(zé)處理所有調(diào)用Session的load()方法的請求。
監(jiān)聽器是單態(tài)模式對象,即所有同類型的事件處理共享同一個監(jiān)聽器實例,因此監(jiān)聽器不應(yīng)該保存任何狀態(tài),即不應(yīng)該使用成員變量。
使用事件系統(tǒng)可按如下步驟進(jìn)行:
(1)實現(xiàn)自己的事件監(jiān)聽器類;
(2)注冊自定義事件監(jiān)聽器,代替系統(tǒng)默認(rèn)的事件監(jiān)聽器。
實現(xiàn)用戶的自定義監(jiān)聽器有如下3個方法:
?? ● 實現(xiàn)對應(yīng)的監(jiān)聽器接口,這是不可思議的,實現(xiàn)接口必須實現(xiàn)接口內(nèi)的所有方法,關(guān)鍵是必須實現(xiàn)Hibernate對應(yīng)的持久化操作,即數(shù)據(jù)庫訪問,這意味著程序員完全取代了Hibernate的底層操作。
?? ● 繼承事件適配器,可以選擇性地實現(xiàn)需要關(guān)注的方法,但依然試圖取代Hibernate完成數(shù)據(jù)庫的訪問,這也不太現(xiàn)實。
?? ● 繼承系統(tǒng)默認(rèn)的事件監(jiān)聽器,擴(kuò)展特定方法。
實際上,前兩種方法很少使用。因為Hibernate的持久化操作也是通過這些監(jiān)聽器實現(xiàn)的,如果用戶取代了這些監(jiān)聽器,則應(yīng)該自己實現(xiàn)所有的持久化操作,這意味著用戶放棄了Hibernate的持久化操作,而改為自己完成Hibernate的核心操作。
通常推薦采用第三種方法實現(xiàn)自己的事件監(jiān)聽器。Hibernate默認(rèn)的事件監(jiān)聽器都被聲明成non-final,從而方便用戶繼承。
下面是用戶自定義監(jiān)聽器的示例:
//自定義LoadListener,繼承默認(rèn)的DefaultLoadEventListener實現(xiàn)類
public class MyLoadListener extends DefaultLoadEventListener
{
??? //在LoadEventListener接口僅僅定義了這個方法
??? public Object onLoad(LoadEvent event, LoadEventListener.LoadType
??? loadType)throws HibernateException
??? {
??????? //先調(diào)用父類的onLoad方法,從而完成默認(rèn)的持久化操作
??????? Object o = super.onLoad(event, loadType);
??????? //加入用戶的自定義處理
??????? System.out.println("自定義的load事件");
??????? System.out.println(event.getEntityClassName() + "==========" +
??????? event.getEntityId());
??????? return o;
??? }
}
下面還有一個MySaveListener,用于監(jiān)聽SaveEvent事件:
//自定義SavaListener,繼承默認(rèn)的DefaultSaveEventListener實現(xiàn)類
public class MySaveListener extends DefaultSaveEventListener
{
??? //該方法完成實際的數(shù)據(jù)插入動作
??? protected Serializable performSaveOrUpdate(SaveOrUpdateEvent event)
??? {
??????? //先執(zhí)行用戶自定義的操作
??????? System.out.println(event.getObject());
??????? //調(diào)用父類的默認(rèn)持久化操作
??????? return super.performSaveOrUpdate(event);
??? }
}
注意:擴(kuò)展用戶自定義監(jiān)聽器時,別忘了在方法中調(diào)用父類的對應(yīng)方法。
注冊用戶自定義監(jiān)聽器也有兩種方法:
?? ● 編程式,通過使用Configuration對象編程注冊。
?? ● 聲明式,在Hibernate的XML格式配置文件中進(jìn)行聲明,使用Properties格式的配置文件將無法配置自定義監(jiān)聽器。
下面的示例代碼,通過編程方式使用自定義監(jiān)聽器:
public class HibernateUtil2
{
??? //靜態(tài)類屬性 SessionFactory
??? public static final SessionFactory sessionFactory;
??? //靜態(tài)初始化塊,完成靜態(tài)屬性的初始化
??? static
??? {
??????? try
??????? {
??????????? Configuration cfg = new Configuration();
??????????? //注冊loadEventListener監(jiān)聽器
??????????? cfg.getSessionEventListenerConfig().setLoadEventListener
??????????? ( new MyLoadListener() );
??????????? //注冊saveListener監(jiān)聽器
??????????? cfg.getSessionEventListenerConfig().setSaveEventListener
??????????? (new MySaveListener() );
??????????? //由Configuration實例來創(chuàng)建一個SessionFactory實例
??????????? sessionFactory = cfg.configure().buildSessionFactory();
??????? }
??????? catch (Throwable ex)
??????? {
??????????? System.err.println("初始化sessionFactory失敗." + ex);
??????????? throw new ExceptionInInitializerError(ex);
??????? }
??? }
??? //ThreadLocal是隔離多個線程的數(shù)據(jù)共享,不存在多個線程之間共享資源,因此不再需要
??? 對線程同步
??? public static final ThreadLocal session = new ThreadLocal();
??? //不加攔截器的打開Session方法
??? public static Session currentSession() throws HibernateException
??? {
??????? Session s = (Session) session.get();
??????? //如果該線程還沒有Session,則創(chuàng)建一個新的Session
??????? if (s == null)
??????? {
??????????? s = sessionFactory.openSession();
??? ??????? //將獲得的Session變量存儲在ThreadLocal變量的Session里
??????????? session.set(s);
??????? }
??????? return s;
??? }
??? //關(guān)閉Session對象
??? public static void closeSession() throws HibernateException
??? {
??????? Session s = (Session) session.get();
??????? if (s != null)
??????????? s.close();
??????? session.set(null);
??? }
}
如果不想修改代碼,也可以在配置文件中使用事件監(jiān)聽器,注冊事件監(jiān)聽器的Hibernate配置文件代碼如下:
<?xml version='1.0' encoding='GBK'?>
<!-- Hibernate配置文件的文件頭,包含DTD等信息 -->
<!DOCTYPE hibernate-configuration PUBLIC
??????? "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
??????? "http://hibernate.sourceforge.net/hibernate-configuration-3.0.
??????? dtd">
<!-- Hibernate配置文件的根元素 -->
<hibernate-configuration>
??? <session-factory>
??????? <!—設(shè)置數(shù)據(jù)庫驅(qū)動 -->
??????? <property name="connection.driver_class">com.mysql.jdbc.Driver
??????? </property>
??????? <!-- 數(shù)據(jù)庫服務(wù)的url -->
??? ??? <property name="connection.url">jdbc:mysql://localhost/hibernate
??????? </property>
??????? <!-- 數(shù)據(jù)庫服務(wù)的用戶名 -->
??????? <property name="connection.username">root</property>
??????? <!-- 數(shù)據(jù)庫服務(wù)的密碼 -->
??????? <property name="connection.password">32147</property>
??????? <!-- JDBC connection pool (use the built-in) -->
??????? <property name="connection.pool_size">5</property>
??????? <!-- 設(shè)置數(shù)據(jù)庫方言 -->
??????? <property name="dialect">org.hibernate.dialect.MySQLDialect
??????? </property>
??????? <!-- 顯示Hibernate生成的SQL語句 -->
??????? <property name="show_sql">true</property>
??????? <!-- 配置應(yīng)用啟動時,是否啟動自動建表 -->
??????? <property name="hbm2ddl.auto">update</property>
??????? <!-- 列出所有的持久化映射文件 -->
??????? <mapping resource="User.hbm.xml"/>
??????? <!-- 注冊事件監(jiān)聽器 -->
??? ??? <listener type="load" class="lee.MyLoadListener"/>
??? ??? <listener type="save" class="lee.MySaveListener"/>
??? </session-factory>
</hibernate-configuration>
使用配置文件注冊事件監(jiān)聽器雖然方便,但也有不利之處,通過配置文件注冊的監(jiān)聽器不能共享實例。如果多個<listener/>元素中使用了相同的類,則每一個引用都將產(chǎn)生一個新的攔截器實例。如果需要在多個事件之間共享監(jiān)聽器的實例,則必須使用編程方式注冊事件監(jiān)聽器。
注意:雖然監(jiān)聽器類實現(xiàn)了特定監(jiān)聽器的接口,在注冊的時候還要明確指出注冊的事件。這是因為一個類可能實現(xiàn)多個監(jiān)聽器的接口,注冊時明確指定要監(jiān)聽的事件,可以使得啟用或者禁用某個事件監(jiān)聽的配置工作更簡單。
posted @
2009-07-19 09:42 jadmin 閱讀(88) |
評論 (0) |
編輯 收藏
4.7 事 務(wù)控 制
每個業(yè)務(wù)邏輯方法都是由一系列的數(shù)據(jù)庫訪問完成,這一系列的數(shù)據(jù)訪問可能會修改多條數(shù)據(jù)記錄,這系列的修改應(yīng)該是一個整體,絕不能僅修改其中的幾條。也就是說,多個數(shù)據(jù)庫原子訪問應(yīng)該綁定成一個整體——這就是事務(wù)。事務(wù)是一個最小的邏輯執(zhí)行單元,整個事務(wù)不能分開執(zhí)行,要么同時執(zhí)行,要么同時放棄執(zhí)行。
4.7.1 事務(wù)的概念
事務(wù)是一步或幾步基本操作組成的邏輯執(zhí)行單元,這些基本操作作為一個整體執(zhí)行單元,它們要么全部執(zhí)行,要么全部取消,絕不能僅僅執(zhí)行部分。一般而言,每次用戶請求,對應(yīng)一個業(yè)務(wù)邏輯方法,一個業(yè)務(wù)邏輯方法往往具有邏輯上的原子性,應(yīng)該使用事務(wù)。例如,一個轉(zhuǎn)賬操作,對應(yīng)修改兩個賬戶的余額,這兩個賬戶的修改要么同時生效,要么同時取消——同時生效是轉(zhuǎn)賬成功,同時取消是轉(zhuǎn)賬失??;但不可只修改其中一個賬戶,那將破壞數(shù)據(jù)庫的完整性。
通常來講,事務(wù)具備如下4個特性:原子性(atomicity)、一致性(consistency)、隔離性(isolation)和持續(xù)性(durability)。這4個特性也簡稱為ACID性。
?? ● 原子性:事務(wù)是應(yīng)用中最小執(zhí)行單位,就如原子是自然界最小顆粒,具有不可再分的特征一樣。事務(wù)是應(yīng)用中不可再分的最小邏輯執(zhí)行體。
?? ● 一致性:事務(wù)執(zhí)行的結(jié)果,必須使數(shù)據(jù)庫從一個一致性狀態(tài),變到另一個一致性狀態(tài)。當(dāng)數(shù)據(jù)庫只包含事務(wù)成功提交的結(jié)果時,數(shù)據(jù)庫處于一致性狀態(tài)。如果系統(tǒng)運行發(fā)生中斷,某個事務(wù)尚未完成而被迫中斷,而該未完成的事務(wù)對數(shù)據(jù)庫所做的修改已被寫入數(shù)據(jù)庫,此時,數(shù)據(jù)庫就處于一種不正確的狀態(tài)。比如銀行在兩個賬戶之間轉(zhuǎn)賬,從A賬戶向B賬戶轉(zhuǎn)入1000元。系統(tǒng)先減少A賬戶的1000元,然后再為B賬戶增加1000元。如果全部執(zhí)行成功,數(shù)據(jù)庫處于一致性狀態(tài)。如果僅執(zhí)行完A賬戶金額的修改,而沒有增加B賬戶的金額,則數(shù)據(jù)庫就處于不一致性狀態(tài)。因此,一致性是通過原子性來保證的。
?? ● 隔離性:各個事務(wù)的執(zhí)行互不干擾,任意一個事務(wù)的內(nèi)部操作對其他并發(fā)的事務(wù),都具有隔離性。也即并發(fā)執(zhí)行的事務(wù)之間不能互相影響。
?? ● 持續(xù)性:持續(xù)性也稱為持久性(persistence),指事務(wù)一旦提交,對數(shù)據(jù)所做的任何改變,都要記錄到永久存儲器中,通常保存進(jìn)物理數(shù)據(jù)庫。
4.7.2 Hibernate的事務(wù)
Hibernate直接使用JDBC連接和JTA資源,不添加任何附加鎖定行為。Hibernate只添加自動版本管理,而不會鎖定內(nèi)存中的對象,也不會改變數(shù)據(jù)庫事務(wù)的隔離級別?;旧?,使用 Hibernate就好像直接使用JDBC(或者JTA/CMT)進(jìn)行數(shù)據(jù)庫訪問。
Hibernate中SessionFactory對象的創(chuàng)建代價很高,它是線程安全的對象,被設(shè)計成可以為所有的應(yīng)用程序線程所共享。通常,SessionFactory會在應(yīng)用程序啟動時創(chuàng)建,一旦創(chuàng)建了SessionFactory將不會輕易關(guān)閉,只有當(dāng)應(yīng)用關(guān)閉時,SessionFactory才會關(guān)閉。
而Session的對象是輕量級的,它也是線程不安全的。對于單個業(yè)務(wù)進(jìn)程單個工作單元而言,Session只被使用一次。創(chuàng)建Session時,并不會立即打開與數(shù)據(jù)庫之間的連接,Session只在需要進(jìn)行數(shù)據(jù)庫操作時,才會獲取JDBC連接。因此,打開和關(guān)閉Session,并不會對性能造成很大的影響。甚至即使無法確定一個請求是否需要數(shù)據(jù)訪問,也可以打開Session對象,因為如果不進(jìn)行數(shù)據(jù)庫訪問,Session不會獲取JDBC連接。
相反,數(shù)據(jù)庫事務(wù)應(yīng)該盡可能的短。從而,降低數(shù)據(jù)庫鎖定造成的資源爭用。數(shù)據(jù)庫長事務(wù)會導(dǎo)致應(yīng)用程序無法承載高并發(fā)的負(fù)荷。
由上面的介紹可知,Hiberante的Session和事務(wù)是緊密相關(guān)的,因為事務(wù)是通過Session來打開的。那么事務(wù)的范圍是多大?單個Session可以跨越多個數(shù)據(jù)庫事務(wù)嗎?事務(wù)和Session的對應(yīng)關(guān)系又如何呢?下面將介紹Hibernate Session和事務(wù)的關(guān)系。
4.7.3 事務(wù)和Session
數(shù)據(jù)庫操作必須在Hibernate的Session管理下進(jìn)行,但不推薦因為一次簡單的數(shù)據(jù)庫原子調(diào)用,就打開和關(guān)閉一次Session,數(shù)據(jù)庫事務(wù)也是如此。因為,對于一次原子操作打開的事務(wù)沒有任何意義——事務(wù)應(yīng)該是將多個操作步驟組合成一個邏輯整體。
事務(wù)是按順序發(fā)送并組成一個邏輯整體的原子操作單元。
注意:也就是說單個的SQL語句發(fā)送之后,自動事務(wù)提交模式失效了。這種自動提交模式僅為SQL控制臺設(shè)計,在實際項目沒有太大的實用價值。Hibernate禁止事務(wù)立即自動提交模式,或者讓應(yīng)用服務(wù)器禁止事務(wù)自動提交。
通常,建議每個請求對應(yīng)一個Session。在這種模式下,來自客戶端的請求被發(fā)送到服務(wù)器端,此處可能對應(yīng)一個業(yè)務(wù)邏輯方法。在這個業(yè)務(wù)邏輯方法內(nèi),一個新的Hibernate Session被打開,然后開始事務(wù),在事務(wù)內(nèi)執(zhí)行這個操作單元中所有的數(shù)據(jù)庫操作。一旦操作完成,需要發(fā)送給客戶端的響應(yīng)也準(zhǔn)備就緒。此時,提交事務(wù),然后關(guān)閉Session。在這種模式下,Session和用戶請求是一對一的關(guān)系,這是一種理想的Session管理模式。
為了達(dá)到這種效果,推薦使用一個ThreadLocal變量,把Session綁定到處理客戶端請求的線程上去。這種方式可以讓運行在該線程上的所有程序代碼輕松地訪問Session。也可以在一個ThreadLocal變量中保持事務(wù)上下文環(huán)境,不過這依賴于所選擇的數(shù)據(jù)庫事務(wù)劃分機(jī)制。這種實現(xiàn)模式被稱之為ThreadLocal Session和Open Session in View。
下面是一個HibernateUtil類,該類將Hibernate Session存放在一個ThreadLocal變量中,對于同一個線程的請求,將可以輕松訪問該Session。
public class HibernateUtil
{
??? public static final SessionFactory sessionFactory;
??? //靜態(tài)初始化塊,使用該類時使用該代碼塊
??? static
??? {
??????? try
??????? {
??????????? //采用默認(rèn)的hibernate.cfg.xml來啟動一個Configuration的實例
??????????? Configuration configuration=new Configuration().configure();
??????????? //由Configuration的實例來創(chuàng)建一個SessionFactory實例
??????????? sessionFactory = configuration.buildSessionFactory();
??????? }
??????? catch (Throwable ex)
??????? {
??????????? System.err.println("初始化sessionFactory失敗." + ex);
??????????? throw new ExceptionInInitializerError(ex);
??????? }
??? }
??? //ThreadLocal是隔離多個線程的數(shù)據(jù)共享,不存在多個線程之間共享資源,因此不再需要
??? 對線程同步
??? public static final ThreadLocal session = new ThreadLocal();
??? //該方法用于獲取當(dāng)前線程的Session對象
??? public static Session currentSession() throws HibernateException
??? {
??????? Session s = (Session) session.get();
??????? //如果該線程還沒有Session,則創(chuàng)建一個新的Session
??????? if (s == null)
??????? {
??????????? s = sessionFactory.openSession();
??????????? //將獲得的Session變量存儲在ThreadLocal變量的Session里
??????????? session.set(s);
??????? }
??????? return s;
??? }
??? //該方法用于關(guān)閉當(dāng)前線程里的Session
??? public static void closeSession() throws HibernateException
??? {
??????? Session s = (Session) session.get();
??????? if (s != null)
??????????? s.close();
??????? session.set(null);
??? }
}
在上面的代碼中,Hibernate Session被綁定到當(dāng)前線程。當(dāng)調(diào)用currentSession方法時,如果當(dāng)前線程中的Session已經(jīng)創(chuàng)建出來,那么將返回這個已經(jīng)存在的Session實例。
每次請求對應(yīng)一個Session的模式不僅可以用于設(shè)計操作單元,甚至很多業(yè)務(wù)處理流程都需要組合一系列的用戶操作,即用戶對數(shù)據(jù)庫的交叉訪問。
但是,對于企業(yè)應(yīng)用,跨用戶交互的數(shù)據(jù)庫事務(wù)是無法接受的。例如,在第一個頁面,用戶打開對話框,打開一個特定Session裝入的數(shù)據(jù),可以隨意修改對話框中的數(shù)據(jù),修改完成后,將修改結(jié)果存入數(shù)據(jù)庫。
從用戶的角度來看,這個操作單元被稱為應(yīng)用程序長事務(wù)。在一個J2EE應(yīng)用實現(xiàn)中,可以有很多方法來實現(xiàn)這種應(yīng)用程序長事務(wù)。
一個比較差的做法是,當(dāng)用戶思考時,應(yīng)用程序保持Session和數(shù)據(jù)庫事務(wù)是打開的,并保持?jǐn)?shù)據(jù)庫鎖定,以阻止并發(fā)修改,從而保證數(shù)據(jù)庫事務(wù)隔離級別和原子操作。這種數(shù)據(jù)庫鎖定會導(dǎo)致應(yīng)用程序無法擴(kuò)展并發(fā)用戶的數(shù)目。
因此,不要使用每個應(yīng)用對應(yīng)一次Hibernate Session的模式,也不要使用每次Http Session對應(yīng)一次Hibernate Session的模式。
注意:幾乎所有情況下,都不要使用每個應(yīng)用對應(yīng)一次Hibernate Session的模式,也不要使用每次Http Session對應(yīng)一次Hibernate Session的模式。
對于這種情況,Hibernate主要有如下兩種模式來解決這個問題:
?? ● 脫管對象,如果采用每次用戶請求對應(yīng)一次Session的模式。那么,前面載入的實例在用戶思考的過程中,始終與Session脫離,處于脫管狀態(tài)。都處于與Session脫離的狀態(tài)。Hibernate允許把脫管對象重新關(guān)聯(lián)到Session上,并且對修改進(jìn)行持久化。在這種模式下,自動版本化被用來隔離并發(fā)修改。這種模式也被稱為使用脫管對象的每個請求對應(yīng)一個Hibernate Session。
?? ● 長生命周期Session,Session可以在數(shù)據(jù)庫事務(wù)提交之后,斷開和底層的JDBC連接。當(dāng)新的客戶端請求到來時,它又重新連接上底層的JDBC連接。這種模式被稱為每個應(yīng)用程序事務(wù)對應(yīng)一個Session,因為應(yīng)用程序事務(wù)相當(dāng)長(跨越多個用戶請求),所以也被稱為每次應(yīng)用事務(wù)對應(yīng)一個Hibernate Session。
posted @
2009-07-19 09:11 jadmin 閱讀(87) |
評論 (0) |
編輯 收藏
數(shù)據(jù)過濾并不是一種常規(guī)的數(shù)據(jù)查詢方法,而是一種整體的篩選方法。數(shù)據(jù)過濾也可對數(shù)據(jù)進(jìn)行篩選,因此,將其放在Hibernate的數(shù)據(jù)查詢框架中介紹。
如果一旦啟用了數(shù)據(jù)過濾器,則不管數(shù)據(jù)查詢,還是數(shù)據(jù)加載,該過濾器將自動作用于所有數(shù)據(jù),只有滿足過濾條件的記錄才會被選出來。
過濾器與定義在類和集合映射文件上的“where”屬性非常相似。它們的區(qū)別是過濾器可以帶參數(shù),應(yīng)用程序可以在運行時決定是否啟用指定的過濾器,以及使用什么樣的參數(shù)值。而映射文件上的“where”屬性將一直生效,且無法動態(tài)傳入?yún)?shù)。
過濾器的用法很像數(shù)據(jù)庫視圖,區(qū)別是視圖在數(shù)據(jù)庫中已經(jīng)定義完成,而過濾器則還需在應(yīng)用程序中確定參數(shù)值。
過濾器的使用分成三步:
(1)定義過濾器。使用filter-def元素定義過濾器;
(2)使用過濾器。使用filter元素使用過濾器;
(3)在代碼中啟用過濾器。
前兩個步驟都是在Hibernate的映射文件中完成的,其中filter-def是hibernate-mapping元素的子元素,而filter元素是class集合元素的子元素。
filter-def元素用于定義一個過濾器,filter則將指定的過濾器應(yīng)用到指定的持久化類。
一個持久化類或集合可以使用多個過濾器,而一個過濾器也可以作用于多個持久化類或集合。
看下面的映射文件示例:
<?xml version="1.0"?>
<!-- Hibernate配置文件的文件頭,包含DTD等信息 -->
<!DOCTYPE hibernate-mapping
??? PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
??? "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Hibernate 配置文件的根元素 -->
<hibernate-mapping >
??? <!-- 每個class元素定義一個持久化類 -->
??? <class name="Category" table="category">
??????? <!-- 定義標(biāo)識屬性 -->
??? ??? <id name="id" column="category_id" >
??? ??????? <!-- 指定主鍵生成器策略 -->
??????? ??? <generator class="native"/>
??? ??? </id>
??????? <!-- 映射name屬性 -->
??? ??? <property name="name" type="string"/>
??????? <!-- 映射effectiveStartDate屬性 -->
??? ??? <property name="effectiveStartDate" column="eff_start_date"
??????? type="java.util.Date"/>
??????? <!-- 映射effectiveEndDate屬性 -->
??? ??? <property name="effectiveEndDate" column="eff_end_date"
??????? type="java.util.Date"/>
??????? <!-- 映射N-N關(guān)聯(lián)屬性 -->
??? ??? <set cascade="none" inverse="true" name="products"
??????? table="product_category">
??????????? <!-- 定義關(guān)聯(lián)屬性的key,對應(yīng)連接表中的外鍵列 -->
??????? ??? <key column="category_id"/>
??????????? <!-- 定義關(guān)聯(lián)屬性 -->
??????? ??? <many-to-many column="product_id" class="Product"/>
??? ??? </set>
??????? <!-- 使用過濾器,并設(shè)置過濾器條件 -->
??? ??? <filter name="effectiveDate" condition=":asOfDate BETWEEN
??????? eff_start_date and eff_end_date"/>
??? </class>
??? <!-- 定義第二個持久化類 -->
??? <class name="Product" table="product">
??????? <!-- 定義標(biāo)識屬性 -->
??? ??? <id name="id" column="product_id" >
??????????? <!-- 指定主鍵生成器策略 -->
??????? ??? <generator class="native"/>
??? ??? </id>
??????? <!-- 映射name屬性 -->
??? ??? <property name="name" type="string"/>
??????? <!-- 映射stockNumber屬性 -->
??? ??? <property name="stockNumber" column="stock_number" type="int"/>
??????? <!-- 映射effectiveStartDate屬性 -->
??? ??? <property name="effectiveStartDate" column="eff_start_date"
??????? type="java.util.Date"/>
??????? <!-- 映射effectiveEndDate屬性 -->
??? ??? <property name="effectiveEndDate" column="eff_end_date"
??????? type="java.util.Date"/>
??????? <!-- 映射N-N關(guān)聯(lián)屬性 -->
??????? <set cascade="all" name="categories" fetch="join"
??????? table="product_category" >
??????????? <!-- 定義關(guān)聯(lián)屬性的key,對應(yīng)連接表中的外鍵列 -->
??????? ??? <key column="product_id"/>
??????????? <!-- 定義關(guān)聯(lián)屬性 -->
??????? ??? <many-to-many column="category_id"
??????????????????????? class="Category" fetch="join">
??????????????? <!-- 對關(guān)聯(lián)屬性使用第一個過濾器 -->
??? ??????????? <filter name="effectiveDate"
??????????????????? condition=":asOfDate BETWEEN eff_start_date and
??????????????????? eff_end_date"/>
??????????????? <!-- 對關(guān)聯(lián)屬性使用第二個過濾器 -->
??????????? ??? <filter name="category" condition="category_id = :catId"/>
??????????? </many-to-many>
??? ??? </set>
??????? <filter name="effectiveDate" condition=":asOfDate BETWEEN
??????? eff_start_date AND eff_end_date"/>
??? </class>
??? <!-- 定義第一個過濾器,該過濾器包含一個date類型的參數(shù) -->
??? <filter-def name="effectiveDate">
??????? <filter-param name="asOfDate" type="date"/>
??? </filter-def>
??? <!-- 定義第二個過濾器,該過濾器包含一個long類型的參數(shù) -->
??? <filter-def name="category">
??????? <filter-param name="catId" type="long"/>
??? </filter-def>
</hibernate-mapping>
在上面的配置文件中,定義了兩個過濾器,過濾器的定義通過filter-def元素完成。定義過濾器時,只需要指定過濾器的名字,以及過濾器的參數(shù)即可。如Java里的一個方法聲明,只有方法名和參數(shù)列表,具體的方法實現(xiàn)是沒有的。
過濾器的過濾條件是使用過濾器時才確定的,使用過濾器通過filter元素確定,filter的condition屬性用于確定過濾條件,滿足該條件的記錄才會被抓取到。
系統(tǒng)默認(rèn)不啟用過濾器,必須顯式通過enableFilter(String filterName)才可以啟用過濾器,該方法返回一個Filter實例,F(xiàn)ilter包含setParameter方法用于為過濾器參數(shù)賦值。
一旦啟用了過濾器,過濾器在整個Session內(nèi)有效,所有的數(shù)據(jù)加載將自動應(yīng)用該過濾條件,直到調(diào)用disableFilter方法。
看下面的使用過濾器的示例代碼:
private void test() throws Exception
{
??? //獲取Hibernate Session對象
??? Session session = HibernateUtil.currentSession();
??? //開始事務(wù)
??? Transaction tx = session.beginTransaction();
??? //啟用第一個過濾器
??? session.enableFilter("effectiveDate")
??????????? //為過濾器設(shè)置參數(shù)
??????????? .setParameter("asOfDate", new Date());
??? //啟動第二個過濾器
??? session.enableFilter("category")
??????????? //為過濾器設(shè)置參數(shù)
??????????? .setParameter("catId", new Long(2));
??? //執(zhí)行查詢,該查詢沒有任何的查詢條件
??? Iterator results = session.createQuery("from Product as p")
????????????????????????? .iterate();
??? //遍歷結(jié)果集
??? while (results.hasNext())
??? {
??????? Product p = (Product)results.next();
??????? System.out.println(p.getName());
??????? //此處獲取Product關(guān)聯(lián)的種類,過濾器也將自動應(yīng)用過濾
??????? Iterator it = p.getCategories().iterator();
??????? System.out.println(p.getCategories().size());
??????? while (it.hasNext())
??????? {
??????????? Category c = (Category)it.next();
??????????? System.out.println(c.getName());
??????? }
??? }
??? tx.commit();
??? HibernateUtil.closeSession();
}
通過使用過濾器定義常用的數(shù)據(jù)篩選規(guī)則,如果是臨時的數(shù)據(jù)篩選,還是使用常規(guī)查詢比較好。對于從前使用行列表達(dá)式視圖的地方,此處可以考慮使用過濾器。
posted @
2009-07-19 09:08 jadmin 閱讀(120) |
評論 (0) |
編輯 收藏
4.5 SQL查詢
Hibernate還支持使用SQL查詢,使用SQL查詢可以利用某些數(shù)據(jù)庫的特性,或者用于將原有的JDBC應(yīng)用遷移到Hibernate應(yīng)用上。使用命名的SQL查詢還可以將SQL語句放在配置文件中配置,從而提高程序的解耦,命名SQL查詢還可以用于調(diào)用存儲過程。
如果是一個新的應(yīng)用,通常不要使用SQL查詢。
SQL查詢是通過SQLQuery接口來表示的,SQLQuery接口是Query接口的子接口,因此完全可以調(diào)用Query接口的方法:
?? ● setFirstResult(),設(shè)置返回結(jié)果集的起始點。
?? ● setMaxResults(),設(shè)置查詢獲取的最大記錄數(shù)。
?? ● list(),返回查詢到的結(jié)果集。
但SQLQuery比Query多了兩個重載的方法:
?? ● addEntity,將查詢到的記錄與特定的實體關(guān)聯(lián)。
?? ● addScalar,將查詢的記錄關(guān)聯(lián)成標(biāo)量值。
執(zhí)行SQL查詢的步驟如下:
(1)獲取Hibernate Session對象;
(2)編寫SQL語句;
(3)以SQL語句作為參數(shù),調(diào)用Session的createSQLQuery方法創(chuàng)建查詢對象;
(4)如果SQL語句包含參數(shù),調(diào)用Query的setXxx方法為參數(shù)賦值;
(5)調(diào)用SQLQuery對象的addEntity或addScalar方法將選出的結(jié)果與實體或標(biāo)量值關(guān)聯(lián);
(6)調(diào)用Query的list方法返回查詢的結(jié)果集。
看下面的SQL查詢示例:
private void test()
{
??? //獲取Hibernate Session對象
??? Session session = HibernateUtil.currentSession();
??? //開始事務(wù)
??? Transaction tx = session.beginTransaction();
??? //編寫SQL語句
??? String sqlString = "select {s.*} from student s where s.name like '馬軍'";
??? //以SQL語句創(chuàng)建SQLQuery對象
??? List l = session.createSQLQuery(sqlString)
??????????????????? //將查詢到的記錄與特定實體關(guān)聯(lián)起來
??????????????????? .addEntity("s",Student.class)
??????????????????? //返回全部的記錄集
??????????????????? .list();
??? //遍歷結(jié)果集
??? Iterator it = l.iterator();
??? while (it.hasNext())
??? {
??????? //因為將查詢結(jié)果與Student類關(guān)聯(lián),因此返回的是Student集合
??????? Student s = (Student)it.next();
??????? Set enrolments = s.getEnrolments();
??????? Iterator iter = enrolments.iterator();
??????? while(iter.hasNext())
??????? {
??????????? Enrolment e = (Enrolment)iter.next();
??????????? System.out.println(e.getCourse().getName());
??????? }
??? }
??? //提交事務(wù)
??? tx.commit();
??? //關(guān)閉Session
??? HibernateUtil.closeSession();
}
上面的示例顯示了將查詢記錄關(guān)聯(lián)成一個實體的示例。事實上,SQL查詢也支持將查詢結(jié)果轉(zhuǎn)換成標(biāo)量值,轉(zhuǎn)換成標(biāo)量值可以使用addScalar方法,如:
Double max = (Double) session.createSQLQuery("select max(cat.weight) as maxWeight from cats cat")
??????? .addScalar("maxWeight", Hibernate.DOUBLE);
??????? .uniqueResult();
使用SQL查詢,如果需要將查詢到的結(jié)果轉(zhuǎn)換成特定實體,就要求為選出的字段命名別名。這別名不是隨意命名的,而是以“/”實例名.屬性名“/”的格式命名,例如:
//依次將多個選出的字段命名別名,命名別名時都以ss作為前綴,ss是關(guān)聯(lián)實體的別名
String sqlStr = "select stu.studentId as {ss.studentNumber},"
??????? + "stu.name as {ss.name} from "
??????? + "student as stu where stu.name like '楊海華'";
List l = session.createSQLQuery(sqlStr)
??????????? //將查詢出的ss實例,關(guān)聯(lián)到Student類
??????????? .addEntity("ss",Student.class)
??????????? .list();
在第一個示例中,以{s.*}代表該表的全部字段,且關(guān)聯(lián)實例的別名也被指定為s。
注意:如果不使用{s.*}的形式,就可讓實體別名和表別名互不相同。關(guān)聯(lián)實體的類型時,被關(guān)聯(lián)的類必須有對應(yīng)的setter方法。
4.5.1 命名SQL查詢
可以將SQL語句不放在程序中,而放在配置文件中,這種方式以松耦合的方式配置SQL語句,可以提高程序解耦。
在Hibernate的映射文件中定義查詢名,然后確定查詢所用的SQL語句,然后就可以直接調(diào)用該命名SQL查詢。在這種情況下,不需要調(diào)用addEntity()方法,因為在配置命名SQL查詢時,已經(jīng)完成了查詢結(jié)果與實體的關(guān)聯(lián)。
下面是命名SQL查詢的配置片段:
<!-- 每個sql-query元素定義一個命名SQL查詢 -->
<sql-query name="mySqlQuery">
??? <!-- 關(guān)聯(lián)返回的結(jié)果與實體類 -->
??? <return alias="s" class="Student"/>
??????? <!-- 定義命名SQL查詢的SQL語句 -->
???? ??? SELECT {s.*}
??????? from student s WHERE s.name like'楊海華'
</sql-query>
sql-query元素是hibernate-mapping元素的子元素。因此,sql-query定義的名可以直接通過Session訪問,上面定義的mySqlQuery查詢可以直接訪問,下面是使用該命名SQL查詢的示例代碼:
private void testNamedSQl()
{
??? //獲取Hibernate Session對象
??? Session session = HibernateUtil.currentSession();
??? //開始事務(wù)
??? Transaction tx = session.beginTransaction();
??? //調(diào)用命名查詢,直接返回結(jié)果
??? List l = session.getNamedQuery("mySqlQuery")
??????????????????? ???? .list();
??? //遍歷結(jié)果集
??? Iterator it = l.iterator();
??? while (it.hasNext())
??? {
??????? //在定義SQL查詢時,已經(jīng)將結(jié)果集與Student類關(guān)聯(lián)起來
??????? //因此,集合里的每個元素都是Student實例
??????? Student s = (Student)it.next();
??????? Set enrolments = s.getEnrolments();
??????? Iterator iter = enrolments.iterator();
??????? while(iter.hasNext())
??????? {
??????????? Enrolment e = (Enrolment)iter.next();
??????????? System.out.println("=====================================");
??????????? System.out.println(e.getCourse().getName());
??????????? System.out.println("=====================================");
??????? }
??? }
??? tx.commit();
??? HibernateUtil.closeSession();
}
4.5.2 調(diào)用存儲過程
Hibernate 3增加了存儲過程的支持,該存儲過程只能返回一個結(jié)果集。
下面是Oracle 9i的存儲過程示例:
CREATE OR REPLACE FUNCTION selectAllEmployments
??? RETURN SYS_REFCURSOR
AS
??? st_cursor SYS_REFCURSOR;
BEGIN
??? OPEN st_cursor FOR
SELECT EMPLOYEE, EMPLOYER,
STARTDATE, ENDDATE,
REGIONCODE, EID, VALUE, CURRENCY
FROM EMPLOYMENT;
????? RETURN st_cursor;
END;
如果需要使用該存儲過程,可以先將其定義成命名SQL查詢,例如:
<!-- 定義命名SQL查詢,name屬性指定命名SQL查詢名 -->
<sql-query name="selectAllEmployees_SP" callable="true">
??? <!-- 定義返回列與關(guān)聯(lián)實體類屬性之間的映射 -->
??? <return alias="emp" class="Employment">
??????? <!-- 依次定義每列與實體類屬性的對應(yīng) -->
??? ??? <return-property name="employee" column="EMPLOYEE"/>
??? ??? <return-property name="employer" column="EMPLOYER"/>
??? ??? <return-property name="startDate" column="STARTDATE"/>
??? ??? <return-property name="endDate" column="ENDDATE"/>
??? ??? <return-property name="regionCode" column="REGIONCODE"/>
??? ??? <return-property name="id" column="EID"/>
??????? <!-- 將兩列值映射到一個關(guān)聯(lián)類的組件屬性 -->
??? ??? <return-property name="salary">
??????????? <!-- 映射列與組件屬性之間的關(guān)聯(lián) -->
??????? ??? <return-column name="VALUE"/>
??????? ??? <return-column name="CURRENCY"/>
??????? </return-property>
??? </return>
??? { ? = call selectAllEmployments() }
</sql-query>
調(diào)用存儲過程還有如下需要注意的地方:
?? ● 因為存儲過程本身完成了查詢的全部操作,所以調(diào)用存儲過程進(jìn)行的查詢無法使用setFirstResult()/setMaxResults()進(jìn)行分頁。
?? ● 存儲過程只能返回一個結(jié)果集,如果存儲過程返回多個結(jié)果集,Hibernate將僅處理第一個結(jié)果集,其他將被丟棄。
?? ● 如果在存儲過程里設(shè)定SET NOCOUNT ON,將有更好的性能表現(xiàn)。當(dāng)然也可以沒有該設(shè)定。
posted @
2009-07-19 09:02 jadmin 閱讀(1159) |
評論 (0) |
編輯 收藏
4.4 條 件 查 詢
條件查詢是更具面向?qū)ο筇厣臄?shù)據(jù)查詢方式。條件查詢可通過如下3個類完成:
?? ● Criteria,代表一次查詢。
?? ● Criterion,代表一個查詢條件。
?? ● Restrictions,產(chǎn)生查詢條件的工具類。
執(zhí)行條件查詢的步驟如下:
(1)獲得Hibernate的Session對象。
(2)以Session對象創(chuàng)建Criteria對象。
(3)增加Criterion查詢條件。
(4)執(zhí)行Criteria的list等方法返回結(jié)果集。
看下面的條件查詢示例:
private void test()
{
??? //獲取Hibernate Session對象
??? Session session = HibernateUtil.currentSession();
??? //開始事務(wù)
??? Transaction tx = session.beginTransaction();
??? //創(chuàng)建Criteria和添加查詢條件同步完成
??? //最后調(diào)用list方法,返回查詢到的結(jié)果集
??? List l = session.createCriteria(Student.class)
??????? //此處增加限制條件必須是Student已經(jīng)存在的屬性
??? ??? .add( Restrictions.gt("studentNumber" , new Long(20050231) ) )
??????? //如果要增加對Student的關(guān)聯(lián)類的屬性的限制則必須重新createCriteria()
??????? /如果此關(guān)聯(lián)屬性是集合,則只要集合里任意一個對象的屬性滿足下面條件
??????? .createCriteria("enrolments")即可
??????? .add( Restrictions.gt("semester" , new Short("2") ) )
??????? .list();
??????? Iterator it = l.iterator();
??? //遍歷查詢到的記錄
??? while (it.hasNext())
??? {
??????? Student s = (Student)it.next();
??????? System.out.println(s.getName());
??????? Set enrolments = s.getEnrolments();
??????? Iterator iter = enrolments.iterator();
??????? while(iter.hasNext())
??????? {
??????????? Enrolment e = (Enrolment)iter.next();
??????????? System.out.println(e.getCourse().getName());
?????? }
??? }
??? tx.commit();
??? ibernateUtil.closeSession();
}
在條件查詢中,Criteria接口代表一次查詢,該查詢本身不具備任何的數(shù)據(jù)篩選功能,Session調(diào)用createCriteria(Class clazz)方法對某個持久化類創(chuàng)建條件查詢實例。
Criteria包含如下兩個方法:
?? ● Criteria setFirstResult(int firstResult),設(shè)置查詢返回的第一行記錄。
?? ● Criteria setMaxResults(int maxResults),設(shè)置查詢返回的記錄數(shù)。
這兩個方法與Query的這兩個方法用法相似,都用于完成查詢分頁。
而Criteria還包含如下常用方法:
?? ● Criteria add(Criterion criterion),增加查詢條件。
?? ● Criteria addOrder(Order order),增加排序規(guī)則。
?? ● List list(),返回結(jié)果集。
Criterion接口代表一個查詢條件,該查詢條件由Restrictions負(fù)責(zé)產(chǎn)生,Restrictions是專門用于產(chǎn)生查詢條件的工具類,它的方法大部分都是靜態(tài)方法,常用的方法如下:
?? ● static Criterion allEq(Map propertyNameValues),判斷指定屬性(由Map參數(shù)的key指定)和指定值(由Map參數(shù)的value指定)是否完全相等。
?? ● static Criterion between(String propertyName,Object lo, Object hi),判斷屬性值在某個值范圍之內(nèi)。
?? ● static Criterion ilike(String propertyName, Object value),判斷屬性值匹配某個字符串。
?? ● static Criterion ilike(String propertyName, String value,MatchMode matchMode),判斷屬性值匹配某個字符串,并確定匹配模式。
?? ● static Criterion in(String propertyName,Collection values),判斷屬性值在某個集合內(nèi)。
?? ● static Criterion in(String propertyName,Object[] values),判斷屬性值是數(shù)組元素的其中之一。
?? ● static Criterion isEmpty(String propertyName),判斷屬性值是否為空。
?? ● static Criterion isNotEmpty(String propertyName),判斷屬性值是否不為空。
?? ● static Criterion isNotNull(String propertyName),判斷屬性值是否為空。
?? ● static Criterion isNull(String propertyName),判斷屬性值是否不為空。
?? ● static Criterion not(Criterion expression),對Criterion求否。
?? ● static Criterion sizeEq(String propertyName, int size),判斷某個屬性的元素個數(shù)是否與size相等。
?? ● static Criterion sqlRestriction(String sql),直接使用SQL語句作為篩選條件。
?? ● static Criterion sqlRestriction(String sql, Object[] values, Type[] types),直接使用帶參數(shù)占位符的SQL語句作為條件,并指定多個參數(shù)值。
?? ● static Criterion sqlRestriction(String sql, Object value, Type type),直接使用帶參數(shù)占位符的SQL語句作為條件,并指定參數(shù)值。
Order實例代表一個排序標(biāo)準(zhǔn),Order有如下構(gòu)造器:
Order(String propertyName, boolean ascending),根據(jù)propertyName排序,是否采用升序,如果后一個參數(shù)為true,采用升序排序,否則采用降序排序。
如果需要使用關(guān)聯(lián)類的屬性來增加查詢條件,則應(yīng)該對屬性再次使用createCriteria方法。看如下示例:
session.createCriteria(Person.class)
??? .add(Restrictions.like("name" , "dd%"))
??? .createCriteria("addresses")
??? .add(Restrictions.like("addressdetail" , "上海%"))
??? .list();
上面的代碼表示建立Person類的條件查詢,第一個查詢條件是直接過濾Person的屬性,即選出name屬性以dd開始的Person實例,第二個查詢條件則過濾Person關(guān)聯(lián)實例的屬性,其中addresses是Person類的關(guān)聯(lián)持久化類Address,而addressdetail則是Address類的屬性。值得注意的是,查詢并不是查詢Address持久化類,而是查詢Person持久化類。
注意:使用關(guān)聯(lián)類的條件查詢,依然是查詢原有持久化類的實例,而不是查詢被關(guān)聯(lián)類的實例。
posted @
2009-07-19 08:59 jadmin 閱讀(148) |
評論 (0) |
編輯 收藏
4.3 使用HQL查詢
Hibernate提供了異常強(qiáng)大的查詢體系,使用Hibernate有多種查詢方式??梢赃x擇使用Hibernate的HQL查詢,或者使用條件查詢,甚至可以使用原生的SQL查詢語句,此外還提供了一種數(shù)據(jù)過濾功能,這些都可用于篩選目標(biāo)數(shù)據(jù)。
下面分別介紹Hibernate的4種數(shù)據(jù)篩選方法:
4.3.1 HQL查詢
HQL是Hibernate Query Language的縮寫,HQL的語法很像SQL的語法,但HQL是一種面向?qū)ο蟮牟樵冋Z言。因此,SQL的操作對象是數(shù)據(jù)表和列等數(shù)據(jù)對象,而HQL的操作對象是類、實例、屬性等。
HQL是完全面向?qū)ο蟮牟樵冋Z言,因此可以支持繼承和多態(tài)等特征。
HQL查詢依賴于Query類,每個Query實例對應(yīng)一個查詢對象。使用HQL查詢可按如下步驟進(jìn)行:
(1)獲取Hibernate Session對象;
(2)編寫HQL語句;
(3)以HQL語句作為參數(shù),調(diào)用Session的createQuery方法創(chuàng)建查詢對象;
(4)如果HQL語句包含參數(shù),調(diào)用Query的setXxx方法為參數(shù)賦值;
(5)調(diào)用Query對象的list等方法遍歷查詢結(jié)果。
看下面的查詢示例:
public class HqlQuery
{
??? public static void main(String[] args)throws Exception
??? {
??????? HqlQuery mgr = new HqlQuery();
??????? //調(diào)用查詢方法
??????? mgr.findPersons();
??????? //調(diào)用第二個查詢方法
??????? mgr.findPersonsByHappenDate();
??????? HibernateUtil.sessionFactory.close();
??? }
??? //第一個查詢方法
??? private void findPersons()
??? {
??????? //獲得Hibernate Session
??????? Session sess = HibernateUtil.currentSession();
??????? //開始事務(wù)
??????? Transaction tx = sess.beginTransaction();
??????? //以HQL語句創(chuàng)建Query對象.
??????? //執(zhí)行setString方法為HQL語句的參數(shù)賦值
??????? //Query調(diào)用list方法訪問查詢的全部實例
??????? List pl = sess.createQuery("from Person p where p.myEvents.title
??????? = :eventTitle")
??????????????????????? .setString("eventTitle","很普通事情")
??????????????????????? .list();
??????? //遍歷查詢的全部結(jié)果
??????? for (Iterator pit = pl.iterator() ; pit.hasNext(); )
??????? {
??????????? Person p = ( Person )pit.next();
??????????? System.out.println(p.getName());
??????? }
??????? //提交事務(wù)
??? ??? tx.commit();
??????? HibernateUtil.closeSession();
??? }
??? //第二個查詢方法
??? private void findPersonsByHappenDate()throws Exception
??? {
??????? //獲得Hibernate Session對象
??????? Session sess = HibernateUtil.currentSession();
??????? Transaction tx = sess.beginTransaction();
??????? //解析出Date對象
??????? SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
??????? Date start = sdf.parse("2005-01-01");
??????? System.out.println("系統(tǒng)開始通過日期查找人" + start);
??????? //通過Session的createQuery方法創(chuàng)建Query對象
??????? //設(shè)置參數(shù)
??????? //返回結(jié)果集
??????? List pl = sess.createQuery(
??????????? "from Person p where p.myEvents.happenDate between :firstDate
??????????? and :endDate")
??????????????????????? .setDate("firstDate",start)
??????????????????????? .setDate("endDate",new Date())
??????????????????????? .list();
??????? //遍歷結(jié)果集
??????? for (Iterator pit = pl.iterator() ; pit.hasNext(); )
??? ??? {
??????? ??? Person p = ( Person )pit.next();
??????????? System.out.println(p.getName());
??? ??? }
??????? tx.commit();
??????? HibernateUtil.closeSession();
??? }
}
通過上面的示例程序,可看出查詢步驟基本相似。Query對象可以連續(xù)多次設(shè)置參數(shù),這得益于Hibernate Query的設(shè)計。
通常,setXxx方法的返回值都是void,但Hibernate Query的setXxx方法返回值是Query本身。因此,程序通過Session創(chuàng)建Query后,直接多次調(diào)用setXxx方法為HQL語句的參數(shù)賦值,再直接調(diào)用list方法返回查詢到的全部結(jié)果即可。
Query還包含兩個方法:
?? ● setFirstResult(int firstResult),設(shè)置返回的結(jié)果集從第幾條記錄開始。
?? ● setMaxResults(int maxResults),設(shè)置本次查詢返回的結(jié)果數(shù)。
這兩個方法用于實現(xiàn)Hibernate分頁。
下面簡單介紹HQL語句的語法。
HQL語句本身是不區(qū)分大小寫的。也就是說,HQL語句的關(guān)鍵字和函數(shù)都是不區(qū)分大小寫的。但HQL語句中所使用的包名、類名、實例名和屬性名都區(qū)分大小寫。
4.3.2 HQL查詢的from子句
from子句是最簡單的HQL語句,也是最基本的HQL語句。from關(guān)鍵字后緊跟持久化類的類名。例如:
from Person
表明從Person持久化類中選出全部的實例。
大部分時候,推薦為該Person的每個實例起別名。例如:
from Person as p
在上面的HQL語句中,Person持久化類中的實例的別名為p,既然 p是實例名,因此也應(yīng)該遵守Java的命名規(guī)則:第一個單詞的首字母小寫,后面每個單詞的首字母大寫。
命名別名時,as關(guān)鍵字是可選的,但為了增加可讀性,建議保留。
from后還可同時出現(xiàn)多個持久化類,此時將產(chǎn)生一個笛卡兒積或跨表的連接。
4.3.3 HQL查詢的select子句
select子句用于確定選擇出的屬性,當(dāng)然select選擇的屬性必須是from后持久化類包含的屬性。例如:
select p.name from Person as p
select可以選擇任意屬性,不僅可以選擇持久化類的直接屬性,還可以選擇組件屬性包含的屬性,例如:
select p.name.firstName from Person as p
select也支持將選擇出的屬性存入一個List對象中,例如:
select new list(p.name , p.address) from Person as p
甚至可以將選擇出的屬性直接封裝成對象,例如:
select new ClassTest(p.name , p.address) from Person as p
前提是ClassTest支持p.name和p.address的構(gòu)造器,假如p.name的數(shù)據(jù)類型是?????????? String,p.address的數(shù)據(jù)類型是String,則ClassTest必須有如下的構(gòu)造器:
ClassTest(String s1, String s2)
select還支持給選中的表達(dá)式命名別名,例如:
select p.name as personName from Person as p
這種用法與new map結(jié)合使用更普遍。如:
select new map(p.name as personName) from Person as p
在這種情形下,選擇出的是Map結(jié)構(gòu),以personName為key,實際選出的值作為value。
4.3.4 HQL查詢的聚集函數(shù)
HQL也支持在選出的屬性上,使用聚集函數(shù)。HQL支持的聚集函數(shù)與SQL完全相同,有如下5個:
?? ● avg,計算屬性平均值。
?? ● count,統(tǒng)計選擇對象的數(shù)量。
?? ● max,統(tǒng)計屬性值的最大值
?? ● min,統(tǒng)計屬性值的最小值。
?? ● sum,計算屬性值的總和。
例如:
select count(*) from Person
select max(p.age) from Person as p
select子句還支持字符串連接符、算術(shù)運算符以及SQL函數(shù)。如:
select p.name || "" || p.address from Person as p
select子句也支持使用distinct和all關(guān)鍵字,此時的效果與SQL中的效果完全相同。
4.3.5 多態(tài)查詢
HQL語句被設(shè)計成能理解多態(tài)查詢,from后跟的持久化類名,不僅會查詢出該持久化類的全部實例,還會查詢出該類的子類的全部實例。
如下面的查詢語句:
from Person as p
該查詢語句不僅會查詢出Person的全部實例,還會查詢出Person的子類,如Teacher的全部實例,前提是Person和Teacher完成了正確的繼承映射。
HQL支持在from子句中指定任何Java類或接口,查詢會返回繼承了該類的持久化子類的實例或返回實現(xiàn)該接口的持久化類的實例。下面的查詢語句返回所有被持久化的對象:
from java.lang.Object o
如果Named接口有多個持久化類,下面的語句將返回這些持久化類的全部實例:
from Named as n
注意:后面的兩個查詢將需要多個SQL SELECT語句,因此無法使用order by子句對結(jié)果集進(jìn)行排序,從而,不允許對這些查詢結(jié)果使用Query.scroll()方法。
4.3.6 HQL查詢的where子句
where子句用于篩選選中的結(jié)果,縮小選擇的范圍。如果沒有為持久化實例命名別名,可以直接使用屬性名引用屬性。
如下面的HQL語句:
from Person where name like 'tom%'
上面HQL語句與下面的語句效果相同:
from Person as p where p.name like "tom%"
在后面的HQL語句中,如果為持久化實例命名了別名,則應(yīng)該使用完整的屬性名。兩個HQL語句都可返回name屬性以tom開頭的實例。
復(fù)合屬性表達(dá)式加強(qiáng)了where子句的功能,例如如下HQL語句:
from Cat cat where cat.mate.name like "kit%"
該查詢將被翻譯成為一個含有內(nèi)連接的SQL查詢,翻譯后的SQL語句如下:
select * from cat_table as table1 cat_table as table2 where table1.mate =
table2.id and table1.name like "kit%"
再看下面的HQL查詢語句:
from Foo foo where foo.bar.baz.customer.address.city like"guangzhou%"
翻譯成SQL查詢語句,將變成一個四表連接的查詢。
=運算符不僅可以被用來比較屬性的值,也可以用來比較實例:
from Cat cat, Cat rival where cat.mate = rival.mate
select cat, mate
from Cat cat, Cat mate
where cat.mate = mate
特殊屬性(小寫)id可以用來表示一個對象的標(biāo)識符。(也可以使用該對象的屬性名。)
from Cat as cat where cat.id = 123
from Cat as cat where cat.mate.id = 69
第二個查詢是一個內(nèi)連接查詢,但在HQL查詢語句下,無須體會多表連接,而完全使用面向?qū)ο蠓绞降牟樵儭?/p>
id也可代表引用標(biāo)識符。例如,Person類有一個引用標(biāo)識符,它由country屬性 與medicareNumber兩個屬性組成。
下面的HQL語句有效:
from Person as person
where person.id.country = 'AU'
??? and person.id.medicareNumber = 123456
from Account as account
where account.owner.id.country = 'AU'
??? and account.owner.id.medicareNumber = 123456
第二個查詢跨越兩個表Person和Account。是一個多表連接查詢,但此處感受不到多表連接查詢的效果。
在進(jìn)行多態(tài)持久化的情況下,class關(guān)鍵字用來存取一個實例的鑒別值(discriminator value)。嵌入where子句中的Java類名,將被作為該類的鑒別值。例如:
from Cat cat where cat.class = DomesticCat
where子句中的屬性表達(dá)式必須以基本類型或java.lang.String結(jié)尾,不要使用組件類型屬性結(jié)尾,例如Account有Person屬性,而Person有Name屬性,Name有firstName屬性。
看下面的情形:
from Account as a where a.person.name.firstName like "dd%" //正確
from Account as a where a.person.name like "dd%" //錯誤
4.3.7 表達(dá)式
HQL的功能非常豐富,where子句后支持的運算符異常豐富,不僅包括SQL的運算符,還包括EJB-QL的運算符等。
where子句中允許使用大部分SQL支持的表達(dá)式:
?? ● 數(shù)學(xué)運算符+、–、*、/?等。
?? ● 二進(jìn)制比較運算符=、>=、<=、<>、!=、like等。
?? ● 邏輯運算符and、or、not等。
?? ● in、not in、between、is null、is not null、is empty、is not empty、member of和not member of等。
?? ● 簡單的case、case ... when ... then ... else ... end和case、case when ... then ... else ...?????? end等。
?? ● 字符串連接符value1 || value2或使用字符串連接函數(shù)concat(value1 , value2)。
?? ● 時間操作函數(shù)current_date()、current_time()、current_timestamp()、second()、minute()、hour()、day()、month()、year()等。
?? ● HQL還支持EJB-QL 3.0所支持的函數(shù)或操作substring()、trim()、lower()、upper()、length()、locate()、abs()、sqrt()、bit_length()、coalesce()和nullif()等。
?? ● 還支持?jǐn)?shù)據(jù)庫的類型轉(zhuǎn)換函數(shù),如cast(... as ...),第二個參數(shù)是Hibernate的類型名,或者extract(... from ...),前提是底層數(shù)據(jù)庫支持ANSI cast()?和extract()。
?? ● 如果底層數(shù)據(jù)庫支持如下單行函數(shù)sign()、trunc()、rtrim()、sin()。則HQL語句也完全可以支持。
?? ● HQL語句支持使用?作為參數(shù)占位符,這與JDBC的參數(shù)占位符一致,也可使用命名參數(shù)占位符號,方法是在參數(shù)名前加冒號 :,例如 :start_date和:x1等。
?? ● 當(dāng)然,也可在where子句中使用SQL常量,例如'foo'、69、'1970-01-01 10:00:???????? 01.0'等。
?? ● 還可以在HQL語句中使用Java public static final 類型的常量,例如eg.Color.TABBY。
除此之外,where子句還支持如下的特殊關(guān)鍵字用法。
?? ● in與between...and可按如下方法使用:
from DomesticCat cat where cat.name between 'A' and 'B'
from DomesticCat cat where cat.name in ( 'Foo','Bar','Baz')
?? ● 當(dāng)然,也支持not in和not between...and的使用,例如:
from DomesticCat cat where cat.name not between 'A' and 'B'
from DomesticCat cat where cat.name not in ( 'Foo','Bar','Baz' )
?? ● 子句is null與is not null可以被用來測試空值,例如:
from DomesticCat cat where cat.name is null;
from Person as p where p.address is not null;
如果在Hibernate配置文件中進(jìn)行如下聲明:
<property name="hibernate.query.substitutions">true 1, false 0</property>
上面的聲明表明,HQL轉(zhuǎn)換SQL語句時,將使用字符1和0來取代關(guān)鍵字true和false。然后將可以在表達(dá)式中使用布爾表達(dá)式,例如:
from Cat cat where cat.alive = true
?? ● size關(guān)鍵字用于返回一個集合的大小,例如:
from Cat cat where cat.kittens.size > 0
from Cat cat where size(cat.kittens) > 0
?? ● 對于有序集合,還可使用minindex與maxindex函數(shù)代表最小與最大的索引序數(shù)。同理,可以使用minelement與maxelement函數(shù)代表集合中最小與最大的元素。???????? 例如:
from Calendar cal where maxelement(cal.holidays) > current date
from Order order where maxindex(order.items) > 100
from Order order where minelement(order.items) > 10000
?? ● 可以使用SQL函數(shù)any、some、all、exists、in操作集合里的元素,例如:
//操作集合元素
select mother from Cat as mother, Cat as kit
where kit in elements(foo.kittens)
//p的name屬性等于集合中某個元素的name屬性
select p from NameList list, Person p
where p.name = some elements(list.names)
//操作集合元素
from Cat cat where exists elements(cat.kittens)
from Player p where 3 > all elements(p.scores)
from Show show where 'fizard' in indices(show.acts)
注意這些結(jié)構(gòu)變量size、elements、indices、minindex、maxindex、minelement、maxelement 等,只能在where子句中使用。
?? ● where子句中,有序集合的元素(arrays, lists, maps)可以通過[ ]運算符訪問。例如:
//items是有序集合屬性,items[0]代表第一個元素
from Order order where order.items[0].id = 1234
//holidays是map集合屬性,holidays[national day]代表其中一個元素
select person from Person person, Calendar calendar
where calendar.holidays['national day'] = person.birthDay
and person.nationality.calendar = calendar
//下面同時使用list 集合和map集合屬性
select item from Item item, Order order
where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11
select item from Item item, Order order
where order.items[ maxindex(order.items) ] = item and order.id = 11
在[]中的表達(dá)式甚至可以是一個算術(shù)表達(dá)式,例如:
select item from Item item, Order order
where order.items[ size(order.items) - 1 ] = item
借助于HQL,可以大大簡化選擇語句的書寫,提高查詢語句的可讀性,看下面的HQL語句:
select cust
from Product prod,
??? Store store
??? inner join store.customers cust
where prod.name = 'widget'
??? and store.location.name in ( 'Melbourne', 'Sydney' )
??? and prod = all elements(cust.currentOrder.lineItems)
如果翻譯成SQL語句,將變成如下形式:
SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order
FROM customers cust,
??? stores store,
??? locations loc,
??? store_customers sc,
??? product prod
WHERE prod.name = 'widget'
??? AND store.loc_id = loc.id
??? AND loc.name IN ( 'Melbourne', 'Sydney' )
??? AND sc.store_id = store.id
??? AND sc.cust_id = cust.id
??? AND prod.id = ALL(
??????? SELECT item.prod_id
??????? FROM line_items item, orders o
??????? WHERE item.order_id = o.id
??????????? AND cust.current_order = o.id
??? )
4.3.8 order by子句
查詢返回的列表(list)可以根據(jù)類或組件屬性的任何屬性進(jìn)行排序,例如:
from Person as p
order by p.name, p.age
還可使用asc或desc關(guān)鍵字指定升序或降序的排序規(guī)則,例如:
from Person as p
order by p.name asc , p.age desc
如果沒有指定排序規(guī)則,默認(rèn)采用升序規(guī)則。即是否使用asc關(guān)鍵字是沒有區(qū)別的,加asc是升序排序,不加asc也是升序排序。
4.3.9 group by子句
返回聚集值的查詢可以對持久化類或組件屬性的屬性進(jìn)行分組,分組所使用的group by子句??聪旅娴腍QL查詢語句:
select cat.color, sum(cat.weight), count(cat)
from Cat cat
group by cat.color
類似于SQL的規(guī)則,出現(xiàn)在select后的屬性,要么出現(xiàn)在聚集函數(shù)中,要么出現(xiàn)在group by的屬性列表中??聪旅媸纠?/p>
//select后出現(xiàn)的id出現(xiàn)在group by之后,而name屬性則出現(xiàn)在聚集函數(shù)中
select foo.id, avg(name), max(name)
from Foo foo join foo.names name
group by foo.id
having子句用于對分組進(jìn)行過濾,如下:
select cat.color, sum(cat.weight), count(cat)
from Cat cat
group by cat.color
having cat.color in (eg.Color.TABBY, eg.Color.BLACK)
注意:having子句用于對分組進(jìn)行過濾,因此having子句只能在有g(shù)roup by子句時才可以使用,沒有g(shù)roup by子句,不能使用having子句。
Hibernate的HQL語句會直接翻譯成數(shù)據(jù)庫SQL語句。因此,如果底層數(shù)據(jù)庫支持的having子句和group by子句中出現(xiàn)一般函數(shù)或聚集函數(shù),HQL語句的having子句和order by 子句中也可以出現(xiàn)一般函數(shù)和聚集函數(shù)。
例如:
select cat
from Cat cat
join cat.kittens kitten
group by cat
having avg(kitten.weight) > 100
order by count(kitten) asc, sum(kitten.weight) desc
注意:group by子句與 order by子句中都不能包含算術(shù)表達(dá)式。
4.3.10 子查詢
如果底層數(shù)據(jù)庫支持子查詢,則可以在HQL語句中使用子查詢。與SQL中子查詢相似的是,HQL中的子查詢也需要使用()括起來。如:
from Cat as fatcat
where fatcat.weight > ( select avg(cat.weight) from DomesticCat cat )
如果select中包含多個屬性,則應(yīng)該使用元組構(gòu)造符:
from Cat as cat
where not ( cat.name, cat.color ) in (
??? select cat.name, cat.color from DomesticCat cat
)
4.3.11 fetch關(guān)鍵字
對于集合屬性,Hibernate默認(rèn)采用延遲加載策略。例如,對于持久化類Person,有集合屬性scores。加載Person實例時,默認(rèn)不加載scores屬性。如果Session被關(guān)閉,Person實例將無法訪問關(guān)聯(lián)的scores屬性。
為了解決該問題,可以在Hibernate映射文件中取消延遲加載或使用fetch join,例如:
from Person as p join p.scores
上面的fetch語句將會初始化person的scores集合屬性。
如果使用了屬性級別的延遲獲取,可以使用fetch all properties來強(qiáng)制Hibernate立即抓取那些原本需要延遲加載的屬性,例如:
from Document fetch all properties order by name
from Document doc fetch all properties where lower(doc.name) like '%cats%'
4.3.12 命名查詢
HQL查詢還支持將查詢所用的HQL語句放入配置文件中,而不是代碼中。通過這種方式,可以大大提供程序的解耦。
使用query元素定義命名查詢,下面是定義命名查詢的配置文件片段:
<!-- 定義命名查詢 -->
<query name="myNamedQuery">
??? <!-- 此處確定命名查詢的HQL語句 -->
??? from Person as p where p.age > ?
</query>
該命名的HQL查詢可以直接通過Session訪問,調(diào)用命名查詢的示例代碼如下:
private void findByNamedQuery()throws Exception
{
??? //獲得Hibernate Session對象
??? Session sess = HibernateUtil.currentSession();
??? //開始事務(wù)
??? Transaction tx = sess.beginTransaction();
??? System.out.println("執(zhí)行命名查詢");
??? //調(diào)用命名查詢
??? List pl = sess.getNamedQuery("myNamedQuery")
??????????????????????? //為參數(shù)賦值
??????????????????? ?? .setInteger(0 , 20)
??????????????????????? //返回全部結(jié)果
??????????????????? ?? .list();
??? //遍歷結(jié)果集
??? for (Iterator pit = pl.iterator() ; pit.hasNext(); )
??? {
??????? Person p = ( Person )pit.next();
??????? System.out.println(p.getName());
??? }
??? //提交事務(wù)
??? tx.commit();
??? HibernateUtil.closeSession();
}
posted @
2009-07-19 08:48 jadmin 閱讀(1604) |
評論 (0) |
編輯 收藏
Hibernate的批量處理
Hibernate完全以面向?qū)ο蟮姆绞絹聿僮鲾?shù)據(jù)庫,當(dāng)程序里以面向?qū)ο蟮姆绞讲僮鞒志没瘜ο髸r,將被自動轉(zhuǎn)換為對數(shù)據(jù)庫的操作。例如調(diào)用Session的delete()方法來刪除持久化對象,Hibernate將負(fù)責(zé)刪除對應(yīng)的數(shù)據(jù)記錄;當(dāng)執(zhí)行持久化對象的set方法時,Hibernate將自動轉(zhuǎn)換為對應(yīng)的update方法,修改數(shù)據(jù)庫的對應(yīng)記錄。
問題是如果需要同時更新100?000條記錄,是不是要逐一加載100?000條記錄,然后依次調(diào)用set方法——這樣不僅繁瑣,數(shù)據(jù)訪問的性能也十分糟糕。對這種批量處理的場景,Hibernate提供了批量處理的解決方案,下面分別從批量插入、批量更新和批量刪除3個方面介紹如何面對這種批量處理的情形。
1) 批量插入
如果需要將100?000條記錄插入數(shù)據(jù)庫,通常Hibernate可能會采用如下做法:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
??? User u = new User (.....);
??? session.save(customer);
}
tx.commit();
session.close();
但隨著這個程序的運行,總會在某個時候運行失敗,并且拋出OutOfMemoryException(內(nèi)存溢出異常)。這是因為Hibernate的Session持有一個必選的一級緩存,所有的User實例都將在Session級別的緩存區(qū)進(jìn)行了緩存的緣故。
為了解決這個問題,有個非常簡單的思路:定時將Session緩存的數(shù)據(jù)刷新入數(shù)據(jù)庫,而不是一直在Session級別緩存。可以考慮設(shè)計一個累加器,每保存一個User實例,累加器增加1。根據(jù)累加器的值決定是否需要將Session緩存中的數(shù)據(jù)刷入數(shù)據(jù)庫。
下面是增加100?000個User實例的代碼片段:
private void testUser()throws Exception
{
??? //打開Session
??? Session session = HibernateUtil.currentSession();
??? //開始事務(wù)
??? Transaction tx = session.beginTransaction();
??? //循環(huán)100 000次,插入100 000條記錄
??? for (int i = 0 ; i < 1000000 ; i++ )
??? {
??????? //創(chuàng)建User實例
??????? User u1 = new User();
??????? u1.setName("xxxxx" + i);
??????? u1.setAge(i);
??????? u1.setNationality("china");
??????? //在Session級別緩存User實例
??????? session.save(u1);
??????? //每當(dāng)累加器是20的倍數(shù)時,將Session中的數(shù)據(jù)刷入數(shù)據(jù)庫,并清空Session緩存
??????? if (i % 20 == 0)
??????? {
??????????? session.flush();
??????????? session.clear();
??????????? tx.commit();
??????????? tx = session.beginTransaction();
??????? }
??? }
??? //提交事務(wù)
??? tx.commit();
??? //關(guān)閉事務(wù)
??? HibernateUtil.closeSession();
}
上面代碼中,當(dāng)i%20 == 0時,手動將Session處的緩存數(shù)據(jù)寫入數(shù)據(jù)庫,并手動提交事務(wù)。如果不提交事務(wù),數(shù)據(jù)將依然緩存在事務(wù)處——未進(jìn)入數(shù)據(jù)庫,也將引起內(nèi)存溢出的異常。
這是對Session級別緩存的處理,還應(yīng)該通過如下配置來關(guān)閉SessionFactory的二級????? 緩存。
hibernate.cache.use_second_level_cache false
注意:除了要手動清空Session級別的緩存外,最好關(guān)閉SessionFactory級別的二級緩存。否則,即使手動清空Session級別的緩存,但因為在SessionFactory級別還有緩存,也可能引發(fā)異常。
2) 批量更新
上面介紹的方法同樣適用于批量更新數(shù)據(jù),如果需要返回多行數(shù)據(jù),可以使用scroll()方法,從而可充分利用服務(wù)器端游標(biāo)所帶來的性能優(yōu)勢。下面是進(jìn)行批量更新的代碼片段:
private void testUser()throws Exception
{
??? //打開Session
??? Session session = HibernateUtil.currentSession();
??? //開始事務(wù)
??? Transaction tx = session.beginTransaction();
??? //查詢出User表中的所有記錄
??? ScrollableResults users = session.createQuery("from User")
??????? .setCacheMode(CacheMode.IGNORE)
??????? .scroll(ScrollMode.FORWARD_ONLY);
??? int count=0;
??? //遍歷User表中的全部記錄
??? while ( users.next() )
??? {
??????? User u = (User) users.get(0);
??????? u.setName("新用戶名" + count);
??????? //當(dāng)count為20的倍數(shù)時,將更新的結(jié)果從Session中flush到數(shù)據(jù)庫
??????? if ( ++count % 20 == 0 )
??????? {
??????????? session.flush();
??????????? session.clear();
??????? }
??? }
??? tx.commit();
??? HibernateUtil.closeSession();
}
通過這種方式,雖然可以執(zhí)行批量更新,但效果非常不好。執(zhí)行效率不高,而且需要先執(zhí)行數(shù)據(jù)查詢,然后再執(zhí)行數(shù)據(jù)更新,并且這種更新將是逐行更新,即每更新一行記錄,都需要執(zhí)行一條update語句,性能非常低下。
為了避免這種情況,Hibernate提供了一種類似于SQL的批量更新和批量刪除的HQL語法。
3) SQL風(fēng)格的批量更新/刪除
Hibernate提供的HQL語句也支持批量的UPDATE和DELETE語法。
批量UPDATE和DELETE語句的語法格式如下:
UPDATE | DELETE FROM? ClassName [WHERE WHERE_CONDITIONS]
關(guān)于上面的語法格式有以下四點值得注意:
?? ● 在FROM子句中,F(xiàn)ROM關(guān)鍵字是可選的。即完全可以不寫FROM關(guān)鍵字。
?? ● 在FROM子句中只能有一個類名,該類名不能有別名。
?? ● 不能在批量HQL語句中使用連接,顯式的或隱式的都不行。但可以在WHERE子句中使用子查詢。
?? ● 整個WHERE子句是可選的。
假設(shè),需要批量更改User類實例的name屬性,可以采用如下代碼片段完成:
private void testUser()throws Exception
{
??? //打開Session
??? Session session = HibernateUtil.currentSession();
??? //開始事務(wù)
??? Transaction tx = session.beginTransaction();
??? //定義批量更新的HQL語句
??? String hqlUpdate = "update User set name = :newName";
??? //執(zhí)行更新
??? int updatedEntities = session.createQuery( hqlUpdate )
??????????????????????? ?? .setString( "newName", "新名字" )
??????????????????????? ?? .executeUpdate();
??? //提交事務(wù)
??? tx.commit();
??? HibernateUtil.closeSession();
}
從上面代碼中可以看出,這種語法非常類似于PreparedStatement的executeUpdate語法。實際上,HQL的這種批量更新就是直接借鑒了SQL語法的UPDATE語句。
注意:使用這種批量更新語法時,通常只需要執(zhí)行一次SQL的UPDATE語句,就可以完成所有滿足條件記錄的更新。但也可能需要執(zhí)行多條UPDATE語句,這是因為有繼承映射等特殊情況,例如有一個Person實例,它有Customer的子類實例。當(dāng)批量更新Person實例時,也需要更新Customer實例。如果采用joined-subclass或union-subclass映射策略,Person和Customer實例保存在不同的表中,因此可能需要多條UPDATE語句。
執(zhí)行一個HQL DELETE,同樣使用 Query.executeUpdate() 方法,下面是一次刪除上面全部記錄的代碼片段:
private void testUser()throws Exception
{
??? //打開Session實例
??? Session session = HibernateUtil.currentSession();
??? //開始事務(wù)
??? Transaction tx = session.beginTransaction();
??? //定義批量刪除的HQL語句
??? String hqlUpdate = "delete User";
??? //執(zhí)行批量刪除
??? int updatedEntities = session.createQuery( hqlUpdate )
??????????????????????? ?? .executeUpdate();
??? //提交事務(wù)
??? tx.commit();
??? //關(guān)閉Session
??? HibernateUtil.closeSession();
}
由Query.executeUpdate()方法返回一個整型值,該值是受此操作影響的記錄數(shù)量。實際上,Hibernate的底層操作是通過JDBC完成的。因此,如果有批量的UPDATE或DELETE操作被轉(zhuǎn)換成多條UPDATE或DELETE語句,該方法返回的是最后一條SQL語句影響的記錄行數(shù)。
posted @
2009-07-19 08:42 jadmin 閱讀(77) |
評論 (0) |
編輯 收藏
作為昔日開發(fā)語言的王者,Java已經(jīng)有14歲了,它創(chuàng)新變革的腳步一直沒有停下來。現(xiàn)在Java已經(jīng)不單單是一種計算機(jī)語言,Java更是一個平臺,一個社區(qū),以及一個生態(tài)系統(tǒng)。在2009年,Oracle收購了SUN,Java留下的,只是一個王者的背影。
JavaSE
目前JDK的正式版本是JDK 6 Update 12。JDK 6 Update 10以來的版本,關(guān)鍵功能包括:
Java內(nèi)核大大縮小了,由原來的大約十幾兆縮小到4兆,這樣提高了啟動Java程序的速度。而其它的Java庫在需要的時候可以后臺下載,這樣也縮短了等待和安裝的時間。
下一代的Plug-in架構(gòu)。Applet運行在自己的進(jìn)程中,而不再依賴瀏覽器,提高了性能和可伸縮性。在2008 JavaOne有一段很酷的演示,就是將Applet直接從瀏覽器中拖拽到桌面上,或者從桌面上拖到瀏覽器中。這個功能的實現(xiàn)有賴于JDK 6中重寫了連接Java運行環(huán)境和瀏覽器的代碼。這個新的Plug-in架構(gòu)還提供一個有意思的功能,可以在Web頁面通過JavaScript調(diào)用Web頁面上任何的Applet,不管這個Applet是用哪種語言寫的,比如JavaFX Script, JRuby, Jython。
這里要提到JDK 6中Java虛擬機(jī)(JVM)對動態(tài)語言的支持(JSR 223)。這個框架可以使Java應(yīng)用程序中支持腳本引擎,這樣各種腳本語言就可以運行在JVM上。JVM發(fā)展的一個重要方向就是去掉“J”,讓JVM成為能支持各種語言的,全能的“VM(虛擬機(jī))”。目前JVM支持的腳本語言包括:JavaFX Script, Groovy, JRuby, Jython, JavaScript, Scala, Clojure??梢灶A(yù)計到的是,將來會有更多的腳本語言運行在Java虛擬機(jī)上。
還在草案階段的JDK7,一些新特性也值得期待:
一個是實現(xiàn)JDK 7模塊化(JSR 294, JSR 277),將與OSGi聯(lián)盟更緊密的配合,以便JSR 294模塊可以被OSGi所使用。
另外一個是并行包。這個包致力于通過充分利用底層硬件來達(dá)到真正的并發(fā)。隨著硬件多核系統(tǒng)的廣泛應(yīng)用,并行計算的需求對Java的性能提出了更高要求。
Java EE
Java EE 6 (JSR 316)的公眾審議將在2009年2月23號結(jié)束。Java EE 6繼承了Java EE 5改進(jìn)的目標(biāo),就是簡化開發(fā),另外,還增加了一個目標(biāo),更好地滿足開發(fā)人員的需求。關(guān)于Java EE 6的討論持續(xù)了很長時間,而爭議最大的就是Java EE 6引入的Profile。
Java EE十年來的發(fā)展結(jié)果,是這個平臺變得越來越龐大,但對很多用戶和開發(fā)者來說,也許他只需要使用眾多功能中的很小一部分,卻不得不安裝整個平臺。Profile就是為解決這個問題而定義的。Profile實際上是Java EE API的子集。討論最熱烈的Web Profile就集中在,哪些API應(yīng)該被放在標(biāo)準(zhǔn)Profile中?
Jave EE 6包括了一系列的新技術(shù)和升級,篇幅所限制,僅羅列一些名詞:WebBean 1.0, JSF 2.0, EJB 3.1, JPA 2.0, Servlet 3.0, JAX-RS 1.1。
Java ME
Mobile Service Architecture 2 (MSA 2)目前已經(jīng)到了公眾審議的尾聲(JSR 249)。預(yù)計2009年,MSA 2將進(jìn)入實用階段。MSA 2是下一代Java ME平臺技術(shù),提供了更多移動開發(fā)的新特性,比如可以訪問手持設(shè)備上的各種傳感器,如加速計傳感器,電池容量(JSR256);可以在手機(jī)上看電視(JSR 272);如同信用卡支付功能的手機(jī)錢包(JSR 257);使用XML,腳本,與Java一同構(gòu)造GUI (JSR 290);通過手機(jī)使用VOIP服務(wù)(JSR 281)。
Java FX
Java在企業(yè)應(yīng)用程序的開發(fā)中一直占主導(dǎo)地位,但現(xiàn)在面向消費者的富互聯(lián)網(wǎng)應(yīng)用(Rich Internet Application, RIA)軟件數(shù)量在急劇增加,這種情形下,JavaFX應(yīng)運而生。和Java語言相比,JavaFX Script更適合開發(fā)高效,快速地開發(fā)集合各種媒體,交互性強(qiáng),界面吸引用戶的RIA應(yīng)用程序。2009年2月,JavaFX SDK 1.1與JavaFX Mobile都正式發(fā)布了,下一個要期待的是JavaFX TV。借助Java這個強(qiáng)大的平臺,JavaFX目標(biāo)是提供給開發(fā)者更好的RIA平臺與技術(shù),除了繼續(xù)要在傳統(tǒng)的PC桌面保持優(yōu)勢外,更是面向未來的終端設(shè)備,手機(jī)和電視。
JAVA是有SUN公司開發(fā)的新一代編程語言,它可以用在各種不同的機(jī)器、操作系統(tǒng)的網(wǎng)絡(luò)環(huán)境中進(jìn)行開發(fā)。不論你使用哪種瀏覽器或者使用哪種操作系統(tǒng)(Windows、Unix等等),只要瀏覽器支持JAVA,你就可以看到生動的主頁。JAVA正在逐步成為Internet應(yīng)用的主要開發(fā)語言,它徹底改變了應(yīng)用軟件的開發(fā)模式,為迅速發(fā)展的信息世界增添了新的活力。所以作為Internet應(yīng)用的開發(fā)技術(shù)人員不可不看JAVA,而JAVA程序不可不先從基礎(chǔ)學(xué)起。
希望Java能夠繼續(xù)發(fā)展下去,作為一個影響著整個業(yè)界和無數(shù)技術(shù)人員的開發(fā)語言,Java不應(yīng)該僅僅是給我們留下一個背影而已。
posted @
2009-07-07 15:30 jadmin 閱讀(87) |
評論 (0) |
編輯 收藏
struts.action.extension
????????? The URL extension to use to determine if the request is meant for a Struts action
?????????? 用URL擴(kuò)展名來確定是否這個請求是被用作Struts action,其實也就是設(shè)置 action的后綴,例如login.do的'do'字。
struts.configuration
????????? The org.apache.struts2.config.Configuration implementation class
??????????? org.apache.struts2.config.Configuration接口名
struts.configuration.files
????????? A list of configuration files automatically loaded by Struts
?????????? struts自動加載的一個配置文件列表
struts.configuration.xml.reload
????????? Whether to reload the XML configuration or not
?????????? 是否加載xml配置(true,false)
struts.continuations.package
?????????? The package containing actions that use Rife continuations
?????????? 含有actions的完整連續(xù)的package名稱
struts.custom.i18n.resources
????????? Location of additional localization properties files to load
?????????? 加載附加的國際化屬性文件(不包含.properties后綴)
struts.custom.properties
????????? Location of additional configuration properties files to load
?????????? 加載附加的配置文件的位置
struts.devMode
????????? Whether Struts is in development mode or not
?????????? 是否為struts開發(fā)模式
struts.dispatcher.parametersWorkaround
????????? Whether to use a Servlet request parameter workaround necessary for some versions of WebLogic
??????????? (某些版本的weblogic專用)是否使用一個servlet請求參數(shù)工作區(qū)(PARAMETERSWORKAROUND)
struts.enable.DynamicMethodInvocation
????????? Allows one to disable dynamic method invocation from the URL
??????????? 允許動態(tài)方法調(diào)用
struts.freemarker.manager.classname
????????? The org.apache.struts2.views.freemarker.FreemarkerManager implementation class
?????????? org.apache.struts2.views.freemarker.FreemarkerManager接口名
struts.i18n.encoding
????????? The encoding to use for localization messages
?????????? 國際化信息內(nèi)碼
struts.i18n.reload
????????? Whether the localization messages should automatically be reloaded
?????????? 是否國際化信息自動加載
struts.locale
????????? The default locale for the Struts application
?????????? 默認(rèn)的國際化地區(qū)信息
struts.mapper.class
????????? The org.apache.struts2.dispatcher.mapper.ActionMapper implementation class
??????????? org.apache.struts2.dispatcher.mapper.ActionMapper接口
struts.multipart.maxSize
????????? The maximize size of a multipart request (file upload)
?????????? multipart請求信息的最大尺寸(文件上傳用)
struts.multipart.parser
????????? The org.apache.struts2.dispatcher.multipart.
????????? MultiPartRequest parser implementation for a multipart request (file upload)
????????? 專為multipart請求信息使用的org.apache.struts2.dispatcher.multipart.MultiPartRequest解析器接口(文件上傳用)
struts.multipart.saveDir
????????? The directory to use for storing uploaded files
?????????? 設(shè)置存儲上傳文件的目錄夾
struts.objectFactory
????????? The com.opensymphony.xwork2.ObjectFactory implementation class
?????????? com.opensymphony.xwork2.ObjectFactory接口(spring)
struts.objectFactory.spring.autoWire
????????? Whether Spring should autoWire or not
?????????? 是否自動綁定Spring
struts.objectFactory.spring.useClassCache
????????? Whether Spring should use its class cache or not
?????????? 是否spring應(yīng)該使用自身的cache
struts.objectTypeDeterminer
????????? The com.opensymphony.xwork2.util.ObjectTypeDeterminer implementation class
??????????? com.opensymphony.xwork2.util.ObjectTypeDeterminer接口
struts.serve.static.browserCache
If static content served by the Struts filter should set browser caching header properties or not
?????????? 是否struts過濾器中提供的靜態(tài)內(nèi)容應(yīng)該被瀏覽器緩存在頭部屬性中
struts.serve.static
????????? Whether the Struts filter should serve static content or not
?????????? 是否struts過濾器應(yīng)該提供靜態(tài)內(nèi)容
struts.tag.altSyntax
????????? Whether to use the alterative syntax for the tags or not
?????????? 是否可以用替代的語法替代tags
struts.ui.templateDir
????????? The directory containing UI templates
?????????? UI templates的目錄夾
struts.ui.theme
????????? The default UI template theme
?????????? 默認(rèn)的UI template主題
struts.url.http.port
????????? The HTTP port used by Struts URLs
?????????? 設(shè)置http端口
struts.url.https.port
????????? The HTTPS port used by Struts URLs
?????????? 設(shè)置https端口
struts.url.includeParams
????????? The default includeParams method to generate Struts URLs
????????? 在url中產(chǎn)生 默認(rèn)的includeParams
struts.velocity.configfile
????????? The Velocity configuration file path
?????????? velocity配置文件路徑
struts.velocity.contexts
????????? List of Velocity context names
?????????? velocity的context列表
struts.velocity.manager.classname
????????? org.apache.struts2.views.velocity.VelocityManager implementation class
?????????? org.apache.struts2.views.velocity.VelocityManager接口名
struts.velocity.toolboxlocation
????????? The location of the Velocity toolbox
?????????? velocity工具盒的位置
struts.xslt.nocache
????????? Whether or not XSLT templates should not be cached
?????????? 是否XSLT模版應(yīng)該被緩存
摘自:http://li445970924.javaeye.com/blog/420056
posted @
2009-07-04 15:33 jadmin 閱讀(108) |
評論 (0) |
編輯 收藏