QueryDSL不使用Postgres索引

时间:2015-09-15 10:02:12

标签: java spring hibernate postgresql querydsl

我在Spring应用程序上使用Hibernate和QueryDSL以及PostgreSQL,并且在我的过滤列表中遇到了一些性能问题。使用StringPath类,我调用了startsWithIgnoreCase,endsWithIgnoreCase或containsIgnoreCase。 生成的查询似乎具有以下where子句:

WHERE lower(person.firstname) LIKE ? ESCAPE '!'

使用较低版本,查询没有利用Postgres索引。在开发人员数据库中,使用ILIKE关键字查询最多需要1秒而不是10毫秒。

有没有办法使用Postgres获得谓词' ILIKE,正如Ops似乎没有提供它?

由于

2 个答案:

答案 0 :(得分:2)

不得不更新:

通过在我们的自定义Hibernate方言中使用ilike注册SQL函数,我们找到了一种创建所需Postgres运算符的方法。

ilike的例子:

//Postgres Constants Operators
public class PostgresOperators {
    private static final String NS = PostgresOperators.class.getName();
    public static final Operator<Boolean> ILIKE = new OperatorImpl<>(NS, "ILIKE");
}

//Custom JPQLTemplates
public class PostgresTemplates extends HQLTemplates {

    public static final PostgresTemplates DEFAULT = new PostgresTemplates();

    public PostgresTemplates() {
        super();
        add(PostgresOperators.ILIKE, "my_ilike({0},{1})");
    }
}

使用jpaquery时指定JPQLTemplates

new JPAQuery(entityManager, PostgresTemplates.DEFAULT);

现在它变得棘手,我们无法直接使用ilike,已经注册了“ilike”关键字的问题,所以我们制作了一个ilike函数并将其注册到一个自定义的Spring hibernate Dialect。

我们的application.yml指定:

#SEE JPA http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html    
spring.data.jpa:com.example.customDialect.config.database.ExtendedPostgresDialect

然后

public class ExtendedPostgresDialect extends org.hibernate.dialect.PostgreSQL82Dialect {
    public ExtendedPostgresDialect() {
        super();
        registerFunction("my_ilike", new PostgreSQLIlikeFunction());
    }
}

我们尝试使用registerKeyword(“ilike”),没有用,我们继续使用我们的函数和以下实现。

public class PostgreSQLIlikeFunction implements SQLFunction {

    @Override
    public Type getReturnType(Type columnType, Mapping mapping)
        throws QueryException {
        return new BooleanType();
    }

    @SuppressWarnings("unchecked")
    @Override
    public String render(Type firstArgumentType, List args, SessionFactoryImplementor factory) throws QueryException {
        if (args.size() != 2) {
            throw new IllegalArgumentException(
                "The function must be passed 2 arguments");
        }

        String str1 = (String) args.get(0);
        String str2 = (String) args.get(1);

        return str1 + " ilike " + str2;
    }

    @Override
    public boolean hasArguments() {
        return true;
    }

    @Override
    public boolean hasParenthesesIfNoArguments() {
        return false;
    }

}

这就是它,现在我们可以通过以下方式使用ILIKE:

 BooleanOperation.create(PostgresOperators.ILIKE, expression1, expression2).isTrue()

答案 1 :(得分:1)

我遇到了完全相同的问题 - lower(column)导致错误的pg统计信息计算并且计划请求效率不高,ilike解决了问题。我还没有理解OP的答案中哪些部分与解决方案有关,所以重新采用相同的方法,但稍微缩短一点。

  1. 介绍具有my_ilike功能的新方言及其实施:

    public class ExtendedPostgresDialect extends org.hibernate.dialect.PostgreSQL9Dialect {
        public ExtendedPostgresDialect() {
            super();
            registerFunction("my_ilike", new SQLFunctionTemplate(BooleanType.INSTANCE, "(?1 ilike ?2)"));
        }
    }
    
  2. 指定Hibernate使用的这个方言(我使用Java配置):

    Properties props = new Properties();
    props.setProperty("hibernate.dialect", "com.example.ExtendedPostgresDialect");
    factory.setJpaProperties(props);
    
  3. 那就是它,现在你可以使用它了:

    BooleanTemplate.create("function('my_ilike', {0}, {%1%})", stringPath, value).isTrue();