关于java:QueryDSL不使用Postgres索引

QueryDSL not using Postgres Indexes

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

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

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

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

谢谢


不得不更新这个:

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

ilike示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//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

1
new JPAQuery(entityManager, PostgresTemplates.DEFAULT);

现在它变得棘手,我们无法直接使用ilike,已经注册了"ilike"关键字的问题,所以我们制作了一个ilike函数并将其注册到一个定制的春天休眠方言。

我们的application.yml指定:

1
2
#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

然后

1
2
3
4
5
6
public class ExtendedPostgresDialect extends org.hibernate.dialect.PostgreSQL82Dialect {
    public ExtendedPostgresDialect() {
        super();
        registerFunction("my_ilike", new PostgreSQLIlikeFunction());
    }
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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:

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

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

  • 引入具有my_ilike功能的新方言及其实现:

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

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

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