QueryDSL 不使用 Postgres 索引

QueryDSL not using Postgres Indexes

我在 Spring 应用程序上使用 Hibernate 和 QueryDSL 以及 PostgreSQL,我的筛选列表面临一些性能问题。使用 StringPath class,我正在调用 startsWithIgnoreCase、endsWithIgnoreCase 或 containsIgnoreCase。 生成的查询似乎具有以下 where 子句:

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

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

有没有办法使用 Postgres 的 ILIKE 获得谓词,因为 Ops 似乎没有提供它?

谢谢

不得不更新这个:

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

喜欢的例子:

//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休眠方言。

我们的 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()

我遇到了完全相同的问题 - 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();