[英]how to use Oracle's regexp_like in Hibernate HQL?
我正在使用oracle 10g
和hibernate 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.