日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

session 中对象实例在不同事务中的状态

發(fā)布時(shí)間:2024/4/17 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 session 中对象实例在不同事务中的状态 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

不同事務(wù)中執(zhí)行hibernate query,則查詢(xún)出來(lái)的對(duì)象會(huì)在不同session中,或一個(gè)在托管態(tài),一個(gè)在session中管理,所以是不同實(shí)例。

?

?

如果在同一事務(wù)中的話,則多次query出來(lái)的對(duì)象實(shí)際上是同一個(gè)實(shí)例,當(dāng)你改變第一次query出來(lái)的對(duì)象時(shí),之后你再query出來(lái)的實(shí)例就會(huì)反映第一次的變化。

同一事務(wù)下:

1.

swapTrade = swapTradeService.getTradeByLqtId(incomingTrade.getLqttradeid());

swapTrade = positionConverter.toSwapTrade(incomingTrade, swapTrade);

2.

SwapTrade persistedTrade = swapTradeService.getTradeByLqtId(swapTrade.getLqttradeid());

?

persistedTrade 會(huì)反映swapTrade的變化。

?

========================

http://www.iteye.com/topic/1113462

用過(guò)Hibernate的人都知道Hibernate最原始的使用Session方式(異常忽略):

?

獲取SessionFactory

打開(kāi)Session

打開(kāi)事務(wù)(可選)

執(zhí)行操作

關(guān)閉事務(wù)(可選)

關(guān)閉Session

?

?

??? 當(dāng)然還有另外一個(gè)方法getCurrentSession() 這個(gè)方法就是通過(guò)SessionContext來(lái)減少Session創(chuàng)建的。比如常用的ThreadLocalSessionContext:

?

Java代碼 ?
  • Session?current?=?existingSession(?factory?); ??
  • if?(current?==?null)?{ ??
  • ????current?=?buildOrObtainSession(); ??
  • ????current.getTransaction().registerSynchronization(?buildCleanupSynch()?); ??
  • ????if?(?needsWrapping(?current?)?)?{ ??
  • ????????current?=?wrap(?current?); ??
  • ????} ??
  • ????doBind(?current,?factory?); ??
  • } ??
  • return?current;??
  • Session current = existingSession( factory ); if (current == null) {current = buildOrObtainSession();current.getTransaction().registerSynchronization( buildCleanupSynch() );if ( needsWrapping( current ) ) {current = wrap( current );}doBind( current, factory ); } return current;

    ?

    ???? ?? currentSession()內(nèi)部先從ThreadLocal中獲取Session。若不為null直接返回,若為null則openSession(...)一個(gè)Session并把這個(gè)Session對(duì)象綁定在ThreadLocal上。它就是通過(guò)在ThreadLocal中注冊(cè)綁定Session來(lái)確保線程中最多只有一個(gè)Session對(duì)象。

    ?

    ?

    上面的地球人都知道

    ==============================================================================

    ?

    Spring提供許多Template對(duì)各種底層ORM等進(jìn)行集成,如JdbcTemplate、HibernateTemplate、JpaTemplate等同時(shí)也提供了相應(yīng)的Dao模板類(lèi),如JdbcDaoSupport、HibernateDaoSupport、JpaDaoSupport等

    既然說(shuō)Spring對(duì)Hibernate的集成,就得看HibernateTemplate和HibernateDaoSupport這兩個(gè)類(lèi)。

    ???? 使用HibernateDaoSupport進(jìn)行數(shù)據(jù)操作時(shí)常用兩種方式訪問(wèn)Session:getSession()和HibernateCallback。

    注意:使用HibernateDaoSupport時(shí)候、如果再通過(guò)SessionFactory進(jìn)行g(shù)etCurrentSession()獲取Session的話就有可能出現(xiàn)問(wèn)題了。因?yàn)?span style="color:#ff0000">Spring的Bean工廠把Hibernate的SessionFactory動(dòng)了點(diǎn)小手腳 ~。就是前面說(shuō)到的ThreadLocalSessionContext被Spring替換為 SpringSessionContext !

    這個(gè)SpringSessionContext的currentSession()方法核心如下:
    ? ? return (org.hibernate.classic.Session) SessionFactoryUtils.doGetSession(this.sessionFactory, false );
    ? ? // 這個(gè)false硬性規(guī)定doGetSession如果在線程中獲取不到Session不自動(dòng)創(chuàng)建Session

    ?

    SessionFactoryUtils#doGetSession()是通過(guò)TransactionSynchronizationManager來(lái)獲取Session資源的,doGetSession()會(huì)通過(guò) TransactionSynchronizationManager訪問(wèn)線程資源、但是整個(gè)操作中沒(méi)有打開(kāi)事務(wù)的話、此方法會(huì)拋出異常:
    ???????? org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here

    ?

    ??? 這個(gè)異常就是出現(xiàn)在SessionFactoryUtils#doGetSession()方法的最后:

    Java代碼 ?
  • if?(!allowCreate?&&?!isSessionTransactional(session,?sessionFactory))?{ ??
  • ????closeSession(session); ??
  • ????throw?new?IllegalStateException("No?Hibernate?Session?bound?to?thread,?"?+ ??
  • ????????"and?configuration?does?not?allow?creation?of?non-transactional?one?here"); ??
  • }??
  • if (!allowCreate && !isSessionTransactional(session, sessionFactory)) {closeSession(session);throw new IllegalStateException("No Hibernate Session bound to thread, " +"and configuration does not allow creation of non-transactional one here"); }?

    ?

    ?

    ????? ? getSession和getCurrentSession實(shí)現(xiàn)過(guò)程差不多,但是精簡(jiǎn)了SessionContext那一段。并且可以設(shè)置allowCreate,如果設(shè)置為false,此方法應(yīng)該和getCurrentSession同樣效果。

    ?

    ?

    =========================================================================

    ?

    ?

    了解了這些之后、我進(jìn)行了一個(gè)HibernateTemplate和getSession之間的測(cè)試,主要就是測(cè)試

    單線程下多次重復(fù)請(qǐng)求創(chuàng)建Session與事務(wù)之間的關(guān)系、HibernateTemplate和getSession()兩者之間在功能實(shí)現(xiàn)和效率上有什么樣的不同。(多線程下卡的要死、不測(cè)試了)

    ?

    Dao層:testTS()方法——臨時(shí)創(chuàng)建用于測(cè)試的方法
    ??????????? 通過(guò)HibernateTemplate和直接getSession等方法獲取Session并保存。??????????
    ??????????? 打印已經(jīng)保存的Session的狀態(tài)。
    ??????????? 打印已經(jīng)保存的Session之間是否相同。
    ??????????? 打印SessionFactory的狀態(tài)記錄。
    Service層:testTS()方法——臨時(shí)創(chuàng)建用于測(cè)試的方法
    ??? ??? 內(nèi)部代碼為執(zhí)行三次請(qǐng)求:
    ???????? getDao().testTS();
    ???????? getDao().testTS();
    ???????? getDao().testTS();

    ?

    ?

    第一次測(cè)試

    ??? Service層的testTS(e)方法不打開(kāi)事務(wù)。???
    ??? 單線程。

    ??? 使用HibernateTemplate。??

    結(jié)論:

    Service層沒(méi)有打開(kāi)事務(wù),但是每次操作CURD操作時(shí),HibernateTemplate都會(huì)自動(dòng)創(chuàng)建

    Transactional。同一線程獲取到的前后兩個(gè)Session之間互不相同。

    ? --------------Closed Transactional && Single-Thread--------------
    ? Last Session Status : false ?? //每次Session使用完自動(dòng)關(guān)閉
    ? This Session Status : false
    ? Compared Session : false??

    ?

    ????? 測(cè)試數(shù)據(jù)擴(kuò)大100倍并記時(shí):
    ????? REQUEST[100]
    ????? Opened Session Count :300
    ????? Transaction Count :600?? //我沒(méi)有聲明打開(kāi)事務(wù)啊!為什么它還為我打開(kāi)事務(wù)
    ????? Connection Count :300
    ????? ---------------------------------------
    ????? TIME : 1719

    ?

    第二次測(cè)試

    ??? Service層的testTS(e)方法打開(kāi)事務(wù)。???
    ??? 單線程。
    ??? 使用HibernateTemplate。

    結(jié)論:

    在事務(wù)邊界內(nèi)獲取的Session由Spring管理、不必手動(dòng)關(guān)閉

    雖然打開(kāi)了事務(wù),但是同一線程下同一事務(wù)邊界內(nèi)前后獲取的兩個(gè)Session仍然不同! 這是為什么???

    后面會(huì)繼續(xù)剖析HibernateTemplate源碼,給出解釋

    ? --------------Open Transactional && Single-Thread--------------
    ? Last Session Status : Open ?? //每次Session使用完之后未關(guān)閉
    ? This Session Status : Open
    ? Compared Session : false??? //同一線程同一個(gè)事務(wù)前后獲取的兩個(gè)Session還不同!!!!!為什么?????

    ???

    ??????? 測(cè)試數(shù)據(jù)擴(kuò)大100倍并記時(shí):
    ??????? REQUEST[100]
    ??????? Opened Session Count :100
    ??????? Transaction Count :200
    ??????? Connection Count :100
    ??????? ---------------------------------------
    ??????? TIME : 1719

    ?

    第三次測(cè)試

    ??? Service層的testTS(e)方法不打開(kāi)事務(wù)。???
    ??? 單線程。
    ??? 使用getSession()。

    可以看到getSession()由于缺少Spring的支持,在無(wú)事務(wù)環(huán)境下。

    它為每個(gè)CURD請(qǐng)求創(chuàng)建了一個(gè)Session。并且更讓人震驚的是,它好像為每個(gè)Session都創(chuàng)建了新

    Connection 。這些Session使用之后,它也并不關(guān)閉。

    ? --------------Closed Transactional && Single-Thread--------------
    ? Last Session Status : Open
    ? This Session Status : Open
    ? Compared Session : false

    ???

    ???? 測(cè)試數(shù)據(jù)擴(kuò)大100倍并記時(shí),令人極其震驚且極其坑爹的一幕出現(xiàn)了!我親眼看到程序以一種 肉眼可見(jiàn)的速度

    慢騰騰的執(zhí)行一次又一次的循環(huán),它竟然執(zhí)行了一分鐘!!!:
    ??????? REQUEST[100]
    ??????? Opened Session Count :300
    ??????? Transaction Count :0
    ??????? Connection Count :300
    ??????? ---------------------------------------
    ??????? TIME : 53844

    ?

    第四次測(cè)試

    ??? Service層的testTS(e)方法打開(kāi)事務(wù)。???
    ??? 單線程。

    ??? 使用getSession() 。

    這個(gè)最容易讓人理解、由于同一線程且在同一事務(wù)邊界內(nèi),前后兩個(gè)Session相同。并且也不會(huì)重復(fù)創(chuàng)建

    Connection。

    ? --------------Open Transactional && Single-Thread--------------
    ? Last Session Status : Open ?? //每次Session使用完之后未關(guān)閉
    ? This Session Status : Open
    ? Compared Session : True ?? //同一線程前后獲取的兩個(gè)Session相同

    ???????? 測(cè)試數(shù)據(jù)擴(kuò)大100倍并記時(shí):
    ???????? REQUEST[100]
    ???????? Opened Session Count :100
    ???????? Transaction Count :200
    ???????? Connection Count :100
    ???????? ---------------------------------------
    ???????? TIME : 1204

    ?

    ?

    總結(jié):

    ????? getSession()在事務(wù)邊界內(nèi)會(huì)通過(guò)TransactionSynchronizationManager獲取Session資源,同一線程內(nèi)它不會(huì)重復(fù)創(chuàng)建Connection , 那些獲取到的Session()不需要手動(dòng)關(guān)閉。但是在無(wú)聲明式事務(wù)環(huán)境下,它就會(huì)表現(xiàn)出極其坑爹的狀況,它反復(fù)獲取Connection,也不關(guān)閉Session。非常消耗資源

    ?

    ????? HibernateTemplate有一個(gè)安全且高效的Session環(huán)境,它的CRUD都是位于HibernateCallback內(nèi)部,如果它的CRUD并沒(méi)有位于事務(wù)之中,它會(huì)自己創(chuàng)建一個(gè)事務(wù)(Spring集成Hibernate時(shí),所有的資源都是由TransactionSynchronizationManager管理的 )。同一個(gè)線程中它只需要一個(gè)Connection。Session也會(huì)在事務(wù)邊界處自動(dòng)關(guān)閉,程序員不需要關(guān)注此事(這個(gè)事務(wù)邊界可能是@Transactional顯式聲明的也可能是HibernateTemplate#doExecute隱式聲明的)。

    ?

    ???? 在同一個(gè)事務(wù)邊界內(nèi),兩個(gè)HibernateTemplate操作內(nèi)部的Session互也不相同這個(gè)問(wèn)題可以在HibernateTemplate源碼中找到答案:

    Java代碼 ?
  • protected?<T>?T?doExecute(HibernateCallback<T>?action,?boolean?enforceNewSession,?boolean?enforceNativeSession) ??
  • ????????throws?DataAccessException?{ ??
  • ????...... ??
  • ????Session?session?=?(enforceNewSession??? ??
  • ????????????????????????SessionFactoryUtils.getNewSession(getSessionFactory(),?getEntityInterceptor())?:?getSession()); ??
  • ????boolean?existingTransaction?=?(!enforceNewSession?&& ??
  • ????????????(!isAllowCreate()?||?SessionFactoryUtils.isSessionTransactional(session,?getSessionFactory()))); ??
  • ????if?(existingTransaction)?{ ??
  • ????????logger.debug("Found?thread-bound?Session?for?HibernateTemplate"); ??
  • ????} ??
  • ??
  • ????FlushMode?previousFlushMode?=?null; ??
  • ????try?{ ??
  • ????????previousFlushMode?=?applyFlushMode(session,?existingTransaction); ??
  • ????????enableFilters(session); ??
  • ????????Session?sessionToExpose?= ??
  • ????????????????(enforceNativeSession?||?isExposeNativeSession()??? ??
  • ????????????????????????????????session?:?createSessionProxy(session)); ??
  • ????????T?result?=?action.doInHibernate(sessionToExpose); ??
  • ????????flushIfNecessary(session,?existingTransaction); ??
  • ????????return?result; ??
  • ????...... ??
  • }??
  • protected <T> T doExecute(HibernateCallback<T> action, boolean enforceNewSession, boolean enforceNativeSession)throws DataAccessException {......Session session = (enforceNewSession ? SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor()) : getSession());boolean existingTransaction = (!enforceNewSession &&(!isAllowCreate() || SessionFactoryUtils.isSessionTransactional(session, getSessionFactory())));if (existingTransaction) {logger.debug("Found thread-bound Session for HibernateTemplate");}FlushMode previousFlushMode = null;try {previousFlushMode = applyFlushMode(session, existingTransaction);enableFilters(session);Session sessionToExpose =(enforceNativeSession || isExposeNativeSession() ? session : createSessionProxy(session));T result = action.doInHibernate(sessionToExpose);flushIfNecessary(session, existingTransaction);return result;...... } Java代碼 ?
  • 通過(guò) ??
  • Session?sessionToExpose?= ??
  • ????????????????(enforceNativeSession?||?isExposeNativeSession()??? ??
  • ????????????????????????????????session?:?createSessionProxy(session)); ??
  • 紅色部分可以看到HibernateTemplate中獲取的Session不是原生的,而是代理的。那個(gè)代理類(lèi)是一個(gè)比較簡(jiǎn)單的內(nèi)部類(lèi),源代碼位于HibernateTemplate類(lèi)文件最下部分。每次HibernateTemplate#doExecute執(zhí)行時(shí)除非聲明不使用代理類(lèi),Spring都會(huì)使用線程中的Session資源來(lái)創(chuàng)建代理。但是這個(gè)代理類(lèi)的創(chuàng)建對(duì)性能的影響微不足道了,Spring這樣做肯定有它的道理。??
  • 通過(guò) Session sessionToExpose =(enforceNativeSession || isExposeNativeSession() ? session : createSessionProxy(session)); 紅色部分可以看到HibernateTemplate中獲取的Session不是原生的,而是代理的。那個(gè)代理類(lèi)是一個(gè)比較簡(jiǎn)單的內(nèi)部類(lèi),源代碼位于HibernateTemplate類(lèi)文件最下部分。每次HibernateTemplate#doExecute執(zhí)行時(shí)除非聲明不使用代理類(lèi),Spring都會(huì)使用線程中的Session資源來(lái)創(chuàng)建代理。但是這個(gè)代理類(lèi)的創(chuàng)建對(duì)性能的影響微不足道了,Spring這樣做肯定有它的道理。

    ?

    ?

    ====================================

    http://www.kuqin.com/docs/hibernate/transactions.html

    第?10?章?事務(wù)和并行(Transactions And Concurrency)

    Hibernate本身并不是數(shù)據(jù)庫(kù),它只是一個(gè)輕量級(jí)的對(duì)象-關(guān)系數(shù)據(jù)庫(kù)映射(object-relational)工具。它的事務(wù)交由底層的數(shù)據(jù)庫(kù)連接管理,如果數(shù)據(jù)庫(kù)連接有JTA的支持,那么在Session中進(jìn)行的操作將是整個(gè)原子性JTA事務(wù)的一部分。Hibernate可以看作是添加了面向?qū)ο笳Z(yǔ)義的JDBC瘦適配器(thin adapter)。

    10.1.?配置,會(huì)話和工廠(Configurations, Sessions and Factories)

    SessionFactory的創(chuàng)建需要耗費(fèi)大量資源,它是線程安全(threadsafe)的對(duì)象,在應(yīng)用中它被所有線程共享。而Session的創(chuàng)建耗費(fèi)資源很少,它不是線程安全的對(duì)象,對(duì)于一個(gè)簡(jiǎn)單商業(yè)過(guò)程(business process),它應(yīng)該只被使用一次,然后被丟棄。舉例來(lái)說(shuō),當(dāng)Hibernate在基于servlet的應(yīng)用中,servlet能夠以下面的方式得到SessionFactory。

    SessionFactory sf = (SessionFactory)getServletContext().getAttribute("my.session.factory");

    每次調(diào)用SessionFactory的service方法能夠生成一個(gè)新的Session對(duì)象,然后調(diào)用Session的flush(),調(diào)用commit()提交它的連接,調(diào)用close()關(guān)閉它,最終丟棄它。(SessionFactory可能被保存在JNDI或者一個(gè)靜態(tài)的單例(Singleton)輔助變量中。)

    在無(wú)狀態(tài)的session bean中,可以同樣使用類(lèi)似的方法。bean在setSessionContext()中得到SessionFactory的實(shí)例,每個(gè)商業(yè)方法會(huì)生成一個(gè)Session對(duì)象,調(diào)用它的flush()和close(),當(dāng)然,應(yīng)用不應(yīng)該commit()connection. (把它留給JTA.在容器管理的事務(wù)中,數(shù)據(jù)庫(kù)連接會(huì)自動(dòng)完成事務(wù)。)

    我們用上述方法使用Hibernate 的Transaction API,對(duì)Transaction執(zhí)行一次commit()會(huì)把所有狀態(tài)同步,把底層的數(shù)據(jù)庫(kù)連接提交(對(duì)JTA 事務(wù)會(huì)特殊處理。)

    這里需要理解flush()的含義。 flush()將持久化存儲(chǔ)與內(nèi)存中的變化進(jìn)行同步,但不是將內(nèi)存的變化與持久化存儲(chǔ)進(jìn)行同步。注意對(duì)所有的Hibernate JDBD 連接/事務(wù)來(lái)說(shuō),其隔離級(jí)別將施加于所有的Hibernate執(zhí)行的操作之上!

    接下來(lái)的幾小節(jié)將討論利用版本化的方法來(lái)確保事務(wù)原子性,這些“高級(jí)”方法需要小心使用。

    10.2.?線程和連接(Threads and connections)

    在創(chuàng)建Hibernate會(huì)話(Session)時(shí),你應(yīng)該留意以下的實(shí)踐(practices):

    • 對(duì)于一個(gè)數(shù)據(jù)庫(kù)連接,不要?jiǎng)?chuàng)建一個(gè)以上的Session或Transaction。

    • 在對(duì)于一個(gè)數(shù)據(jù)庫(kù)連接、一個(gè)事務(wù)使用多個(gè)Session時(shí),你尤其需要格外地小心。Session對(duì)象會(huì)記錄下調(diào)入數(shù)據(jù)更新的情況,所以另一個(gè)Session對(duì)象可能會(huì)遇到過(guò)時(shí)的數(shù)據(jù)。

    • Session不是線程安全的。決不要在兩個(gè)并發(fā)的線程中訪問(wèn)同一個(gè)Session。一個(gè)Session一般只對(duì)應(yīng)一批需要一次性完成的單元操作!

    10.3.?考慮對(duì)象辨別

    程序可能在兩批單元操作中并發(fā)訪問(wèn)同一個(gè)對(duì)象的持久化狀態(tài)。不管怎樣,持久化類(lèi)的一個(gè)實(shí)例不可能在兩個(gè)Session中共享。所以有兩種不同的辨別方式:

    數(shù)據(jù)庫(kù)辨別

    foo.getId().equals( bar.getId() )

    JVM 辨別

    foo==bar

    對(duì)于依附于某個(gè)特定Session的對(duì)象,兩種辨別方式是等價(jià)的。然而,當(dāng)程序可能在兩個(gè)不同的session中并發(fā)訪問(wèn)“同一個(gè)”(持久化辨別)商業(yè)對(duì)象時(shí),兩個(gè)實(shí)例(對(duì)于JVM辨別來(lái)說(shuō))卻可能是“不同”的。

    這種方式把關(guān)于并發(fā)的頭疼問(wèn)題留給了Hibernate和數(shù)據(jù)庫(kù)。程序不需要對(duì)任何商業(yè)對(duì)象進(jìn)行同步,只要程序堅(jiān)持每個(gè)Session一個(gè)線程,或者對(duì)象辨別的策略(在一個(gè)Session重,程序可以安全的使用==來(lái)比較對(duì)象)。

    10.4.?樂(lè)觀并發(fā)控制(Optimistic concurrency control)

    許多商業(yè)過(guò)程需要一系列與用戶(hù)進(jìn)行交互的過(guò)程,數(shù)據(jù)庫(kù)訪問(wèn)穿插在這些過(guò)程中。對(duì)于web和企業(yè)應(yīng)用來(lái)說(shuō),跨一個(gè)用戶(hù)交互過(guò)程的數(shù)據(jù)事務(wù)是不可接受的。

    維護(hù)各商業(yè)事務(wù)間的隔離(isolocation)就成為應(yīng)用層的部分責(zé)任,我們把這種過(guò)程稱(chēng)為長(zhǎng)時(shí)間運(yùn)行的應(yīng)用事務(wù)(application transaction)。單一的應(yīng)用事務(wù)可能跨越多個(gè)數(shù)據(jù)庫(kù)事務(wù)。如果這些數(shù)據(jù)庫(kù)事務(wù)中只有一個(gè)(最后一個(gè))保存了被修改的數(shù)據(jù),其他事務(wù)只是簡(jiǎn)單地讀數(shù)據(jù),則這個(gè)應(yīng)用事務(wù)就是原子性的。

    唯一滿足高并發(fā)性以及高可擴(kuò)展性的方法是使用帶有版本化的樂(lè)觀并發(fā)控制。Hibernate為使用樂(lè)觀并發(fā)控制的代碼提供了三種可能的方法。

    10.4.1.?使用長(zhǎng)生命周期帶有自動(dòng)版本化的會(huì)話

    在整個(gè)商業(yè)過(guò)程中使用一個(gè)單獨(dú)的Session實(shí)例以及它的持久化實(shí)例,這個(gè)Session使用帶有版本化的樂(lè)觀鎖定機(jī)制,來(lái)確保多個(gè)數(shù)據(jù)庫(kù)事務(wù)對(duì)于應(yīng)用來(lái)說(shuō)只是一個(gè)邏輯上的事務(wù)。在等待用戶(hù)交互時(shí),Session斷開(kāi)與數(shù)據(jù)庫(kù)的連接。這個(gè)方法從數(shù)據(jù)庫(kù)訪問(wèn)方面來(lái)看是最有效的,應(yīng)用不需要關(guān)心對(duì)自己的版本檢查或是重新與不需要序列化(transient)的實(shí)例進(jìn)行關(guān)聯(lián)。

    在整個(gè)應(yīng)用事務(wù)中,使用單一的Session 實(shí)例和它的持久化實(shí)例。

    Session 使用帶有版本化的樂(lè)觀鎖定來(lái)保證多個(gè)數(shù)據(jù)庫(kù)事務(wù)對(duì)程序來(lái)說(shuō)就如同是單一的邏輯應(yīng)用事務(wù)。在等待用戶(hù)交互的時(shí)候,Session 脫離所有的底層JDBC連接。對(duì)于數(shù)據(jù)庫(kù)訪問(wèn)來(lái)說(shuō),這種方法是最高效的。程序自己不需要關(guān)心版本檢查或者把已經(jīng)脫離session的實(shí)例重新關(guān)聯(lián)到session。

    // foo is an instance loaded earlier by the Session session.reconnect(); foo.setProperty("bar"); session.flush(); session.connection().commit(); session.disconnect();

    foo對(duì)象仍然知道是哪個(gè)Session把自己裝載的。 只要Session 擁有一個(gè)JDBC連接,我們可以把對(duì)象的更改提交。

    如果我們的 Session 太大,以至于在用戶(hù)思考的時(shí)間內(nèi)無(wú)法保存住,這種模式就會(huì)出現(xiàn)問(wèn)題。比如,HttpSession應(yīng)該保持盡量小。因?yàn)镾ession也持有(必須的)第一級(jí)緩存,包含所有被裝載的對(duì)象,我們只能在很少的request/response周期中使用這一策略。這種少用是被鼓勵(lì)的,因?yàn)镾ession 很快就會(huì)出現(xiàn)過(guò)時(shí)的數(shù)據(jù)。

    10.4.2.?使用帶有自動(dòng)版本化的多個(gè)會(huì)話

    每個(gè)與持久化存儲(chǔ)的交互出現(xiàn)在一個(gè)新的Session中,在每次與數(shù)據(jù)庫(kù)的交互中,使用相同的持久化實(shí)例。應(yīng)用操作那些從其它Session調(diào)入的已經(jīng)脫離session的實(shí)例的狀態(tài),通過(guò)使用Session.update()或者Session.saveOrUpdate()來(lái)重新建立與它們的關(guān)聯(lián)。

    // foo is an instance loaded by a previous Session foo.setProperty("bar"); session = factory.openSession(); session.saveOrUpdate(foo); session.flush(); session.connection().commit(); session.close();

    你也可以調(diào)用lock()而非update(),如果你確信對(duì)象沒(méi)有被修改過(guò),可以使用LockMode.READ(進(jìn)行一次版本檢查,而跳過(guò)所有的緩存)。

    10.4.3.?應(yīng)用程序自己進(jìn)行版本檢查

    每當(dāng)一個(gè)新的Session中與數(shù)據(jù)庫(kù)出現(xiàn)交互的時(shí)候,這個(gè)session會(huì)在操作持久化實(shí)例前重新把它們從數(shù)據(jù)庫(kù)中裝載進(jìn)來(lái)。我們現(xiàn)在所說(shuō)的方式就是你的應(yīng)用程序自己使用版本檢查來(lái)確保應(yīng)用事務(wù)的隔離性。(當(dāng)然,Hibernate仍會(huì)為你更新版本號(hào))。從數(shù)據(jù)庫(kù)訪問(wèn)方面來(lái)看,這種方法是最沒(méi)有效率的,與entity EJB方式類(lèi)似。

    // foo is an instance loaded by a previous Session session = factory.openSession(); int oldVersion = foo.getVersion(); session.load( foo, foo.getKey() ); if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException(); foo.setProperty("bar"); session.flush(); session.connection().commit(); session.close();

    當(dāng)然,如果在低數(shù)據(jù)并行(low-data-concurrency)的環(huán)境中,并不需要版本檢查,你仍可以使用這個(gè)方法,只需要忽略版本檢查。

    10.5.?會(huì)話斷開(kāi)連接(Session disconnection)

    The first approach described above is to maintain a single Session for a whole business process thats spans user think time. (For example, a servlet might keep a Session in the user's HttpSession.) For performance reasons you should

    上面提到的第一種方法是對(duì)于對(duì)一個(gè)用戶(hù)的一次登錄產(chǎn)生的整個(gè)商業(yè)過(guò)程維護(hù)一個(gè)Session。(舉例來(lái)說(shuō),servlet有可能會(huì)在用戶(hù)的HttpSession中保留一個(gè)Session)。為性能考慮,你必須

  • 提交Transaction(或者JDBC連接),然后

  • (在等待用戶(hù)操作前,)斷開(kāi)Session與JDBC連接。

  • Session.disconnect()方法會(huì)斷開(kāi)會(huì)話與JDBC的連接,把連接返還給連接池(除非是你自己提供這個(gè)連接的)。

    Session.reconnect()方法會(huì)得到一個(gè)新的連接(你也可以自己提供一個(gè)),重新開(kāi)始會(huì)話。在重新連接后,你可以通過(guò)對(duì)任何可能被其它事務(wù)更新的對(duì)象調(diào)用Session.lock()方法,來(lái)強(qiáng)迫對(duì)你沒(méi)有更新的數(shù)據(jù)進(jìn)行版本檢查。你不需要對(duì)正在更新的數(shù)據(jù)調(diào)用lock()。

    這是一個(gè)例子:

    SessionFactory sessions; List fooList; Bar bar; .... Session s = sessions.openSession();Transaction tx = null; try {tx = s.beginTransaction();fooList = s.find("select foo from eg.Foo foo where foo.Date = current date"// uses db2 date function);bar = (Bar) s.create(Bar.class);tx.commit(); } catch (Exception e) {if (tx!=null) tx.rollback();s.close();throw e; } s.disconnect();

    接下來(lái):

    s.reconnect();try {tx = s.beginTransaction();bar.setFooTable( new HashMap() );Iterator iter = fooList.iterator();while ( iter.hasNext() ) {Foo foo = (Foo) iter.next();s.lock(foo, LockMode.READ); //check that foo isn't stalebar.getFooTable().put( foo.getName(), foo );}tx.commit(); } catch (Exception e) {if (tx!=null) tx.rollback();throw e; } finally {s.close(); }

    從上面的例子可以看到Transaction和Session之間是多對(duì)一的關(guān)系。一個(gè)Session表示了應(yīng)用程序與數(shù)據(jù)庫(kù)之間的一個(gè)對(duì)話,Transaction把這個(gè)對(duì)話分隔成一個(gè)個(gè)在數(shù)據(jù)庫(kù)級(jí)別具有原子性的單元。

    10.6.?悲觀鎖定(Pessimistic Locking)

    用戶(hù)不需要在鎖定策略上花費(fèi)過(guò)多時(shí)間,通常我們可以對(duì)JDBC連接選定一種隔離級(jí)別(isolationn level),然后讓數(shù)據(jù)庫(kù)完成所有的工作。高級(jí)用戶(hù)可能希望得到悲觀鎖定或者在新的事務(wù)開(kāi)始時(shí)重新得到鎖。

    Hibernate一直都會(huì)使用數(shù)據(jù)庫(kù)的鎖定機(jī)制,而不會(huì)在內(nèi)存中鎖定對(duì)象。

    LockMode類(lèi)定義了Hibernate需要的不同的鎖級(jí)別。鎖由以下的機(jī)制得到:

    • LockMode.WRITE在Hibernate更新或插入一行數(shù)據(jù)時(shí)自動(dòng)得到。

    • LockMode.UPGRADE在用戶(hù)通過(guò)SELECT ... FOR UPDATE這樣的特定請(qǐng)求得到,需要數(shù)據(jù)庫(kù)支持這種語(yǔ)法。

    • LockMode.UPGRADE_NOWAIT在用戶(hù)通過(guò)SELECT ... FOR UPDATE NOWAIT這樣的特定請(qǐng)求在Oracle數(shù)據(jù)庫(kù)環(huán)境下得到。

    • LockMode.READ在Hibernate在不斷讀(Repeatable Read)和序列化(Serializable)的隔離級(jí)別下讀取數(shù)據(jù)時(shí)得到。也可以通過(guò)用戶(hù)的明確請(qǐng)求重新獲得。

    • LockMode.NONE表示沒(méi)有鎖。所有對(duì)象在Transaction結(jié)束時(shí)會(huì)切換到這種鎖模式,通過(guò)調(diào)用update()或者saveOrUpdate()與會(huì)話進(jìn)行關(guān)聯(lián)的對(duì)象,開(kāi)始時(shí)也會(huì)在這種鎖模式。

    “明確的用戶(hù)請(qǐng)求”會(huì)以下的幾種方式出現(xiàn):

    • 調(diào)用Session.load(),指定一種LockMode。

    • 調(diào)用Session.lock()。

    • 調(diào)用Query.setLockMode()。

    如果在調(diào)用Session.load()時(shí)指定了UPGRADE或者UPGRADE_NOWAIT,并且請(qǐng)求的對(duì)象還沒(méi)有被會(huì)話調(diào)入,那么這個(gè)對(duì)象會(huì)以SELECT ... FOR UPDATE的方式調(diào)入。如果調(diào)用load()在一個(gè)已經(jīng)調(diào)入的對(duì)象,并且這個(gè)對(duì)象調(diào)入時(shí)的鎖級(jí)別沒(méi)有請(qǐng)求時(shí)來(lái)得嚴(yán)格,Hibernate會(huì)對(duì)這個(gè)對(duì)象調(diào)用lock()。

    Session.lock()會(huì)執(zhí)行版本號(hào)檢查的特定的鎖模式是:READ,UPGRADE或者UPGRADE_NOWAIT。(在UPGRADE或者UPGRADE_NOWAIT,SELECT ... FOR UPGRADE使用的情況下。)

    如果數(shù)據(jù)庫(kù)不支持所請(qǐng)求的鎖模式,Hibernate將會(huì)選擇一種合適的受支持的鎖模式替換(而不是拋出一個(gè)異常)。這確保了應(yīng)用具有可移植性。

    ?

    ============http://www.redsaga.com/hibernate-ref/3.x/zh-cn/html/objectstate.html

    第?10?章?與對(duì)象共事

    Hibernate是完整的對(duì)象/關(guān)系映射解決方案,它提供了對(duì)象狀態(tài)管理(state management)的功能,使開(kāi)發(fā)者不再需要理會(huì)底層數(shù)據(jù)庫(kù)系統(tǒng)的細(xì)節(jié)。 也就是說(shuō),相對(duì)于常見(jiàn)的JDBC/SQL持久層方案中需要管理SQL語(yǔ)句,Hibernate采用了更自然的面向?qū)ο蟮囊暯莵?lái)持久化Java應(yīng)用中的數(shù)據(jù)。

    換句話說(shuō),使用Hibernate的開(kāi)發(fā)者應(yīng)該總是關(guān)注對(duì)象的狀態(tài)(state),不必考慮SQL語(yǔ)句的執(zhí)行。 這部分細(xì)節(jié)已經(jīng)由Hibernate掌管妥當(dāng),只有開(kāi)發(fā)者在進(jìn)行系統(tǒng)性能調(diào)優(yōu)的時(shí)候才需要進(jìn)行了解。

    10.1.?Hibernate對(duì)象狀態(tài)(object states)

    Hibernate定義并支持下列對(duì)象狀態(tài)(state):

    • 瞬時(shí)(Transient) - 由new操作符創(chuàng)建,且尚未與Hibernate Session 關(guān)聯(lián)的對(duì)象被認(rèn)定為瞬時(shí)(Transient)的。瞬時(shí)(Transient)對(duì)象不會(huì)被持久化到數(shù)據(jù)庫(kù)中,也不會(huì)被賦予持久化標(biāo)識(shí)(identifier)。 如果程序中沒(méi)有保持對(duì)瞬時(shí)(Transient)對(duì)象的引用,它會(huì)被垃圾回收器(garbage collector)銷(xiāo)毀。 使用Hibernate Session可以將其變?yōu)槌志?Persistent)狀態(tài)。(Hibernate會(huì)自動(dòng)執(zhí)行必要的SQL語(yǔ)句)

    • 持久(Persistent) - 持久(Persistent)的實(shí)例在數(shù)據(jù)庫(kù)中有對(duì)應(yīng)的記錄,并擁有一個(gè)持久化標(biāo)識(shí)(identifier)。 持久(Persistent)的實(shí)例可能是剛被保存的,或剛被加載的,無(wú)論哪一種,按定義對(duì)象都僅在相關(guān)聯(lián)的Session生命周期內(nèi)的保持這種狀態(tài)。 Hibernate會(huì)檢測(cè)到處于持久(Persistent)狀態(tài)的對(duì)象的任何改動(dòng),在當(dāng)前操作單元(unit of work)執(zhí)行完畢時(shí)將對(duì)象數(shù)據(jù)(state)與數(shù)據(jù)庫(kù)同步(synchronize)。 開(kāi)發(fā)者不需要手動(dòng)執(zhí)行UPDATE。將對(duì)象從持久(Persistent)狀態(tài)變成瞬時(shí)(Transient)狀態(tài)同樣也不需要手動(dòng)執(zhí)行DELETE語(yǔ)句。

    • 脫管(Detached) - 與持久(Persistent)對(duì)象關(guān)聯(lián)的Session被關(guān)閉后,對(duì)象就變?yōu)槊摴?Detached)的。 對(duì)脫管(Detached)對(duì)象的引用依然有效,對(duì)象可繼續(xù)被修改。脫管(Detached)對(duì)象如果重新關(guān)聯(lián)到某個(gè)新的Session上, 會(huì)再次轉(zhuǎn)變?yōu)槌志?Persistent)的(Detached其間的改動(dòng)將被持久化到數(shù)據(jù)庫(kù))。 這個(gè)功能使得一種編程模型,即中間會(huì)給用戶(hù)思考時(shí)間(user think-time)的長(zhǎng)時(shí)間運(yùn)行的操作單元(unit of work)的編程模型成為可能。 我們稱(chēng)之為應(yīng)用程序事務(wù),即從用戶(hù)觀點(diǎn)看是一個(gè)操作單元(unit of work)。

    接下來(lái)我們來(lái)細(xì)致的討論下?tīng)顟B(tài)(states)及狀態(tài)間的轉(zhuǎn)換(state transitions)(以及觸發(fā)狀態(tài)轉(zhuǎn)換的Hibernate方法)。

    10.2.?使對(duì)象持久化

    Hibernate認(rèn)為持久化類(lèi)(persistent class)新實(shí)例化的對(duì)象是瞬時(shí)(Transient)的。 我們可將瞬時(shí)(Transient)對(duì)象與session關(guān)聯(lián)而變?yōu)?span id="ozvdkddzhkzd" class="emphasis">持久(Persistent)的。

    DomesticCat fritz = new DomesticCat(); fritz.setColor(Color.GINGER); fritz.setSex('M'); fritz.setName("Fritz"); Long generatedId = (Long) sess.save(fritz);

    如果Cat的持久化標(biāo)識(shí)(identifier)是generated類(lèi)型的, 那么該標(biāo)識(shí)(identifier)會(huì)自動(dòng)在save()被調(diào)用時(shí)產(chǎn)生并分配給cat。 如果Cat的持久化標(biāo)識(shí)(identifier)是assigned類(lèi)型的,或是一個(gè)復(fù)合主鍵(composite key), 那么該標(biāo)識(shí)(identifier)應(yīng)當(dāng)在調(diào)用save()之前手動(dòng)賦予給cat。 你也可以按照EJB3 early draft中定義的語(yǔ)義,使用persist()替代save()。

    此外,你可以用一個(gè)重載版本的save()方法。

    DomesticCat pk = new DomesticCat(); pk.setColor(Color.TABBY); pk.setSex('F'); pk.setName("PK"); pk.setKittens( new HashSet() ); pk.addKitten(fritz); sess.save( pk, new Long(1234) );

    如果你持久化的對(duì)象有關(guān)聯(lián)的對(duì)象(associated objects)(例如上例中的kittens集合) 那么對(duì)這些對(duì)象(譯注:pk和kittens)進(jìn)行持久化的順序是任意的(也就是說(shuō)可以先對(duì)kittens進(jìn)行持久化也可以先對(duì)pk進(jìn)行持久化), 除非你在外鍵列上有NOT NULL約束。 Hibernate不會(huì)違反外鍵約束,但是如果你用錯(cuò)誤的順序持久化對(duì)象(譯注:在pk持久之前持久kitten),那么可能會(huì)違反NOT NULL約束。

    通常你不會(huì)為這些細(xì)節(jié)煩心,因?yàn)槟愫芸赡軙?huì)使用Hibernate的 傳播性持久化(transitive persistence)功能自動(dòng)保存相關(guān)聯(lián)那些對(duì)象。 這樣連違反NOT NULL約束情況都不會(huì)出現(xiàn)了 - Hibernate會(huì)管好所有的事情。 傳播性持久化(transitive persistence)將在本章稍后討論。

    10.3.?裝載對(duì)象

    如果你知道某個(gè)實(shí)例的持久化標(biāo)識(shí)(identifier),你就可以使用Session的load()方法 來(lái)獲取它。 load()的另一個(gè)參數(shù)是指定類(lèi)的.class對(duì)象。 本方法會(huì)創(chuàng)建指定類(lèi)的持久化實(shí)例,并從數(shù)據(jù)庫(kù)加載其數(shù)據(jù)(state)。

    Cat fritz = (Cat) sess.load(Cat.class, generatedId); // you need to wrap primitive identifiers long pkId = 1234; DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) );

    此外, 你可以把數(shù)據(jù)(state)加載到指定的對(duì)象實(shí)例上(覆蓋掉該實(shí)例原來(lái)的數(shù)據(jù))。

    Cat cat = new DomesticCat(); // load pk's state into cat sess.load( cat, new Long(pkId) ); Set kittens = cat.getKittens();

    請(qǐng)注意如果沒(méi)有匹配的數(shù)據(jù)庫(kù)記錄,load()方法可能拋出無(wú)法恢復(fù)的異常(unrecoverable exception)。 如果類(lèi)的映射使用了代理(proxy),load()方法會(huì)返回一個(gè)未初始化的代理,直到你調(diào)用該代理的某方法時(shí)才會(huì)去訪問(wèn)數(shù)據(jù)庫(kù)。 若你希望在某對(duì)象中創(chuàng)建一個(gè)指向另一個(gè)對(duì)象的關(guān)聯(lián),又不想在從數(shù)據(jù)庫(kù)中裝載該對(duì)象時(shí)同時(shí)裝載相關(guān)聯(lián)的那個(gè)對(duì)象,那么這種操作方式就用得上的了。 如果為相應(yīng)類(lèi)映射關(guān)系設(shè)置了batch-size, 那么使用這種操作方式允許多個(gè)對(duì)象被一批裝載(因?yàn)榉祷氐氖谴?#xff0c;無(wú)需從數(shù)據(jù)庫(kù)中抓取所有對(duì)象的數(shù)據(jù))。

    如果你不確定是否有匹配的行存在,應(yīng)該使用get()方法,它會(huì)立刻訪問(wèn)數(shù)據(jù)庫(kù),如果沒(méi)有對(duì)應(yīng)的行,會(huì)返回null。

    Cat cat = (Cat) sess.get(Cat.class, id); if (cat==null) {cat = new Cat();sess.save(cat, id); } return cat;

    你甚至可以選用某個(gè)LockMode,用SQL的SELECT ... FOR UPDATE裝載對(duì)象。 請(qǐng)查閱API文檔以獲取更多信息。

    Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);

    注意,任何關(guān)聯(lián)的對(duì)象或者包含的集合都不會(huì)被以FOR UPDATE方式返回, 除非你指定了lock或者all作為關(guān)聯(lián)(association)的級(jí)聯(lián)風(fēng)格(cascade style)。

    任何時(shí)候都可以使用refresh()方法強(qiáng)迫裝載對(duì)象和它的集合。如果你使用數(shù)據(jù)庫(kù)觸發(fā)器功能來(lái)處理對(duì)象的某些屬性,這個(gè)方法就很有用了。

    sess.save(cat); sess.flush(); //force the SQL INSERT sess.refresh(cat); //re-read the state (after the trigger executes)

    此處通常會(huì)出現(xiàn)一個(gè)重要問(wèn)題: Hibernate會(huì)從數(shù)據(jù)庫(kù)中裝載多少東西?會(huì)執(zhí)行多少條相應(yīng)的SQLSELECT語(yǔ)句? 這取決于抓取策略(fetching strategy),會(huì)在第?19.1?節(jié) “ 抓取策略(Fetching strategies) ”中解釋。

    10.4.?查詢(xún)

    如果不知道所要尋找的對(duì)象的持久化標(biāo)識(shí),那么你需要使用查詢(xún)。Hibernate支持強(qiáng)大且易于使用的面向?qū)ο蟛樵?xún)語(yǔ)言(HQL)。 如果希望通過(guò)編程的方式創(chuàng)建查詢(xún),Hibernate提供了完善的按條件(Query By Criteria, QBC)以及按樣例(Query By Example, QBE)進(jìn)行查詢(xún)的功能。 你也可以用原生SQL(native SQL)描述查詢(xún),Hibernate提供了將結(jié)果集(result set)轉(zhuǎn)化為對(duì)象的部分支持。

    10.4.1.?執(zhí)行查詢(xún)

    HQL和原生SQL(native SQL)查詢(xún)要通過(guò)為org.hibernate.Query的實(shí)例來(lái)表達(dá)。 這個(gè)接口提供了參數(shù)綁定、結(jié)果集處理以及運(yùn)行實(shí)際查詢(xún)的方法。 你總是可以通過(guò)當(dāng)前Session獲取一個(gè)Query對(duì)象:

    List cats = session.createQuery("from Cat as cat where cat.birthdate < ?").setDate(0, date).list();List mothers = session.createQuery("select mother from Cat as cat join cat.mother as mother where cat.name = ?").setString(0, name).list();List kittens = session.createQuery("from Cat as cat where cat.mother = ?").setEntity(0, pk).list();Cat mother = (Cat) session.createQuery("select cat.mother from Cat as cat where cat = ?").setEntity(0, izi).uniqueResult();

    一個(gè)查詢(xún)通常在調(diào)用list()時(shí)被執(zhí)行,執(zhí)行結(jié)果會(huì)完全裝載進(jìn)內(nèi)存中的一個(gè)集合(collection)。 查詢(xún)返回的對(duì)象處于持久(persistent)狀態(tài)。如果你知道的查詢(xún)只會(huì)返回一個(gè)對(duì)象,可使用list()的快捷方式uniqueResult()。

    10.4.1.1.?迭代式獲取結(jié)果(Iterating results)

    某些情況下,你可以使用iterate()方法得到更好的性能。 這通常是你預(yù)期返回的結(jié)果在session,或二級(jí)緩存(second-level cache)中已經(jīng)存在時(shí)的情況。 如若不然,iterate()會(huì)比list()慢,而且可能簡(jiǎn)單查詢(xún)也需要進(jìn)行多次數(shù)據(jù)庫(kù)訪問(wèn): iterate()會(huì)首先使用1條語(yǔ)句得到所有對(duì)象的持久化標(biāo)識(shí)(identifiers),再根據(jù)持久化標(biāo)識(shí)執(zhí)行n條附加的select語(yǔ)句實(shí)例化實(shí)際的對(duì)象。

    // fetch ids Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate(); while ( iter.hasNext() ) {Qux qux = (Qux) iter.next(); // fetch the object// something we couldnt express in the queryif ( qux.calculateComplicatedAlgorithm() ) {// delete the current instanceiter.remove();// dont need to process the restbreak;} }

    10.4.1.2.?返回元組(tuples)的查詢(xún)

    (譯注:元組(tuples)指一條結(jié)果行包含多個(gè)對(duì)象) Hibernate查詢(xún)有時(shí)返回元組(tuples),每個(gè)元組(tuples)以數(shù)組的形式返回:

    Iterator kittensAndMothers = sess.createQuery("select kitten, mother from Cat kitten join kitten.mother mother").list().iterator();while ( kittensAndMothers.hasNext() ) {Object[] tuple = (Object[]) kittensAndMothers.next();Cat kitten = tuple[0];Cat mother = tuple[1];.... }

    10.4.1.3.?標(biāo)量(Scalar)結(jié)果

    查詢(xún)可在select從句中指定類(lèi)的屬性,甚至可以調(diào)用SQL統(tǒng)計(jì)(aggregate)函數(shù)。 屬性或統(tǒng)計(jì)結(jié)果被認(rèn)定為"標(biāo)量(Scalar)"的結(jié)果(而不是持久(persistent state)的實(shí)體)。

    Iterator results = sess.createQuery("select cat.color, min(cat.birthdate), count(cat) from Cat cat " +"group by cat.color").list().iterator();while ( results.hasNext() ) {Object[] row = results.next();Color type = (Color) row[0];Date oldest = (Date) row[1];Integer count = (Integer) row[2];..... }

    10.4.1.4.?綁定參數(shù)

    接口Query提供了對(duì)命名參數(shù)(named parameters)、JDBC風(fēng)格的問(wèn)號(hào)(?)參數(shù)進(jìn)行綁定的方法。 不同于JDBC,Hibernate對(duì)參數(shù)從0開(kāi)始計(jì)數(shù)。 命名參數(shù)(named parameters)在查詢(xún)字符串中是形如:name的標(biāo)識(shí)符。 命名參數(shù)(named parameters)的優(yōu)點(diǎn)是:

    • 命名參數(shù)(named parameters)與其在查詢(xún)串中出現(xiàn)的順序無(wú)關(guān)

    • 它們可在同一查詢(xún)串中多次出現(xiàn)

    • 它們本身是自我說(shuō)明的

    //named parameter (preferred) Query q = sess.createQuery("from DomesticCat cat where cat.name = :name"); q.setString("name", "Fritz"); Iterator cats = q.iterate(); //positional parameter Query q = sess.createQuery("from DomesticCat cat where cat.name = ?"); q.setString(0, "Izi"); Iterator cats = q.iterate(); //named parameter list List names = new ArrayList(); names.add("Izi"); names.add("Fritz"); Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)"); q.setParameterList("namesList", names); List cats = q.list();

    10.4.1.5.?分頁(yè)

    如果你需要指定結(jié)果集的范圍(希望返回的最大行數(shù)/或開(kāi)始的行數(shù)),應(yīng)該使用Query接口提供的方法:

    Query q = sess.createQuery("from DomesticCat cat"); q.setFirstResult(20); q.setMaxResults(10); List cats = q.list();

    Hibernate 知道如何將這個(gè)有限定條件的查詢(xún)轉(zhuǎn)換成你的數(shù)據(jù)庫(kù)的原生SQL(native SQL)。

    10.4.1.6.?可滾動(dòng)遍歷(Scrollable iteration)

    如果你的JDBC驅(qū)動(dòng)支持可滾動(dòng)的ResuleSet,Query接口可以使用ScrollableResults,允許你在查詢(xún)結(jié)果中靈活游走。

    Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +"order by cat.name"); ScrollableResults cats = q.scroll(); if ( cats.first() ) {// find the first name on each page of an alphabetical list of cats by namefirstNamesOfPages = new ArrayList();do {String name = cats.getString(0);firstNamesOfPages.add(name);}while ( cats.scroll(PAGE_SIZE) );// Now get the first page of catspageOfCats = new ArrayList();cats.beforeFirst();int i=0;while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );} cats.close()

    請(qǐng)注意,使用此功能需要保持?jǐn)?shù)據(jù)庫(kù)連接(以及游標(biāo)(cursor))處于一直打開(kāi)狀態(tài)。 如果你需要斷開(kāi)連接使用分頁(yè)功能,請(qǐng)使用setMaxResult()/setFirstResult()

    10.4.1.7.?外置命名查詢(xún)(Externalizing named queries)

    你可以在映射文件中定義命名查詢(xún)(named queries)。 (如果你的查詢(xún)串中包含可能被解釋為XML標(biāo)記(markup)的字符,別忘了用CDATA包裹起來(lái)。)

    <query name="eg.DomesticCat.by.name.and.minimum.weight"><![CDATA[from eg.DomesticCat as catwhere cat.name = ?and cat.weight > ? ] ]></query>

    參數(shù)綁定及執(zhí)行以編程方式(programatically)完成:

    Query q = sess.getNamedQuery("eg.DomesticCat.by.name.and.minimum.weight"); q.setString(0, name); q.setInt(1, minWeight); List cats = q.list();

    請(qǐng)注意實(shí)際的程序代碼與所用的查詢(xún)語(yǔ)言無(wú)關(guān),你也可在元數(shù)據(jù)中定義原生SQL(native SQL)查詢(xún), 或?qū)⒃械钠渌牟樵?xún)語(yǔ)句放在配置文件中,這樣就可以讓Hibernate統(tǒng)一管理,達(dá)到遷移的目的。

    10.4.2.?過(guò)濾集合

    集合過(guò)濾器(filter)是一種用于一個(gè)持久化集合或者數(shù)組的特殊的查詢(xún)。查詢(xún)字符串中可以使用"this"來(lái)引用集合中的當(dāng)前元素。

    Collection blackKittens = session.createFilter(pk.getKittens(), "where this.color = ?").setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) ).list() );

    返回的集合可以被認(rèn)為是一個(gè)包(bag, 無(wú)順序可重復(fù)的集合(collection)),它是所給集合的副本。 原來(lái)的集合不會(huì)被改動(dòng)(這與“過(guò)濾器(filter)”的隱含的含義不符,不過(guò)與我們期待的行為一致)。

    請(qǐng)注意過(guò)濾器(filter)并不需要from子句(當(dāng)然需要的話它們也可以加上)。過(guò)濾器(filter)不限定于只能返回集合元素本身。

    Collection blackKittenMates = session.createFilter(pk.getKittens(), "select this.mate where this.color = eg.Color.BLACK.intValue").list();

    即使無(wú)條件的過(guò)濾器(filter)也是有意義的。例如,用于加載一個(gè)大集合的子集:

    Collection tenKittens = session.createFilter(mother.getKittens(), "").setFirstResult(0).setMaxResults(10).list();

    10.4.3.?條件查詢(xún)(Criteria queries)

    HQL極為強(qiáng)大,但是有些人希望能夠動(dòng)態(tài)的使用一種面向?qū)ο驛PI創(chuàng)建查詢(xún),而非在他們的Java代碼中嵌入字符串。對(duì)于那部分人來(lái)說(shuō),Hibernate提供了直觀的Criteria查詢(xún)API。

    Criteria crit = session.createCriteria(Cat.class); crit.add( Expression.eq( "color", eg.Color.BLACK ) ); crit.setMaxResults(10); List cats = crit.list();

    Criteria以及相關(guān)的樣例(Example)API將會(huì)再第?15?章 條件查詢(xún)(Criteria Queries) 中詳細(xì)討論。

    10.4.4.?使用原生SQL的查詢(xún)

    你可以使用createSQLQuery()方法,用SQL來(lái)描述查詢(xún),并由Hibernate處理將結(jié)果集轉(zhuǎn)換成對(duì)象的工作。 請(qǐng)注意,你可以在任何時(shí)候調(diào)用session.connection()來(lái)獲得并使用JDBC Connection對(duì)象。 如果你選擇使用Hibernate的API, 你必須把SQL別名用大括號(hào)包圍起來(lái):

    List cats = session.createSQLQuery("SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10","cat",Cat.class ).list(); List cats = session.createSQLQuery("SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +"{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +"FROM CAT {cat} WHERE ROWNUM<10","cat",Cat.class ).list()

    和Hibernate查詢(xún)一樣,SQL查詢(xún)也可以包含命名參數(shù)和占位參數(shù)。 可以在第?16?章 Native SQL查詢(xún)找到更多關(guān)于Hibernate中原生SQL(native SQL)的信息。

    10.5.?修改持久對(duì)象

    事務(wù)中的持久實(shí)例(就是通過(guò)session裝載、保存、創(chuàng)建或者查詢(xún)出的對(duì)象) 被應(yīng)用程序操作所造成的任何修改都會(huì)在Session被刷出(flushed)的時(shí)候被持久化(本章后面會(huì)詳細(xì)討論)。 這里不需要調(diào)用某個(gè)特定的方法(比如update(),設(shè)計(jì)它的目的是不同的)將你的修改持久化。 所以最直接的更新一個(gè)對(duì)象的方法就是在Session處于打開(kāi)狀態(tài)時(shí)load()它,然后直接修改即可:

    DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) ); cat.setName("PK"); sess.flush(); // changes to cat are automatically detected and persisted

    有時(shí)這種程序模型效率低下,因?yàn)樗谕籗ession里需要一條SQL SELECT語(yǔ)句(用于加載對(duì)象) 以及一條SQL UPDATE語(yǔ)句(持久化更新的狀態(tài))。 為此Hibernate提供了另一種途徑,使用脫管(detached)實(shí)例。

    請(qǐng)注意Hibernate本身不提供直接執(zhí)行UPDATE或DELETE語(yǔ)句的API。 Hibernate提供的是狀態(tài)管理(state management)服務(wù),你不必考慮要使用的語(yǔ)句(statements)。 JDBC是出色的執(zhí)行SQL語(yǔ)句的API,任何時(shí)候調(diào)用session.connection()你都可以得到一個(gè)JDBC Connection對(duì)象。 此外,在聯(lián)機(jī)事務(wù)處理(OLTP)程序中,大量操作(mass operations)與對(duì)象/關(guān)系映射的觀點(diǎn)是相沖突的。 Hibernate的將來(lái)版本可能會(huì)提供專(zhuān)門(mén)的進(jìn)行大量操作(mass operation)的功能。 參考第?13?章 批量處理(Batch processing),尋找一些可用的批量(batch)操作技巧。

    10.6.?修改脫管(Detached)對(duì)象

    很多程序需要在某個(gè)事務(wù)中獲取對(duì)象,然后將對(duì)象發(fā)送到界面層去操作,最后在一個(gè)新的事務(wù)保存所做的修改。 在高并發(fā)訪問(wèn)的環(huán)境中使用這種方式,通常使用附帶版本信息的數(shù)據(jù)來(lái)保證這些“長(zhǎng)“工作單元之間的隔離。

    Hibernate通過(guò)提供使用Session.update()或Session.merge()方法 重新關(guān)聯(lián)脫管實(shí)例的辦法來(lái)支持這種模型。

    // in the first session Cat cat = (Cat) firstSession.load(Cat.class, catId); Cat potentialMate = new Cat(); firstSession.save(potentialMate);// in a higher layer of the application cat.setMate(potentialMate);// later, in a new session secondSession.update(cat); // update cat secondSession.update(mate); // update mate

    如果具有catId持久化標(biāo)識(shí)的Cat之前已經(jīng)被另一Session(secondSession)裝載了, 應(yīng)用程序進(jìn)行重關(guān)聯(lián)操作(reattach)的時(shí)候會(huì)拋出一個(gè)異常。

    如果你確定當(dāng)前session沒(méi)有包含與之具有相同持久化標(biāo)識(shí)的持久實(shí)例,使用update()。 如果想隨時(shí)合并你的的改動(dòng)而不考慮session的狀態(tài),使用merge()。 換句話說(shuō),在一個(gè)新session中通常第一個(gè)調(diào)用的是update()方法,以便保證重新關(guān)聯(lián)脫管(detached)對(duì)象的操作首先被執(zhí)行。

    希望相關(guān)聯(lián)的脫管對(duì)象(通過(guò)引用“可到達(dá)”的脫管對(duì)象)的數(shù)據(jù)也要更新到數(shù)據(jù)庫(kù)時(shí)(并且也僅僅在這種情況), 應(yīng)用程序需要對(duì)該相關(guān)聯(lián)的脫管對(duì)象單獨(dú)調(diào)用update() 當(dāng)然這些可以自動(dòng)完成,即通過(guò)使用傳播性持久化(transitive persistence),請(qǐng)看第?10.11?節(jié) “傳播性持久化(transitive persistence)”。

    lock()方法也允許程序重新關(guān)聯(lián)某個(gè)對(duì)象到一個(gè)新session上。不過(guò),該脫管(detached)的對(duì)象必須是沒(méi)有修改過(guò)的!

    //just reassociate: sess.lock(fritz, LockMode.NONE); //do a version check, then reassociate: sess.lock(izi, LockMode.READ); //do a version check, using SELECT ... FOR UPDATE, then reassociate: sess.lock(pk, LockMode.UPGRADE);

    請(qǐng)注意,lock()可以搭配多種LockMode, 更多信息請(qǐng)閱讀API文檔以及關(guān)于事務(wù)處理(transaction handling)的章節(jié)。重新關(guān)聯(lián)不是lock()的唯一用途。

    其他用于長(zhǎng)時(shí)間工作單元的模型會(huì)在第?11.3?節(jié) “樂(lè)觀并發(fā)控制(Optimistic concurrency control)”中討論。

    10.7.?自動(dòng)狀態(tài)檢測(cè)

    Hibernate的用戶(hù)曾要求一個(gè)既可自動(dòng)分配新持久化標(biāo)識(shí)(identifier)保存瞬時(shí)(transient)對(duì)象,又可更新/重新關(guān)聯(lián)脫管(detached)實(shí)例的通用方法。 saveOrUpdate()方法實(shí)現(xiàn)了這個(gè)功能。

    // in the first session Cat cat = (Cat) firstSession.load(Cat.class, catID);// in a higher tier of the application Cat mate = new Cat(); cat.setMate(mate);// later, in a new session secondSession.saveOrUpdate(cat); // update existing state (cat has a non-null id) secondSession.saveOrUpdate(mate); // save the new instance (mate has a null id)

    saveOrUpdate()用途和語(yǔ)義可能會(huì)使新用戶(hù)感到迷惑。 首先,只要你沒(méi)有嘗試在某個(gè)session中使用來(lái)自另一session的實(shí)例,你應(yīng)該就不需要使用update(), saveOrUpdate(),或merge()。有些程序從來(lái)不用這些方法。

    通常下面的場(chǎng)景會(huì)使用update()或saveOrUpdate():

    • 程序在第一個(gè)session中加載對(duì)象

    • 該對(duì)象被傳遞到表現(xiàn)層

    • 對(duì)象發(fā)生了一些改動(dòng)

    • 該對(duì)象被返回到業(yè)務(wù)邏輯層

    • 程序調(diào)用第二個(gè)session的update()方法持久這些改動(dòng)

    saveOrUpdate()做下面的事:

    • 如果對(duì)象已經(jīng)在本session中持久化了,不做任何事

    • 如果另一個(gè)與本session關(guān)聯(lián)的對(duì)象擁有相同的持久化標(biāo)識(shí)(identifier),拋出一個(gè)異常

    • 如果對(duì)象沒(méi)有持久化標(biāo)識(shí)(identifier)屬性,對(duì)其調(diào)用save()

    • 如果對(duì)象的持久標(biāo)識(shí)(identifier)表明其是一個(gè)新實(shí)例化的對(duì)象,對(duì)其調(diào)用save()

    • 如果對(duì)象是附帶版本信息的(通過(guò)<version>或<timestamp>) 并且版本屬性的值表明其是一個(gè)新實(shí)例化的對(duì)象,save()它。

    • 否則update() 這個(gè)對(duì)象

    merge()可非常不同:

    • 如果session中存在相同持久化標(biāo)識(shí)(identifier)的實(shí)例,用用戶(hù)給出的對(duì)象的狀態(tài)覆蓋舊有的持久實(shí)例

    • 如果session沒(méi)有相應(yīng)的持久實(shí)例,則嘗試從數(shù)據(jù)庫(kù)中加載,或創(chuàng)建新的持久化實(shí)例

    • 最后返回該持久實(shí)例

    • 用戶(hù)給出的這個(gè)對(duì)象沒(méi)有被關(guān)聯(lián)到session上,它依舊是脫管的

    10.8.?刪除持久對(duì)象

    使用Session.delete()會(huì)把對(duì)象的狀態(tài)從數(shù)據(jù)庫(kù)中移除。 當(dāng)然,你的應(yīng)用程序可能仍然持有一個(gè)指向已刪除對(duì)象的引用。所以,最好這樣理解:delete()的用途是把一個(gè)持久實(shí)例變成瞬時(shí)(transient)實(shí)例。

    sess.delete(cat);

    你可以用你喜歡的任何順序刪除對(duì)象,不用擔(dān)心外鍵約束沖突。當(dāng)然,如果你搞錯(cuò)了順序,還是有可能引發(fā)在外鍵字段定義的NOT NULL約束沖突。 例如你刪除了父對(duì)象,但是忘記刪除孩子們。

    10.9.?在兩個(gè)不同數(shù)據(jù)庫(kù)間復(fù)制對(duì)象

    偶爾會(huì)用到不重新生成持久化標(biāo)識(shí)(identifier),將持久實(shí)例以及其關(guān)聯(lián)的實(shí)例持久到不同的數(shù)據(jù)庫(kù)中的操作。

    //retrieve a cat from one database Session session1 = factory1.openSession(); Transaction tx1 = session1.beginTransaction(); Cat cat = session1.get(Cat.class, catId); tx1.commit(); session1.close();//reconcile with a second database Session session2 = factory2.openSession(); Transaction tx2 = session2.beginTransaction(); session2.replicate(cat, ReplicationMode.LATEST_VERSION); tx2.commit(); session2.close();

    ReplicationMode決定數(shù)據(jù)庫(kù)中已存在相同行時(shí),replicate()如何處理。

    • ReplicationMode.IGNORE - 忽略它

    • ReplicationMode.OVERWRITE - 覆蓋相同的行

    • ReplicationMode.EXCEPTION - 拋出異常

    • ReplicationMode.LATEST_VERSION - 如果當(dāng)前的版本較新,則覆蓋,否則忽略

    這個(gè)功能的用途包括使錄入的數(shù)據(jù)在不同數(shù)據(jù)庫(kù)中一致,產(chǎn)品升級(jí)時(shí)升級(jí)系統(tǒng)配置信息,回滾non-ACID事務(wù)中的修改等等。 (譯注,non-ACID,非ACID;ACID,Atomic,Consistent,Isolated and Durable的縮寫(xiě))

    10.10.?Session刷出(flush)

    每間隔一段時(shí)間,Session會(huì)執(zhí)行一些必需的SQL語(yǔ)句來(lái)把內(nèi)存中的對(duì)象的狀態(tài)同步到JDBC連接中。這個(gè)過(guò)程被稱(chēng)為刷出(flush),默認(rèn)會(huì)在下面的時(shí)間點(diǎn)執(zhí)行:

    • 在某些查詢(xún)執(zhí)行之前

    • 在調(diào)用org.hibernate.Transaction.commit()的時(shí)候

    • 在調(diào)用Session.flush()的時(shí)候

    涉及的SQL語(yǔ)句會(huì)按照下面的順序發(fā)出執(zhí)行:

  • 所有對(duì)實(shí)體進(jìn)行插入的語(yǔ)句,其順序按照對(duì)象執(zhí)行Session.save()的時(shí)間順序

  • 所有對(duì)實(shí)體進(jìn)行更新的語(yǔ)句

  • 所有進(jìn)行集合刪除的語(yǔ)句

  • 所有對(duì)集合元素進(jìn)行刪除,更新或者插入的語(yǔ)句

  • 所有進(jìn)行集合插入的語(yǔ)句

  • 所有對(duì)實(shí)體進(jìn)行刪除的語(yǔ)句,其順序按照對(duì)象執(zhí)行Session.delete()的時(shí)間順序

  • (有一個(gè)例外是,如果對(duì)象使用native方式來(lái)生成ID(持久化標(biāo)識(shí))的話,它們一執(zhí)行save就會(huì)被插入。)

    除非你明確地發(fā)出了flush()指令,關(guān)于Session何時(shí)會(huì)執(zhí)行這些JDBC調(diào)用是完全無(wú)法保證的,只能保證它們執(zhí)行的前后順序。 當(dāng)然,Hibernate保證,Query.list(..)絕對(duì)不會(huì)返回已經(jīng)失效的數(shù)據(jù),也不會(huì)返回錯(cuò)誤數(shù)據(jù)。

    也可以改變默認(rèn)的設(shè)置,來(lái)讓刷出(flush)操作發(fā)生的不那么頻繁。 FlushMode類(lèi)定義了三種不同的方式。 僅在提交時(shí)刷出(僅當(dāng)Hibernate的Transaction API被使用時(shí)有效), 按照剛才說(shuō)的方式刷出, 以及除非明確使用flush()否則從不刷出。 最后一種模式對(duì)于那些需要長(zhǎng)時(shí)間保持Session為打開(kāi)或者斷線狀態(tài)的長(zhǎng)時(shí)間運(yùn)行的工作單元很有用。 (參見(jiàn) 第?11.3.2?節(jié) “長(zhǎng)生命周期session和自動(dòng)版本化”).

    sess = sf.openSession(); Transaction tx = sess.beginTransaction(); sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale stateCat izi = (Cat) sess.load(Cat.class, id); izi.setName(iznizi);// might return stale data sess.find("from Cat as cat left outer join cat.kittens kitten");// change to izi is not flushed! ... tx.commit(); // flush occurs

    刷出(flush)期間,可能會(huì)拋出異常。(例如一個(gè)DML操作違反了約束) 異常處理涉及到對(duì)Hibernate事務(wù)性行為的理解,因此我們將在第?11?章 事務(wù)和并發(fā)中討論。

    10.11.?傳播性持久化(transitive persistence)

    對(duì)每一個(gè)對(duì)象都要執(zhí)行保存,刪除或重關(guān)聯(lián)操作讓人感覺(jué)有點(diǎn)麻煩,尤其是在處理許多彼此關(guān)聯(lián)的對(duì)象的時(shí)候。 一個(gè)常見(jiàn)的例子是父子關(guān)系。考慮下面的例子:

    如果一個(gè)父子關(guān)系中的子對(duì)象是值類(lèi)型(value typed)(例如,地址或字符串的集合)的,他們的生命周期會(huì)依賴(lài)于父對(duì)象,可以享受方便的級(jí)聯(lián)操作(Cascading),不需要額外的動(dòng)作。 父對(duì)象被保存時(shí),這些值類(lèi)型(value typed)子對(duì)象也將被保存;父對(duì)象被刪除時(shí),子對(duì)象也將被刪除。 這對(duì)將一個(gè)子對(duì)象從集合中移除是同樣有效:Hibernate會(huì)檢測(cè)到,并且因?yàn)橹殿?lèi)型(value typed)的對(duì)象不可能被其他對(duì)象引用,所以Hibernate會(huì)在數(shù)據(jù)庫(kù)中刪除這個(gè)子對(duì)象。

    現(xiàn)在考慮同樣的場(chǎng)景,不過(guò)父子對(duì)象都是實(shí)體(entities)類(lèi)型,而非值類(lèi)型(value typed)(例如,類(lèi)別與個(gè)體,或母貓和小貓)。 實(shí)體有自己的生命期,允許共享對(duì)其的引用(因此從集合中移除一個(gè)實(shí)體,不意味著它可以被刪除), 并且實(shí)體到其他關(guān)聯(lián)實(shí)體之間默認(rèn)沒(méi)有級(jí)聯(lián)操作的設(shè)置。 Hibernate默認(rèn)不實(shí)現(xiàn)所謂的可到達(dá)即持久化(persistence by reachability)的策略。

    每個(gè)Hibernate session的基本操作 - 包括 persist(), merge(), saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate() - 都有對(duì)應(yīng)的級(jí)聯(lián)風(fēng)格(cascade style)。 這些級(jí)聯(lián)風(fēng)格(cascade style)風(fēng)格分別命名為 create, merge, save-update, delete, lock, refresh, evict, replicate。 如果你希望一個(gè)操作被順著關(guān)聯(lián)關(guān)系級(jí)聯(lián)傳播,你必須在映射文件中指出這一點(diǎn)。例如:

    <one-to-one name="person" cascade="create"/>

    級(jí)聯(lián)風(fēng)格(cascade style)是可組合的:

    <one-to-one name="person" cascade="create,delete,lock"/>

    你可以使用cascade="all"來(lái)指定全部操作都順著關(guān)聯(lián)關(guān)系級(jí)聯(lián)(cascaded)。 默認(rèn)值是cascade="none",即任何操作都不會(huì)被級(jí)聯(lián)(cascaded)。

    注意有一個(gè)特殊的級(jí)聯(lián)風(fēng)格(cascade style) delete-orphan,只應(yīng)用于one-to-many關(guān)聯(lián),表明delete()操作 應(yīng)該被應(yīng)用于所有從關(guān)聯(lián)中刪除的對(duì)象。

    建議:

    • 通常在<many-to-one>或<many-to-many>關(guān)系中應(yīng)用級(jí)聯(lián)(cascade)沒(méi)什么意義。 級(jí)聯(lián)(cascade)通常在 <one-to-one>和<one-to-many>關(guān)系中比較有用。

    • 如果子對(duì)象的壽命限定在父親對(duì)象的壽命之內(nèi),可通過(guò)指定cascade="all,delete-orphan"將其變?yōu)?span id="ozvdkddzhkzd" class="emphasis">自動(dòng)生命周期管理的對(duì)象(lifecycle object)。

    • 其他情況,你可根本不需要級(jí)聯(lián)(cascade)。但是如果你認(rèn)為你會(huì)經(jīng)常在某個(gè)事務(wù)中同時(shí)用到父對(duì)象與子對(duì)象,并且你希望少打點(diǎn)兒字,可以考慮使用cascade="create,merge,save-update"。

    可以使用cascade="all"將一個(gè)關(guān)聯(lián)關(guān)系(無(wú)論是對(duì)值對(duì)象的關(guān)聯(lián),或者對(duì)一個(gè)集合的關(guān)聯(lián))標(biāo)記為父/子關(guān)系的關(guān)聯(lián)。 這樣對(duì)父對(duì)象進(jìn)行save/update/delete操作就會(huì)導(dǎo)致子對(duì)象也進(jìn)行save/update/delete操作。

    此外,一個(gè)持久的父對(duì)象對(duì)子對(duì)象的淺引用(mere reference)會(huì)導(dǎo)致子對(duì)象被同步save/update。 不過(guò),這個(gè)隱喻(metaphor)的說(shuō)法并不完整。除非關(guān)聯(lián)是<one-to-many>關(guān)聯(lián)并且被標(biāo)記為cascade="delete-orphan", 否則父對(duì)象失去對(duì)某個(gè)子對(duì)象的引用不會(huì)導(dǎo)致該子對(duì)象被自動(dòng)刪除。 父子關(guān)系的級(jí)聯(lián)(cascading)操作準(zhǔn)確語(yǔ)義如下:

    • 如果父對(duì)象被persist(),那么所有子對(duì)象也會(huì)被persist()

    • 如果父對(duì)象被merge(),那么所有子對(duì)象也會(huì)被merge()

    • 如果父對(duì)象被save(),update()或 saveOrUpdate(),那么所有子對(duì)象則會(huì)被saveOrUpdate()

    • 如果某個(gè)持久的父對(duì)象引用了瞬時(shí)(transient)或者脫管(detached)的子對(duì)象,那么子對(duì)象將會(huì)被saveOrUpdate()

    • 如果父對(duì)象被刪除,那么所有子對(duì)象也會(huì)被delete()

    • 除非被標(biāo)記為cascade="delete-orphan"(刪除“孤兒”模式,此時(shí)不被任何一個(gè)父對(duì)象引用的子對(duì)象會(huì)被刪除), 否則子對(duì)象失掉父對(duì)象對(duì)其的引用時(shí),什么事也不會(huì)發(fā)生。 如果有特殊需要,應(yīng)用程序可通過(guò)顯式調(diào)用delete()刪除子對(duì)象。

    10.12.?使用元數(shù)據(jù)

    Hibernate中有一個(gè)非常豐富的元級(jí)別(meta-level)的模型,含有所有的實(shí)體和值類(lèi)型數(shù)據(jù)的元數(shù)據(jù)。 有時(shí)這個(gè)模型對(duì)應(yīng)用程序本身也會(huì)非常有用。 比如說(shuō),應(yīng)用程序可能在實(shí)現(xiàn)一種“智能”的深度拷貝算法時(shí), 通過(guò)使用Hibernate的元數(shù)據(jù)來(lái)了解哪些對(duì)象應(yīng)該被拷貝(比如,可變的值類(lèi)型數(shù)據(jù)), 那些不應(yīng)該(不可變的值類(lèi)型數(shù)據(jù),也許還有某些被關(guān)聯(lián)的實(shí)體)。

    Hibernate提供了ClassMetadata接口,CollectionMetadata接口和Type層次體系來(lái)訪問(wèn)元數(shù)據(jù)。 可以通過(guò)SessionFactory獲取元數(shù)據(jù)接口的實(shí)例。

    Cat fritz = ......; ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);Object[] propertyValues = catMeta.getPropertyValues(fritz); String[] propertyNames = catMeta.getPropertyNames(); Type[] propertyTypes = catMeta.getPropertyTypes();// get a Map of all properties which are not collections or associations Map namedValues = new HashMap(); for ( int i=0; i<propertyNames.length; i++ ) {if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) {namedValues.put( propertyNames[i], propertyValues[i] );} }

    ?

    總結(jié)

    以上是生活随笔為你收集整理的session 中对象实例在不同事务中的状态的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。