前言
Hibernate作為一個成熟的持久層解決方案,通過簡單的配置,可以在不修改源碼的情況下,無縫的在不同的數(shù)據(jù)庫上運行。盡管如此,使用不同的數(shù)據(jù)庫仍然會出現(xiàn)一些問題,比如本文要討論的Oracle中的問題。
1. Hibernate無法創(chuàng)建hibernate_sequence的問題
錯誤提示:
問題分析:
從錯誤提示上看,查詢語句沒有找到hibernate_sequence這個序列。到Oracle數(shù)據(jù)庫中查看,果然沒有。按道理說,hibernate應該在Oracle中自動生成這個序列,那為什么沒有生成呢?原因主要有兩個:
1.hibernate的核心配置中沒有設置自動建對象的屬性,導致無法自動建立序列:
<property name="hibernate.hbm2ddl.auto">update</property> |
2.其他用戶存在hibernate_sequence這個序列,導致當前用戶不再創(chuàng)建該序列。
原因:
hibernate在啟動時即需要去檢測所要創(chuàng)建的序列是否存在,如果存在,則不再創(chuàng)建此序列。在某些情況下,hibernate能夠探測到其它用戶所創(chuàng)建的同名序列,而被認為不再需要創(chuàng)建序列,這時候如果進行保存數(shù)據(jù)的操作,就會因為在當前用戶下找不到該序列從而導致出錯。
要解決這個問題的首要方法是需要找到hibernate判斷指定序列是否存在的方法,在hibernate的dialet中,取決于getQuerySequencesString這個方法,此方法返回了查詢所有序列的方法。
Hibernate中的與Oracle相關dialet一般配置為Oracle10gDialect、Oracle9iDialect、Oracle8iDialect。從源碼我們可以看到他們之間是繼承關系,如:Oracle10gDialect extend Oracle9iDialect extend Oracle8iDialect extend Dialet。通過源代碼我們可以看出,該方法只是在Oracle8iDialect中進行了覆寫,其子類都沿用了該實現(xiàn)。實現(xiàn)代碼如下:
public String 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實現(xiàn)。在這種情況下,如果當前用戶擁有dba權限(開發(fā)時候,經常給一個用戶授dba權限)或者由于其它某種原因,就能夠找到其他用戶的sequence了。而如果其他用戶也創(chuàng)建了一個叫hibernate_sequence的sequence,那hibernate在當前用戶就不能再創(chuàng)建sequence了。
知道了這個原因后,如何解決呢?
我們可以創(chuàng)建一個新的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生成表不能正確創(chuàng)建表的問題
問題:
hbm2ddl.auto配置成update時,發(fā)現(xiàn)hibernate并沒有按照默認的生成規(guī)則生成相應的數(shù)據(jù)表信息
分析發(fā)現(xiàn),這里需要引用的表p_menu在另一個用戶空間里已經存在了,而hibernate在創(chuàng)建表時,在另一個用戶空間中找到了這個表,故不再在當前的用戶空間中創(chuàng)建這個表了。而在創(chuàng)建關聯(lián)表時,由于關聯(lián)的是本用戶空間的表,故有此錯誤。
hibernate使用了jdbc默認的databasemeta來尋找相應表數(shù)據(jù)信息,當使用默認的配置時,由于某種原因(并不是每次都能發(fā)生,取決于數(shù)據(jù)庫本身以及相應的驅動)。當使用當前用戶連接到數(shù)據(jù)庫時,使用databasemeta尋找數(shù)據(jù)庫表信息時,會查詢出其它用戶的數(shù)據(jù)表信息(即使當前用戶沒有相應的權限)。
兩種解決方案:
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,實體類使用了復雜的數(shù)據(jù)類型的時候,也會拋出這個信息。
分析:
這個東西只是個 INFO 級別的,也就是說僅僅是提醒,無關緊要的。但總覺的不爽。如何解決呢?
從驅動上尋找解決方案:
使用Oracle10G,將默認的JDBC驅動ojdbc14升級為更高版本,如Oracle11G的ojdbc6.jar的版本。
從hibernate上尋找解決方案:
修改hibernate配置項hibernate.temp.use_jdbc_metadata_defaults=false。
另外:Hibernate4的官方資料里說11g和10g都是Oracle10gDialect,方言通用,這個沒啥影響。
網(wǎng)上參考:http://stackoverflow.com/questions/4588755/hibernate-disabling-contextual-lob-creation-as-createclob-method-threw-error