首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Hibernate 2 升級 3:解決 Oracle 8 外連接 (+) 語法不支援問題

     編輯:Java 相關知識

背景與問題

維護一個 Hibernate 2 + Oracle 8 的老專案,嘗試升級至 Hibernate 3 以獲得批量刪除/更新、新 HQL 解析器(AST)等新特性。按文檔替換 Jar 包、調整配置後啟動,遇到經典錯誤:

org.hibernate.hql.ast.QuerySyntaxException: unexpected token: ) near line 1, column 106
[select t1.c1,t2.c1 from Table1 t1,Table2 t2 where t1.c1=t2.c1(+)]

原因:Hibernate 3 預設使用 ASTQueryTranslatorFactory,不再支援 Oracle 8i / Sybase 11 的 Theta-Style 外連接語法(即 (+))。

兩種官方解決方案及其缺陷

方案一:改用 ANSI SQL 標準語法

select t1.c1, t2.c1 from Table1 t1 left outer join Table2 t2 on t1.c1 = t2.c1

缺點:要求映射檔中預先配置實體間的關聯關係(many-to-one 等),若不想維護關聯映射則無法使用。

方案二:回退到 Hibernate 2 的 Classic 解析器

在 hibernate.cfg.xml 中配置:

<property name="query.factory_class">
    org.hibernate.hql.classic.ClassicQueryTranslatorFactory
</property>

缺點:Classic 解析器不支援 Hibernate 3 新增的 delete/update ... where 批量操作語法,會拋出 query must begin with SELECT or FROM 錯誤。且 Session.delete(String hql) 方法已被移除,陷入兩難。

終極解決方案:動態選擇解析器

核心思路:根據 HQL 內容動態決定使用哪個解析器。檢測到 (+) 時用 Classic,否則用 AST,兼得兩者優點。

修改 HQLQueryPlan 構造函數

定位 org.hibernate.engine.query.HQLQueryPlan 的五參數構造函數,其中透過 factory.getSettings().getQueryTranslatorFactory() 獲取解析器。修改邏輯如下:

if (collectionRole == null) {
    // 主查詢
    if (hql.replaceAll("\\s*", "").indexOf("(+)") != -1) {
        translators[i] = new ClassicQueryTranslatorFactory()
            .createQueryTranslator(hql, concreteQueryStrings[i], enabledFilters, factory);
    } else {
        translators[i] = factory.getSettings().getQueryTranslatorFactory()
            .createQueryTranslator(hql, concreteQueryStrings[i], enabledFilters, factory);
    }
    translators[i].compile(factory.getSettings().getQuerySubstitutions(), shallow);
} else {
    // 過濾器查詢
    if (hql.replaceAll("\\s*", "").indexOf("(+)") != -1) {
        translators[i] = new ClassicQueryTranslatorFactory()
            .createFilterTranslator(hql, concreteQueryStrings[i], enabledFilters, factory);
    } else {
        translators[i] = factory.getSettings().getQueryTranslatorFactory()
            .createFilterTranslator(hql, concreteQueryStrings[i], enabledFilters, factory);
    }
    ((FilterTranslator) translators[i]).compile(collectionRole,
        factory.getSettings().getQuerySubstitutions(), shallow);
}

部署方式

  1. 編譯修改後的 HQLQueryPlan.java。
  2. 將 HQLQueryPlan.class 覆蓋回 hibernate3.jar 對應目錄,或置於專案 WEB-INF/classes(類加載優先級高於 Jar)。

替代方案:轉型使用 Classic Session

Hibernate 3 的 SessionFactory.openSession() 返回 org.hibernate.classic.Session(繼承自 org.hibernate.Session)。可強制轉型:

((org.hibernate.classic.Session) session).delete("from User u where u.name='Unmi'");

缺點:仍需全局使用 Classic 解析器,失去新版批量操作優勢,不推薦長期使用。

補充:HQL 查詢計劃快取

Hibernate 會在 QueryPlanCache 中以 HQLQueryPlanKey(queryString, shallow, enabledFilters) 為鍵快取計劃。動態解析器不影響快取鍵值,相同 HQL 仍會複用計劃。

總結

修改 HQLQueryPlan 實現「按需選擇解析器」是最佳平衡:既保留 Oracle (+) 相容性,又享用 Hibernate 3 批量 DML、新 HQL 語法等現代特性。生產環境建議配合完整測試後發布。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved