JPA2: Case-insensitive like matching anywhere

后端 未结 7 2038
梦毁少年i
梦毁少年i 2020-12-13 03:45

I have been using Hibernate Restrictions in JPA 1.0 ( Hibernate driver ). There is defined Restrictions.ilike(\"column\",\"keyword\", MatchMode.ANYWHERE) which

7条回答
  •  悲哀的现实
    2020-12-13 04:04

    If you are using a database like Postgres which supports ilike which provides a much better performance as using the lower() function none of the provided solution solves the issue properly.

    A solution can be a custom function.

    The HQL query you are writing is:

    SELECT * FROM User WHERE (function('caseInSensitiveMatching', name, '%test%')) = true
    

    Where the caseInSensitiveMatching is the function name of our custom function. The name is the path to the property which you want to compare with and the %test% is the pattern which you want to match it against.

    The goal is to convert the HQL query into the following SQL query:

    SELECT * FROM User WHERE (name ilike '%test%') = true
    

    To achieve this we have to implement our own dialect with our custom function registered:

        public class CustomPostgreSQL9Dialect extends PostgreSQL9Dialect {
            /**
             * Default constructor.
             */
            public CustomPostgreSQL9Dialect() {
                super();
                registerFunction("caseInSensitiveMatching", new CaseInSensitiveMatchingSqlFunction());
            }
    
            private class CaseInSensitiveMatchingSqlFunction implements SQLFunction {
    
                @Override
                public boolean hasArguments() {
                    return true;
                }
    
                @Override
                public boolean hasParenthesesIfNoArguments() {
                    return true;
                }
    
                @Override
                public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException {
                    return StandardBasicTypes.BOOLEAN;
                }
    
                @Override
                public String render(Type firstArgumentType, @SuppressWarnings("rawtypes") List arguments,
                        SessionFactoryImplementor factory) throws QueryException {
    
                    if (arguments.size() != 2) {
                        throw new IllegalStateException(
                                "The 'caseInSensitiveMatching' function requires exactly two arguments.");
                    }
    
                    StringBuilder buffer = new StringBuilder();
    
                    buffer.append("(").append(arguments.get(0)).append(" ilike ").append(arguments.get(1)).append(")");
    
                    return buffer.toString();
                }
    
            }
    
        }
    

    The above optimization produced in our situation a performance improvement of a factor of 40 compared to the version with the lower function as Postgres could leverage the index on the corresponding column. In our situation the query execution time could be reduced from 4.5 seconds to 100 ms.

    The lower prevents an efficient usage of the index and as such it is much slower.

提交回复
热议问题