簡體   English   中英

如何在 Hibernate HQL 中使用 Oracle 的 regexp_like?

[英]how to use Oracle's regexp_like in Hibernate HQL?

我正在使用oracle 10ghibernate 3.3.2 我以前在sql中使用過正則表達式,現在我第一次在HQL中使用它。

Query query = getSession().createQuery("From Company company 
where company.id!=:companyId and 
regexp_like(upper(rtrim(ltrim(company.num))), '^0*514619915$' )");

這是我的 hql,當我在沒有regex_like function 的情況下運行它時,它按預期運行。 但我無法使用regex_like表達式執行它。

它說..

嵌套異常是 org.hibernate.hql.ast.QuerySyntaxException: unexpected AST node: ( near line 1, column 66 .....

請幫助,如何在 hibernate 本機查詢中使用regex_like 或者其他一些替代方法。

實際上,除了PL / SQL中的條件語句之外,您無法將REGEXP_LIKE的結果與任何內容進行比較。

Hibernate似乎不接受沒有returnType的自定義函數,因為你總是需要將輸出與某些東西進行比較,即:

REGEXP_LIKE('bananas', 'a', 'i') = 1

由於Oracle不允許您將此函數的結果與任何內容進行比較,因此我提出了使用案例條件的解決方案:

public class Oracle10gExtendedDialect extends Oracle10gDialect {

    public Oracle10gExtendedDialect() {
        super();
        registerFunction(
          "regexp_like", new SQLFunctionTemplate(StandardBasicTypes.BOOLEAN,
          "(case when (regexp_like(?1, ?2, ?3)) then 1 else 0 end)")
        );
    }

}

你的HQL應該是這樣的:

REGEXP_LIKE('bananas', 'a', 'i') = 1

它會工作:)

你絕對可以使用Hibernate HQL所希望的任何類型的特定於數據庫的函數(只要Hibernate是提供者,就可以使用JPQL)。 你只需要告訴Hibernate這些功能。 在3.3中,唯一的選擇是提供自定義Dialect並從Dialect的構造函數中注冊該函數。 如果你看一下Dialect基類,你會看到許多注冊函數的例子。 通常最好擴展您當前使用的精確方言,並簡單地提供您的擴展(此處,注冊該功能)。

一個有趣的說明是Oracle沒有將regexp_like歸類為函數。 他們將其歸類為條件/謂詞。 我認為這主要是因為Oracle SQL沒有定義BOOLEAN數據類型,即使他們的PL / SQL確實如此,我敢打賭regexp_like被定義為返回BOOLEAN的PL / SQL函數...

假設您當前使用的是Oracle10gDialect,您可以:

public class MyOracle10gDialect extends Oracle10gDialect {
    public Oracle10gDialect() {
        super();

        registerFunction( 
            "regexp_like", 
             new StandardSQLFunction( "regexp_like", StandardBasicTypes.BOOLEAN )
        );
    }
}

我不記得HQL解析器是否喜歡返回布爾值的函數,但是它本身就是一個謂詞。 您可能必須將true / false轉換為其他內容並檢查該返回:

public class MyOracle10gDialect extends Oracle10gDialect {
    public Oracle10gDialect() {
        super();

        registerFunction( 
            "regexp_like", 
             new StandardSQLFunction( "regexp_like", StandardBasicTypes.INTEGER ) {
                 @Override
                 public String render(
                         Type firstArgumentType, 
                         List arguments, 
                         SessionFactoryImplementor factory) {
                     return "some_conversion_from_boolean_to_int(" + 
                             super.render( firstArgumentType, arguments, factory ) +
                             ")";
                 }
             }
        );
    }
}

您可以嘗試使用標准LIKE運算符:

where company.num like '%514619915'

然后使用Java正則表達式過濾掉不需要的那些。 這應該減少將返回的不需要的行數。

這不會使用索引,因為它以'%'開頭。

除非JPAQL / HQL提供了這樣做的方法,否則您無法訪問特定的數據庫函數,也無法為正則表達式提供任何內容。 因此,您需要編寫本機SQL查詢以使用正則表達式。

在另一個非常重要的問題上,一些同事(Oracle DBA)告訴我永遠不要在oracle中使用正則表達式,因為它們無法編入索引,最終會在數據庫中執行完整的數據庫掃描。 如果表有幾個條目,那么沒關系,但如果它有很多行,它可能會削弱性能。

對於那些使用Hibernate標准和sqlRestriction的人(Hibernate Version 4.2.7)

 Criterion someCriterion = Restrictions.sqlRestriction("regexp_like (column_name, ?, 'i')", "(^|\\s)"+searchValue+"($|\\s|.$)", StringType.INSTANCE);

或者另一種選擇是在oracle中創建類似的函數,它將根據操作結果返回數值。 這樣的事情

CREATE OR REPLACE FUNCTION MY_REGEXP_LIKE(text VARCHAR2, pattern VARCHAR2)
RETURN NUMBER 
IS function_result NUMBER;
BEGIN
  function_result := CASE WHEN REGEXP_LIKE(text, pattern) 
  THEN 1 
  ELSE 0
  END;    
  RETURN(function_result);
END MY_REGEXP_LIKE;

你將能夠使用

MY_REGEXP_LIKE('bananas', 'a') = 1

您可以使用規范。

 Specification<YourEntity> specification = (root, query, builder) -> builder.equal(builder.selectCase()
            .when(builder.function("regexp_like", Boolean.class, root.get("your_field"), builder.literal("^0*514619915$")), 1)
            .otherwise(0), 1);
    List<YourEntity> yourEntities = yourEntityRepository.findAll(specification);

我發現CriteriaBuilder 中的解決方案 Accessing REGEXP_LIKE function 對此很有用。 根據您的 Oracle 版本添加方言。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM