Java學習實戰教程之Hibernate使用Oracle資料庫遇到的幾個問題
前言
Hibernate作為一個成熟的持久層解決方案,通過簡單的配置,可以在不修改原始碼的情況下,無縫的在不同的資料庫上執行。儘管如此,使用不同的資料庫仍然會出現一些問題,比如本文要討論的Oracle中的問題。
1. Hibernate無法建立hibernate_sequence的問題
錯誤提示:
問題分析:
從錯誤提示上看,查詢語句沒有找到hibernate_sequence這個序列。到Oracle資料庫中檢視,果然沒有。按道理說,hibernate應該在Oracle中自動生成這個序列,那為什麼沒有生成呢?原因主要有兩個:
1.hibernate的核心配置中沒有設定自動建物件的屬性,導致無法自動建立序列:
<property name="hibernate.hbm2ddl.auto">update</property> |
2.其他使用者存在hibernate_sequence這個序列,導致當前使用者不再建立該序列。
原因:
hibernate在啟動時即需要去檢測所要建立的序列是否存在,如果存在,則不再建立此序列。在某些情況下,hibernate能夠探測到其它使用者所建立的同名序列,而被認為不再需要建立序列,這時候如果進行儲存資料的操作,就會因為在當前使用者下找不到該序列從而導致出錯。
要解決這個問題的首要方法是需要找到hibernate判斷指定序列是否存在的方法,在hibernate的dialet中,取決於getQuerySequencesString這個方法,此方法返回了查詢所有序列的方法。
Hibernate中的與Oracle相關dialet一般配置為Oracle10gDialect、Oracle9iDialect、Oracle8iDialect。從原始碼我們可以看到他們之間是繼承關係,如:Oracle10gDialect extend Oracle9iDialect extend Oracle8iDialect extend Dialet。通過原始碼我們可以看出,該方法只是在Oracle8iDialect中進行了覆寫,其子類都沿用了該實現。實現程式碼如下:
publicString getQuerySequencesString() { return " select sequence_name from all_sequences" + " union" + " select synonym_name" + " from all_synonyms us, all_sequences asq" + " where asq.sequence_name = us.table_name" + " and asq.sequence_owner = us.table_owner"; } |
從程式碼分析看,它是從all_sequence中尋找所能找到的sequence,而當前使用者能找到的sequence全在於當前使用者能有多大的許可權,以及具體的oracle jdbc實現。在這種情況下,如果當前使用者擁有dba許可權(開發時候,經常給一個使用者授dba許可權)或者由於其它某種原因,就能夠找到其他使用者的sequence了。而如果其他使用者也建立了一個叫hibernate_sequence的sequence,那hibernate在當前使用者就不能再建立sequence了。
知道了這個原因後,如何解決呢?
我們可以建立一個新的OracleDialect,直接繼承於Oracle10gDialect,並在新的類中覆蓋getQuerySequencesString方法,將其中的sql語句改為從user_sequences(即當前使用者的sequence空間)中尋找相應的序列,這樣就不會尋找到其它使用者的序列了。參考程式碼如下:
public class Oracle10gDialectEx extends Oracle10gDialect { public String getQuerySequencesString() { return "select sequence_name from user_sequences" ; } } |
還有個解決辦法,就是去掉該Oracle使用者的跨使用者訪問的許可權,如select any table的許可權或dba角色。
2. 使用hibernate生成表不能正確建立表的問題
問題:
hbm2ddl.auto配置成update時,發現hibernate並沒有按照預設的生成規則生成相應的資料表資訊
分析發現,這裡需要引用的表p_menu在另一個使用者空間裡已經存在了,而hibernate在建立表時,在另一個使用者空間中找到了這個表,故不再在當前的使用者空間中建立這個表了。而在建立關聯表時,由於關聯的是本使用者空間的表,故有此錯誤。
hibernate使用了jdbc預設的databasemeta來尋找相應表資料資訊,當使用預設的配置時,由於某種原因(並不是每次都能發生,取決於資料庫本身以及相應的驅動)。當使用當前使用者連線到資料庫時,使用databasemeta尋找資料庫表資訊時,會查詢出其它使用者的資料表資訊(即使當前使用者沒有相應的許可權)。
兩種解決方案:
1.在hibernate核心配置中配置一個屬性:
< property name = " hibernate.default_schema" > 當前連線使用者</ property > |
2.在hbm檔案配置中的class元素中指定schema=""的屬性
<class name="class" table="table"schema="當前連線使用者 "> |
3.去掉該Oracle使用者的跨使用者訪問的許可權,如select any table的許可權
3. SSH啟動中的輸出日誌中有createClob的info資訊的問題
在使用SSH+Oracle啟動的時候,控制檯會列印:
INFO: HHH000424: Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException
如果是使用的Oracle10G,實體類使用了Clob欄位的時候,會丟擲這個資訊。
如果使用的是Oracle11G,實體類使用了複雜的資料型別的時候,也會丟擲這個資訊。
分析:
這個東西只是個 INFO 級別的,也就是說僅僅是提醒,無關緊要的。但總覺的不爽。如何解決呢?
從驅動上尋找解決方案:
使用Oracle10G,將預設的JDBC驅動ojdbc14升級為更高版本,如Oracle11G的ojdbc6.jar的版本。
從hibernate上尋找解決方案:
修改hibernate配置項hibernate.temp.use_jdbc_metadata_defaults=false。
另外:Hibernate4的官方資料裡說11g和10g都是Oracle10gDialect,方言通用,這個沒啥影響。