Hibernate內(nèi)嵌了對C3P0,Proxool,JNDI數(shù)據(jù)源等數(shù)據(jù)庫連接池的支持。但當(dāng)我們需要使用除了這幾個(gè)數(shù)據(jù)源外的其他數(shù)據(jù)源的時(shí)候就有問題了,例如我們需要用Apache的開源連接池項(xiàng)目DBCP,或者說我們想要使用多數(shù)JDBC驅(qū)動(dòng)程序中自帶的XxxxDataSource時(shí),Hibernate就沒有提供對這方面的支持。慶幸的是Hibernate做為一個(gè)強(qiáng)大的數(shù)據(jù)持久層組件,它在實(shí)現(xiàn)數(shù)據(jù)庫連接方面的擴(kuò)展性也是非常強(qiáng)大的。本文將介紹兩種如何在Hibernate項(xiàng)目中使用自定義數(shù)據(jù)源的方法。
本文假設(shè)你已經(jīng)有Hibernate的開發(fā)經(jīng)驗(yàn)。
在開始之前應(yīng)該先明確你的項(xiàng)目中的具體情況,也就是確認(rèn)Hibernate內(nèi)嵌的組件是否真的無法支持你的應(yīng)用需要。例如C3P0或者Proxool已經(jīng)可以滿足大部分?jǐn)?shù)據(jù)庫的需要,又或者你的數(shù)據(jù)源是在應(yīng)用服務(wù)器中配置的,那么你也沒有必要進(jìn)行擴(kuò)展,你可以直接使用DatasourceConnectionProvider來讓Hibernate使用你所定義的數(shù)據(jù)源。
那么什么時(shí)候你需要擴(kuò)展Hibernate對數(shù)據(jù)源的支持呢?可能你永遠(yuǎn)也用不上,但我一直在用。我用的原因可能不能成為正當(dāng)?shù)睦碛桑驗(yàn)镃3P0或者Proxool總有些小地方的不足讓我不爽,個(gè)人更偏向于DBCP連接池。或許本文應(yīng)該改名為《讓Hibernate支持DBCP數(shù)據(jù)源》,其實(shí)DBCP只不過是我的一個(gè)具體的例子,本文具有更普遍的應(yīng)用意義。下面我們具體介紹兩種不同的擴(kuò)展思路。
假設(shè)我們已經(jīng)有了一個(gè)WEB項(xiàng)目,該項(xiàng)目采用了Struts框架,而且我們已經(jīng)在Struts中配置了數(shù)據(jù)源,也有不少的代碼是依賴這個(gè)數(shù)據(jù)源運(yùn)行的。現(xiàn)在我們需要給項(xiàng)目中加入對Hibernate的支持,但又不想去修改舊的已經(jīng)成功穩(wěn)定運(yùn)行的代碼了。那我們該怎么辦?如果同樣在Hibernate配置一個(gè)數(shù)據(jù)源指到同一個(gè)數(shù)據(jù)庫,相信你也不樂意這樣干,因?yàn)橐坏┡渲蒙嫌行薷哪敲碨truts和Hibernate的配置都需要修改,這個(gè)也只是麻煩一點(diǎn)而已,最要命的是沒法讓原有的代碼和Hibernate共用一個(gè)數(shù)據(jù)庫連接,因此事務(wù)處理也就無從談起。
說那么多理由,無非就是為了讓Hibernate可以使用Struts中配置的數(shù)據(jù)源,而我們暫且不去考慮這是否是最好的解決方法。
在Hibernate中有一個(gè)UserSuppliedConnectionProvider類,其實(shí)這個(gè)類什么也不干,你一旦讓它干點(diǎn)啥吧,它還凈出異常,搞得你很是惱火。在Hibernate中,這個(gè)類的含義是要求用戶自己來提供數(shù)據(jù)庫連接的獲取方法,同時(shí)當(dāng)然也要自己負(fù)責(zé)關(guān)閉連接。
為了使用Struts中配置的數(shù)據(jù)源,我們就不能直接調(diào)用SessionFactory.openSession()方法來獲取Session實(shí)例,因?yàn)槟闳绻麤]有在Hibernate中配置任何的數(shù)據(jù)庫連接,那Hibernate會默認(rèn)讓UserSuppliedConnectionProvider類來跟你搗亂,你會收到很多異常信息,反復(fù)提醒我們必須自己提供數(shù)據(jù)庫連接!我們要做還是調(diào)用openSession方法,不同的是需要先從Struts的數(shù)據(jù)源中獲取數(shù)據(jù)庫連接,然后傳遞該連接給openSession方法(參照 SessionFactory.openSession(Connection) 方法)。
下面是我寫的一個(gè)代碼片斷
|
需要提醒大家注意的是closeSession方法,在該方法中我們必須手工去關(guān)閉session對應(yīng)的數(shù)據(jù)庫連接,我們前面已經(jīng)提到了,UserSuppliedConnectionProvider類就是要求用戶自己提供數(shù)據(jù)庫連接已經(jīng)連接的關(guān)閉。如果沒有調(diào)用ssn.connection().close()方法,這會導(dǎo)致Struts的數(shù)據(jù)源的連接沒有被釋放。
同理,上面提到的Struts只是一個(gè)應(yīng)用普遍的例子,實(shí)際中你可以使用任何的外部連接池,你只需要將獲取到的數(shù)據(jù)庫連接傳遞給openSession方法,并自行負(fù)責(zé)釋放數(shù)據(jù)庫連接即可。應(yīng)該說這是一種最簡單的思路,好處是對系統(tǒng)的變動(dòng)最小,兼容原來的代碼。
![]() |
|
Hibernate本身是通過ConnectionProvider接口來實(shí)現(xiàn)管理數(shù)據(jù)庫連接的。例如其自帶的C3P0ConnectionProvider,ProxoolConnectionProvider等。
在這個(gè)思路中,我們希望可以直接在Hibernate的配置文件中配置數(shù)據(jù)庫連接,也就是讓Hibernate獨(dú)攬數(shù)據(jù)庫的管理,真正做到各司其職。為了更了解該接口的使用,你不妨閱讀一下Hibernate提供的上面幾個(gè)類的源碼。
接下來我們需要編寫一個(gè)實(shí)現(xiàn)了ConnectionProvider接口的類,要求這個(gè)類能支持任何的符合DataSource接口規(guī)范的數(shù)據(jù)源,同時(shí)在Hibernate的配置文件中進(jìn)行參數(shù)的設(shè)定。首先我們假定我們的類名是DataSourceConnProvider,那我們的配置信息在hibernate.cfg.xml中看起來應(yīng)該像下面一樣
|
在上面的配置信息中,connection.provider_class是Hibernate本身用來指定不同ConnectionProvider實(shí)現(xiàn)類。接下來我們規(guī)定了我們的擴(kuò)展所使用的配置鍵值都是以dscp.開頭,同時(shí)我們使用dscp.datasource來指定具體實(shí)現(xiàn)了DataSource接口的類名,例如如果使用DBCP這個(gè)連接池,那么這個(gè)類名應(yīng)該是org.apache.commons.dbcp.BasicDataSource。對于其他以dscp.開頭的且不是dscp.datasource的配置信息都會直接賦值給DataSource的實(shí)現(xiàn)類。例如上面的配置中,driverClassName、url、username、password等配置信息都是BasicDataSource類的屬性。
下面是我們所實(shí)現(xiàn)的DataSourceConnProvider類的源碼。
|
在DataSourceConnProvider類中,configure方法會在Hibernate進(jìn)行初始化的過程中被調(diào)用,我們根據(jù)配置的DataSource類名創(chuàng)建數(shù)據(jù)源實(shí)例,并將配置參數(shù)賦值給該實(shí)例后即完成了數(shù)據(jù)源的初始化。接下來就是實(shí)現(xiàn)了getConnection和closeConnection方法分別是獲取數(shù)據(jù)庫連接和關(guān)閉連接的方法。方法close用來關(guān)閉整個(gè)數(shù)據(jù)源,該方法會在Hibernate釋放時(shí)被調(diào)用。
你也可以使用其他一些不同的數(shù)據(jù)源而不一定非是DBCP數(shù)據(jù)源。配置完畢后接下來的事情就簡單了,直接調(diào)用SessionFactory.openSession()方法獲取Session實(shí)例,直接調(diào)用session.close()釋放該實(shí)例,無需再手工去關(guān)閉session所封裝的connection接口。
相比較上面兩種思路而言,各有千秋。如果你真的有必要擴(kuò)展Hibernate對數(shù)據(jù)源的支持,如果你沒有兼容舊代碼這個(gè)問題需要考慮的話,那我更傾向于第二種思路。因?yàn)樗沟谜麄€(gè)項(xiàng)目的各個(gè)層次分工非常清晰,而且除了ConnectionProvider 類以外應(yīng)用的代碼也相對簡單。