Use Regular Expressions in JPA CriteriaBuilder
Asked Answered
K

4

12

I'm using the JPA CriteriaBuilder to select entities of type MyEntity from a MySQL db as follows:

String regExp = "(abc|def)"
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery query = cb.createQuery( MyEntity.class );
root = query.from( MyEntity.class );
predicates = new ArrayList<Predicate>();

predicates.add( cb.like( root.<String>get( "name" ), regExp ) );

Thus, the query result should contain any entity where the name value matches the given regExp. But the result list is always empty. Changing the regExp to /(abc|def)/g has no effect, neither does adding the wildcard %

How to make the pattern matching work?

Alternatively: How can I use native MySQL REGEXP together with the CriteriaBuilder?

Kosiur answered 28/7, 2014 at 13:1 Comment(0)
D
9

Pattern matching in JPA queries is limited only to

  • _ - any character
  • % - any string

REGEXP has operator syntax in MySQL (SELECT 'a' REGEXP 'A') so it cannot be used with CriteriaBuilder.function() API. I'm afraid the best is to run native SQL query.

If you are using Hibernate you have one more option. You can wrap REGEXP operator in SQLFunctionTemplate, extend hibernate dialect and run with CriteriaBuilder.function().

Dogie answered 28/7, 2014 at 13:29 Comment(0)
A
5

I came across this recently and used the first post to implement the hibernate mysql function option.

To help save some time for others this is what I did:

set up the function in your custom dialect file in hibernate:

public class MySQLDialect extends Dialect {

   public MySQLDialect() {
      super();
      ...
      registerFunction("regexp", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "?1 REGEXP ?2"));
      ...
   }
   ...
}

then within the criteria builder section:

CriteriaBuilder builder = ...;    

Pattern regexPattern = Pattern.compile("^[0-9]\\|[0-9]+");

Expression<String> patternExpression = builder.<String>literal(regexPattern.pattern());

Path<String> path = ... ;// regex comparison column

// regexp comes from the name of the regex function 
// defined in the Mysql Dialect file above
Predicate theRegexPredicate = builder.equal(builder.function("regexp", Integer.class, path, patternExpression), 1);

Then use theRegexPredicate to construct the where clause in your CriteriaBuilder query.

Argentina answered 3/7, 2017 at 14:22 Comment(1)
I tried just what you said but it doesn't return anything. Can you have a look at ticket #66601647, maybe I missed something?Polash
F
2

Maybe this snippet will help. We had to exclude characters in a search, and we using Oracle. CriteriaBuilder (at least as of 2.1) will let you call a function.

private static final Pattern UNDESIRABLES = Pattern.compile("[(){},.;!?<>%_-]");
private static final String UNDESIRABLE_REPLACEMENT = "";
...

In the search method, create a Predicate to use in your where clause:

Expression<String> undesirables = cb.literal(UNDESIRABLES.toString());
Expression<String> replaceWith = cb.literal(UNDESIRABLE_REPLACEMENT);
Expression<String> regExp = cb.function("REGEXP_REPLACE", String.class, client.get(Client_.companyName),
    undesirables, replaceWith);
Predicate companyNameMatch = cb.equal(cb.trim(cb.lower(regExp)), removeUndesireables(name).trim());
...

And create a method for the right hand comapare that uses the same values as the left:

private String removeUndesireables(String name) {
    return UNDESIRABLES.matcher(name).replaceAll(UNDESIRABLE_REPLACEMENT).toLowerCase();
}
Flaky answered 23/6, 2015 at 14:40 Comment(0)
S
0

If you are using Postgres you can combine array_length and regexp_match functions, by invoking criteriaBuilder.function() to generate sql like this:

... where array_length(regexp_match(my_field,'<my_regexp_pattern>'),1)>0

the > 0 condition checks if there is a match for the my_regexp_pattern. If you want to verify that my_regexp_pattern does not match use:

regexp_match(my_field,'<my_regexp_pattern>') is null

Soughtafter answered 25/8, 2024 at 10:33 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.