1. 程式人生 > 其它 >Java學習實戰教程之Hibernate使用Oracle資料庫遇到的幾個問題

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空間)中尋找相應的序列,這樣就不會尋找到其它使用者的序列了。參考程式碼如下:

publicclassOracle10gDialectExextendsOracle10gDialect {
publicString 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核心配置中配置一個屬性:

<propertyname="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,方言通用,這個沒啥影響。