10

我正在使用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 列.....

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

4

7 回答 7

13

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

它会工作:)

于 2012-10-19T14:57:54.583 回答
4

您绝对可以通过 Hibernate HQL(和 JPQL,只要 Hibernate 是提供者)使用您希望使用的任何类型的数据库特定功能。你只需要告诉 Hibernate 这些函数。在 3.3 中,唯一的选择是提供自定义方言并从方言的构造函数中注册函数。如果你看一下基本的 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 ) +
                             ")";
                 }
             }
        );
    }
}
于 2012-08-06T22:34:01.393 回答
0

您不能访问特定的数据库函数,除非 JPAQL/HQL 提供了这样做的方法,并且也不为正则表达式提供任何东西。因此,您需要编写一个原生 SQL 查询来使用正则表达式。

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

于 2012-08-03T09:55:21.827 回答
0

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

where company.num like '%514619915'

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

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

于 2012-08-06T13:27:27.517 回答
0

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

 Criterion someCriterion = Restrictions.sqlRestriction("regexp_like (column_name, ?, 'i')", "(^|\\s)"+searchValue+"($|\\s|.$)", StringType.INSTANCE);
于 2015-03-11T19:51:28.340 回答
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
于 2015-09-25T12:12:47.850 回答
0

您可以使用规范。

 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);
于 2021-09-30T09:25:02.807 回答