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

时间:2012-08-03 09:40:21

标签: java sql oracle 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功能的情况下运行它时,它按预期运行。但是我无法用regex_like表达式执行它。

它说..

  

嵌套异常是org.hibernate.hql.ast.QuerySyntaxException:   意外的AST节点:(靠近第1行,第66列......

请帮助,如何在hibernate本机查询中使用regex_like?或其他一些替代方案。

6 个答案:

答案 0 :(得分:10)

实际上,除了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

它会起作用:))

答案 1 :(得分:3)

你绝对可以使用任何类型的数据库特定功能,你希望使用Hibernate HQL(和JPQL,只要Hibernate是提供者)。你只需要告诉Hibernate这些功能。在3.3中,唯一的选择是提供自定义Dialect并从Dialect的构造函数中注册该函数。如果你看一下Dialect基类,你会看到许多注册函数的例子。通常最好扩展您当前使用的精确方言,并简单地提供您的扩展(此处,注册该功能)。

一个有趣的说明是Oracle没有将regexp_like归类为函数。他们将其归类为条件/谓词。我认为这主要是因为Oracle SQL没有定义BOOLEAN数据类型,即使他们的PL / SQL确实如此,我敢打赌regexp_like被定义为PL / SQL函数返回BOOLEAN ......

假设您当前使用的是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 ) +
                             ")";
                 }
             }
        );
    }
}

答案 2 :(得分:1)

您可以尝试使用标准LIKE运算符:

where company.num like '%514619915'

然后使用Java正则表达式过滤掉不需要的那些。这应该减少将返回的不需要的行数。

这不会使用索引,因为它以'%'开头。

答案 3 :(得分:0)

除非JPAQL / HQL提供了这样做的方法,否则您无法访问特定的数据库函数,也无法为正则表达式提供任何内容。因此,您需要编写本机SQL查询以使用正则表达式。

另一方面,非常重要的一点,一些同事(Oracle DBA)告诉我永远不要在oracle中使用正则表达式,因为它们无法编入索引,最终导致数据库执行完整的数据库扫描。如果表中有几个条目,那就没关系,但如果它有很多行,可能会削弱性能。

答案 4 :(得分:0)

对于那些使用带有sqlRestriction(Hibernate Version 4.2.7)的Hibernate标准的人

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

答案 5 :(得分:0)

或者另一种选择是在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