HHH-14739 - Implement ILike support
Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
parent
be485796b1
commit
0c1c469352
|
@ -212,6 +212,7 @@ LEAST : [lL] [eE] [aA] [sS] [tT];
|
||||||
LEFT : [lL] [eE] [fF] [tT];
|
LEFT : [lL] [eE] [fF] [tT];
|
||||||
LENGTH : [lL] [eE] [nN] [gG] [tT] [hH];
|
LENGTH : [lL] [eE] [nN] [gG] [tT] [hH];
|
||||||
LIKE : [lL] [iI] [kK] [eE];
|
LIKE : [lL] [iI] [kK] [eE];
|
||||||
|
ILIKE : [iI] [lL] [iI] [kK] [eE];
|
||||||
LIMIT : [lL] [iI] [mM] [iI] [tT];
|
LIMIT : [lL] [iI] [mM] [iI] [tT];
|
||||||
LIST : [lL] [iI] [sS] [tT];
|
LIST : [lL] [iI] [sS] [tT];
|
||||||
LN : [lL] [nN];
|
LN : [lL] [nN];
|
||||||
|
|
|
@ -405,10 +405,10 @@ predicate
|
||||||
| expression IS (NOT)? EMPTY # IsEmptyPredicate
|
| expression IS (NOT)? EMPTY # IsEmptyPredicate
|
||||||
| expression (NOT)? IN inList # InPredicate
|
| expression (NOT)? IN inList # InPredicate
|
||||||
| expression (NOT)? BETWEEN expression AND expression # BetweenPredicate
|
| expression (NOT)? BETWEEN expression AND expression # BetweenPredicate
|
||||||
| expression (NOT)? LIKE expression (likeEscape)? # LikePredicate
|
| expression (NOT)? (LIKE | ILIKE) expression (likeEscape)? # LikePredicate
|
||||||
| expression comparisonOperator expression # ComparisonPredicate
|
| expression comparisonOperator expression # ComparisonPredicate
|
||||||
| EXISTS (ELEMENTS|INDICES) LEFT_PAREN dotIdentifierSequence RIGHT_PAREN # ExistsCollectionPartPredicate
|
| EXISTS (ELEMENTS|INDICES) LEFT_PAREN dotIdentifierSequence RIGHT_PAREN # ExistsCollectionPartPredicate
|
||||||
| EXISTS expression # ExistsPredicate
|
| EXISTSexpression # ExistsPredicate
|
||||||
| expression (NOT)? MEMBER OF path # MemberOfPredicate
|
| expression (NOT)? MEMBER OF path # MemberOfPredicate
|
||||||
| NOT predicate # NegatedPredicate
|
| NOT predicate # NegatedPredicate
|
||||||
| predicate AND predicate # AndPredicate
|
| predicate AND predicate # AndPredicate
|
||||||
|
@ -973,6 +973,7 @@ identifier
|
||||||
| HOUR
|
| HOUR
|
||||||
| ID
|
| ID
|
||||||
| IFNULL
|
| IFNULL
|
||||||
|
| ILIKE
|
||||||
| IN
|
| IN
|
||||||
| INDEX
|
| INDEX
|
||||||
| INDICES
|
| INDICES
|
||||||
|
|
|
@ -33,7 +33,6 @@ import javax.persistence.criteria.Subquery;
|
||||||
import org.hibernate.query.NullPrecedence;
|
import org.hibernate.query.NullPrecedence;
|
||||||
import org.hibernate.query.SortOrder;
|
import org.hibernate.query.SortOrder;
|
||||||
import org.hibernate.metamodel.model.domain.DomainType;
|
import org.hibernate.metamodel.model.domain.DomainType;
|
||||||
import org.hibernate.query.sqm.SqmExpressable;
|
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -621,6 +620,18 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
|
||||||
@Override
|
@Override
|
||||||
JpaPredicate like(Expression<String> x, String pattern, char escapeChar);
|
JpaPredicate like(Expression<String> x, String pattern, char escapeChar);
|
||||||
|
|
||||||
|
JpaPredicate ilike(Expression<String> x, Expression<String> pattern);
|
||||||
|
|
||||||
|
JpaPredicate ilike(Expression<String> x, String pattern);
|
||||||
|
|
||||||
|
JpaPredicate ilike(Expression<String> x, Expression<String> pattern, Expression<Character> escapeChar);
|
||||||
|
|
||||||
|
JpaPredicate ilike(Expression<String> x, Expression<String> pattern, char escapeChar);
|
||||||
|
|
||||||
|
JpaPredicate ilike(Expression<String> x, String pattern, Expression<Character> escapeChar);
|
||||||
|
|
||||||
|
JpaPredicate ilike(Expression<String> x, String pattern, char escapeChar);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
JpaPredicate notLike(Expression<String> x, Expression<String> pattern);
|
JpaPredicate notLike(Expression<String> x, Expression<String> pattern);
|
||||||
|
|
||||||
|
@ -639,6 +650,18 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
|
||||||
@Override
|
@Override
|
||||||
JpaPredicate notLike(Expression<String> x, String pattern, char escapeChar);
|
JpaPredicate notLike(Expression<String> x, String pattern, char escapeChar);
|
||||||
|
|
||||||
|
JpaPredicate notIlike(Expression<String> x, Expression<String> pattern);
|
||||||
|
|
||||||
|
JpaPredicate notIlike(Expression<String> x, String pattern);
|
||||||
|
|
||||||
|
JpaPredicate notIlike(Expression<String> x, Expression<String> pattern, Expression<Character> escapeChar);
|
||||||
|
|
||||||
|
JpaPredicate notIlike(Expression<String> x, Expression<String> pattern, char escapeChar);
|
||||||
|
|
||||||
|
JpaPredicate notIlike(Expression<String> x, String pattern, Expression<Character> escapeChar);
|
||||||
|
|
||||||
|
JpaPredicate notIlike(Expression<String> x, String pattern, char escapeChar);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
<T> JpaInPredicate<T> in(Expression<? extends T> expression);
|
<T> JpaInPredicate<T> in(Expression<? extends T> expression);
|
||||||
|
|
||||||
|
|
|
@ -1972,12 +1972,15 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
public SqmPredicate visitLikePredicate(HqlParser.LikePredicateContext ctx) {
|
public SqmPredicate visitLikePredicate(HqlParser.LikePredicateContext ctx) {
|
||||||
final boolean negated = ( (TerminalNode) ctx.getChild( 1 ) ).getSymbol().getType() == HqlParser.NOT;
|
final boolean negated = ( (TerminalNode) ctx.getChild( 1 ) ).getSymbol().getType() == HqlParser.NOT;
|
||||||
final int startIndex = negated ? 3 : 2;
|
final int startIndex = negated ? 3 : 2;
|
||||||
|
final boolean caseSensitive = ( (TerminalNode) ctx.getChild( negated ? 2 : 1 ) ).getSymbol()
|
||||||
|
.getType() == HqlParser.LIKE;
|
||||||
if ( ctx.getChildCount() == startIndex + 2 ) {
|
if ( ctx.getChildCount() == startIndex + 2 ) {
|
||||||
return new SqmLikePredicate(
|
return new SqmLikePredicate(
|
||||||
(SqmExpression<?>) ctx.getChild( 0 ).accept( this ),
|
(SqmExpression<?>) ctx.getChild( 0 ).accept( this ),
|
||||||
(SqmExpression<?>) ctx.getChild( startIndex ).accept( this ),
|
(SqmExpression<?>) ctx.getChild( startIndex ).accept( this ),
|
||||||
(SqmExpression<?>) ctx.getChild( startIndex + 1 ).getChild( 1 ).accept( this ),
|
(SqmExpression<?>) ctx.getChild( startIndex + 1 ).getChild( 1 ).accept( this ),
|
||||||
negated,
|
negated,
|
||||||
|
caseSensitive,
|
||||||
creationContext.getNodeBuilder()
|
creationContext.getNodeBuilder()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1986,6 +1989,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
(SqmExpression<?>) ctx.getChild( 0 ).accept( this ),
|
(SqmExpression<?>) ctx.getChild( 0 ).accept( this ),
|
||||||
(SqmExpression<?>) ctx.getChild( startIndex ).accept( this ),
|
(SqmExpression<?>) ctx.getChild( startIndex ).accept( this ),
|
||||||
negated,
|
negated,
|
||||||
|
caseSensitive,
|
||||||
creationContext.getNodeBuilder()
|
creationContext.getNodeBuilder()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1970,6 +1970,79 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmPredicate ilike(Expression<String> searchString, Expression<String> pattern) {
|
||||||
|
return new SqmLikePredicate(
|
||||||
|
(SqmExpression) searchString,
|
||||||
|
(SqmExpression) pattern,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmPredicate ilike(Expression<String> searchString, String pattern) {
|
||||||
|
return new SqmLikePredicate(
|
||||||
|
(SqmExpression) searchString,
|
||||||
|
value( pattern, (SqmExpression) searchString ),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmPredicate ilike(
|
||||||
|
Expression<String> searchString,
|
||||||
|
Expression<String> pattern,
|
||||||
|
Expression<Character> escapeChar) {
|
||||||
|
return new SqmLikePredicate(
|
||||||
|
(SqmExpression) searchString,
|
||||||
|
(SqmExpression) pattern,
|
||||||
|
(SqmExpression) escapeChar,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmPredicate ilike(Expression<String> searchString, Expression<String> pattern, char escapeChar) {
|
||||||
|
return new SqmLikePredicate(
|
||||||
|
(SqmExpression) searchString,
|
||||||
|
(SqmExpression) pattern,
|
||||||
|
literal( escapeChar ),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmPredicate ilike(Expression<String> searchString, String pattern, Expression<Character> escapeChar) {
|
||||||
|
return new SqmLikePredicate(
|
||||||
|
(SqmExpression) searchString,
|
||||||
|
value( pattern, (SqmExpression) searchString ),
|
||||||
|
(SqmExpression) escapeChar,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmPredicate ilike(Expression<String> searchString, String pattern, char escapeChar) {
|
||||||
|
return new SqmLikePredicate(
|
||||||
|
(SqmExpression) searchString,
|
||||||
|
value( pattern, (SqmExpression) searchString ),
|
||||||
|
literal( escapeChar ),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmPredicate notLike(Expression<String> x, Expression<String> pattern) {
|
public SqmPredicate notLike(Expression<String> x, Expression<String> pattern) {
|
||||||
return not( like( x, pattern ) );
|
return not( like( x, pattern ) );
|
||||||
|
@ -2000,6 +2073,36 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext {
|
||||||
return not( like( x, pattern, escapeChar ) );
|
return not( like( x, pattern, escapeChar ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmPredicate notIlike(Expression<String> x, Expression<String> pattern) {
|
||||||
|
return not( ilike( x, pattern ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmPredicate notIlike(Expression<String> x, String pattern) {
|
||||||
|
return not( ilike( x, pattern ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmPredicate notIlike(Expression<String> x, Expression<String> pattern, Expression<Character> escapeChar) {
|
||||||
|
return not( ilike( x, pattern, escapeChar ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmPredicate notIlike(Expression<String> x, Expression<String> pattern, char escapeChar) {
|
||||||
|
return not( ilike( x, pattern, escapeChar ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmPredicate notIlike(Expression<String> x, String pattern, Expression<Character> escapeChar) {
|
||||||
|
return not( ilike( x, pattern, escapeChar ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmPredicate notIlike(Expression<String> x, String pattern, char escapeChar) {
|
||||||
|
return not( ilike( x, pattern, escapeChar ) );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T> SqmInPredicate<T> in(Expression<? extends T> expression) {
|
public <T> SqmInPredicate<T> in(Expression<? extends T> expression) {
|
||||||
|
|
|
@ -838,8 +838,9 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitLikePredicate(SqmLikePredicate predicate) {
|
public Object visitLikePredicate(SqmLikePredicate predicate) {
|
||||||
|
final String likeType = predicate.isCaseSensitive() ? "like" : "ilike";
|
||||||
processStanza(
|
processStanza(
|
||||||
predicate.isNegated() ? "is-not-like" : "is-like",
|
( predicate.isNegated() ? "is-not-" : "is-" ) + likeType,
|
||||||
() -> {
|
() -> {
|
||||||
predicate.getPattern().accept( this );
|
predicate.getPattern().accept( this );
|
||||||
predicate.getMatchExpression().accept( this );
|
predicate.getMatchExpression().accept( this );
|
||||||
|
|
|
@ -4468,7 +4468,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
(Expression) predicate.getMatchExpression().accept( this ),
|
(Expression) predicate.getMatchExpression().accept( this ),
|
||||||
(Expression) predicate.getPattern().accept( this ),
|
(Expression) predicate.getPattern().accept( this ),
|
||||||
escapeExpression,
|
escapeExpression,
|
||||||
predicate.isNegated()
|
predicate.isNegated(),
|
||||||
|
predicate.isCaseSensitive()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ public class SqmLikePredicate extends AbstractNegatableSqmPredicate {
|
||||||
private final SqmExpression<?> matchExpression;
|
private final SqmExpression<?> matchExpression;
|
||||||
private final SqmExpression<?> pattern;
|
private final SqmExpression<?> pattern;
|
||||||
private final SqmExpression<?> escapeCharacter;
|
private final SqmExpression<?> escapeCharacter;
|
||||||
|
private final boolean isCaseSensitive;
|
||||||
|
|
||||||
public SqmLikePredicate(
|
public SqmLikePredicate(
|
||||||
SqmExpression<?> matchExpression,
|
SqmExpression<?> matchExpression,
|
||||||
|
@ -26,17 +27,27 @@ public class SqmLikePredicate extends AbstractNegatableSqmPredicate {
|
||||||
this( matchExpression, pattern, escapeCharacter, false, nodeBuilder );
|
this( matchExpression, pattern, escapeCharacter, false, nodeBuilder );
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
|
||||||
public SqmLikePredicate(
|
public SqmLikePredicate(
|
||||||
SqmExpression<?> matchExpression,
|
SqmExpression<?> matchExpression,
|
||||||
SqmExpression<?> pattern,
|
SqmExpression<?> pattern,
|
||||||
SqmExpression<?> escapeCharacter,
|
SqmExpression<?> escapeCharacter,
|
||||||
boolean negated,
|
boolean negated,
|
||||||
NodeBuilder nodeBuilder) {
|
NodeBuilder nodeBuilder) {
|
||||||
|
this( matchExpression, pattern, escapeCharacter, negated, true, nodeBuilder );
|
||||||
|
}
|
||||||
|
|
||||||
|
public SqmLikePredicate(
|
||||||
|
SqmExpression<?> matchExpression,
|
||||||
|
SqmExpression<?> pattern,
|
||||||
|
SqmExpression<?> escapeCharacter,
|
||||||
|
boolean negated,
|
||||||
|
boolean isCaseSensitive,
|
||||||
|
NodeBuilder nodeBuilder) {
|
||||||
super( negated, nodeBuilder );
|
super( negated, nodeBuilder );
|
||||||
this.matchExpression = matchExpression;
|
this.matchExpression = matchExpression;
|
||||||
this.pattern = pattern;
|
this.pattern = pattern;
|
||||||
this.escapeCharacter = escapeCharacter;
|
this.escapeCharacter = escapeCharacter;
|
||||||
|
this.isCaseSensitive = isCaseSensitive;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SqmLikePredicate(
|
public SqmLikePredicate(
|
||||||
|
@ -50,8 +61,9 @@ public class SqmLikePredicate extends AbstractNegatableSqmPredicate {
|
||||||
SqmExpression<?> matchExpression,
|
SqmExpression<?> matchExpression,
|
||||||
SqmExpression<?> pattern,
|
SqmExpression<?> pattern,
|
||||||
boolean negated,
|
boolean negated,
|
||||||
|
boolean isCaseSensitive,
|
||||||
NodeBuilder nodeBuilder) {
|
NodeBuilder nodeBuilder) {
|
||||||
this( matchExpression, pattern, null, negated, nodeBuilder );
|
this( matchExpression, pattern, null, negated, isCaseSensitive, nodeBuilder );
|
||||||
}
|
}
|
||||||
|
|
||||||
public SqmExpression<?> getMatchExpression() {
|
public SqmExpression<?> getMatchExpression() {
|
||||||
|
@ -66,6 +78,10 @@ public class SqmLikePredicate extends AbstractNegatableSqmPredicate {
|
||||||
return escapeCharacter;
|
return escapeCharacter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isCaseSensitive() {
|
||||||
|
return isCaseSensitive;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T accept(SemanticQueryWalker<T> walker) {
|
public <T> T accept(SemanticQueryWalker<T> walker) {
|
||||||
return walker.visitLikePredicate( this );
|
return walker.visitLikePredicate( this );
|
||||||
|
|
|
@ -4562,6 +4562,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitLikePredicate(LikePredicate likePredicate) {
|
public void visitLikePredicate(LikePredicate likePredicate) {
|
||||||
|
if ( likePredicate.isCaseSensitive() ) {
|
||||||
|
|
||||||
likePredicate.getMatchExpression().accept( this );
|
likePredicate.getMatchExpression().accept( this );
|
||||||
if ( likePredicate.isNegated() ) {
|
if ( likePredicate.isNegated() ) {
|
||||||
appendSql( " not" );
|
appendSql( " not" );
|
||||||
|
@ -4572,6 +4574,49 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
||||||
appendSql( " escape " );
|
appendSql( " escape " );
|
||||||
likePredicate.getEscapeCharacter().accept( this );
|
likePredicate.getEscapeCharacter().accept( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (dialect.supportsCaseInsensitiveLike()) {
|
||||||
|
|
||||||
|
likePredicate.getMatchExpression().accept( this );
|
||||||
|
if ( likePredicate.isNegated() ) {
|
||||||
|
appendSql( " not" );
|
||||||
|
}
|
||||||
|
appendSql( " " );
|
||||||
|
appendSql( dialect.getCaseInsensitiveLike() );
|
||||||
|
appendSql( " " );
|
||||||
|
likePredicate.getPattern().accept( this );
|
||||||
|
if ( likePredicate.getEscapeCharacter() != null ) {
|
||||||
|
appendSql( " escape " );
|
||||||
|
likePredicate.getEscapeCharacter().accept( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
renderCaseInsensitiveLikeEmulation(likePredicate.getMatchExpression(), likePredicate.getPattern(), likePredicate.getEscapeCharacter(), likePredicate.isNegated());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderCaseInsensitiveLikeEmulation(Expression lhs, Expression rhs, Expression escapeCharacter, boolean negated) {
|
||||||
|
//LOWER(lhs) operator LOWER(rhs)
|
||||||
|
appendSql( " " );
|
||||||
|
appendSql( dialect.getLowercaseFunction() );
|
||||||
|
appendSql( "( ");
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " )" );
|
||||||
|
appendSql( negated ? " not" : "" );
|
||||||
|
appendSql( " like " );
|
||||||
|
appendSql( dialect.getLowercaseFunction() );
|
||||||
|
appendSql( "( " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " ) " );
|
||||||
|
if ( escapeCharacter != null ) {
|
||||||
|
appendSql( " escape " );
|
||||||
|
escapeCharacter.accept( this );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -17,6 +17,7 @@ public class LikePredicate implements Predicate {
|
||||||
private final Expression pattern;
|
private final Expression pattern;
|
||||||
private final Expression escapeCharacter;
|
private final Expression escapeCharacter;
|
||||||
private final boolean negated;
|
private final boolean negated;
|
||||||
|
private final boolean isCaseSensitive;
|
||||||
|
|
||||||
public LikePredicate(
|
public LikePredicate(
|
||||||
Expression matchExpression,
|
Expression matchExpression,
|
||||||
|
@ -28,11 +29,22 @@ public class LikePredicate implements Predicate {
|
||||||
public LikePredicate(
|
public LikePredicate(
|
||||||
Expression matchExpression,
|
Expression matchExpression,
|
||||||
Expression pattern,
|
Expression pattern,
|
||||||
Expression escapeCharacter, boolean negated) {
|
Expression escapeCharacter,
|
||||||
|
boolean negated) {
|
||||||
|
this( matchExpression, pattern, escapeCharacter, negated, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
public LikePredicate(
|
||||||
|
Expression matchExpression,
|
||||||
|
Expression pattern,
|
||||||
|
Expression escapeCharacter,
|
||||||
|
boolean negated,
|
||||||
|
boolean isCaseSensitive) {
|
||||||
this.matchExpression = matchExpression;
|
this.matchExpression = matchExpression;
|
||||||
this.pattern = pattern;
|
this.pattern = pattern;
|
||||||
this.escapeCharacter = escapeCharacter;
|
this.escapeCharacter = escapeCharacter;
|
||||||
this.negated = negated;
|
this.negated = negated;
|
||||||
|
this.isCaseSensitive = isCaseSensitive;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LikePredicate(Expression matchExpression, Expression pattern) {
|
public LikePredicate(Expression matchExpression, Expression pattern) {
|
||||||
|
@ -55,6 +67,10 @@ public class LikePredicate implements Predicate {
|
||||||
return negated;
|
return negated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isCaseSensitive() {
|
||||||
|
return isCaseSensitive;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -0,0 +1,278 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.test.query.hql;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
|
||||||
|
import org.hibernate.query.Query;
|
||||||
|
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||||
|
import org.hibernate.testing.orm.domain.gambit.BasicEntity;
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jan Schatteman
|
||||||
|
*/
|
||||||
|
@ServiceRegistry
|
||||||
|
@DomainModel(
|
||||||
|
standardModels = StandardDomainModel.GAMBIT
|
||||||
|
)
|
||||||
|
@SessionFactory
|
||||||
|
public class ILikeCriteriaTest {
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void prepareData(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
em -> {
|
||||||
|
BasicEntity be1 = new BasicEntity(1, "Product_one");
|
||||||
|
em.persist( be1 );
|
||||||
|
BasicEntity be2 = new BasicEntity(2, "proDUct two");
|
||||||
|
em.persist( be2 );
|
||||||
|
BasicEntity be3 = new BasicEntity(3, "Product three");
|
||||||
|
em.persist( be3 );
|
||||||
|
BasicEntity be4 = new BasicEntity(4, "pROducT four");
|
||||||
|
em.persist( be4 );
|
||||||
|
BasicEntity be5 = new BasicEntity(5, "Product five");
|
||||||
|
em.persist( be5 );
|
||||||
|
BasicEntity be6 = new BasicEntity(6, "Prodact six");
|
||||||
|
em.persist( be6 );
|
||||||
|
BasicEntity be7 = new BasicEntity(7, "prodACt seven");
|
||||||
|
em.persist( be7 );
|
||||||
|
BasicEntity be8 = new BasicEntity(8, "Prod_act eight");
|
||||||
|
em.persist( be8 );
|
||||||
|
BasicEntity be9 = new BasicEntity(9, "prod_ACt nine");
|
||||||
|
em.persist( be9 );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void tearDown(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> session.createQuery( "delete from BasicEntity" ).executeUpdate()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLike(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
|
||||||
|
CriteriaQuery cq = cb.createQuery( BasicEntity.class);
|
||||||
|
Root<BasicEntity> from = cq.from( BasicEntity.class );
|
||||||
|
|
||||||
|
cq.where( cb.like( from.get( "data" ), "Prod%" ) );
|
||||||
|
Query q = session.createQuery( cq );
|
||||||
|
List l = q.getResultList();
|
||||||
|
assertEquals( 5, l.size() );
|
||||||
|
|
||||||
|
cq.where( cb.like( from.get( "data" ), cb.literal( "Prod%" ) ) );
|
||||||
|
q = session.createQuery( cq );
|
||||||
|
l = q.getResultList();
|
||||||
|
assertEquals( 5, l.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLikeEscape(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
|
||||||
|
CriteriaQuery cq = cb.createQuery( BasicEntity.class);
|
||||||
|
Root<BasicEntity> from = cq.from( BasicEntity.class );
|
||||||
|
|
||||||
|
cq.where( cb.like( from.get( "data" ), cb.literal( "Prod%" ), cb.literal( '$' ) ) );
|
||||||
|
Query q = session.createQuery( cq );
|
||||||
|
List l = q.getResultList();
|
||||||
|
assertEquals( 5, l.size() );
|
||||||
|
|
||||||
|
cq.where( cb.like( from.get( "data" ), cb.literal( "Prod%" ), '$' ) );
|
||||||
|
q = session.createQuery( cq );
|
||||||
|
l = q.getResultList();
|
||||||
|
assertEquals( 5, l.size() );
|
||||||
|
|
||||||
|
cq.where( cb.like( from.get( "data" ), "Prod%", cb.literal( '$' ) ) );
|
||||||
|
q = session.createQuery( cq );
|
||||||
|
l = q.getResultList();
|
||||||
|
assertEquals( 5, l.size() );
|
||||||
|
|
||||||
|
cq.where( cb.like( from.get( "data" ), "Prod%", '$' ) );
|
||||||
|
q = session.createQuery( cq );
|
||||||
|
l = q.getResultList();
|
||||||
|
assertEquals( 5, l.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotLike(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
|
||||||
|
CriteriaQuery cq = cb.createQuery( BasicEntity.class);
|
||||||
|
Root<BasicEntity> from = cq.from( BasicEntity.class );
|
||||||
|
|
||||||
|
cq.where( cb.notLike( from.get( "data" ), "Prod%" ) );
|
||||||
|
Query q = session.createQuery( cq );
|
||||||
|
List l = q.getResultList();
|
||||||
|
assertEquals( 4, l.size() );
|
||||||
|
|
||||||
|
cq.where( cb.notLike( from.get( "data" ), cb.literal( "Prod%" ) ) );
|
||||||
|
q = session.createQuery( cq );
|
||||||
|
l = q.getResultList();
|
||||||
|
assertEquals( 4, l.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotLikeEscape(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
|
||||||
|
CriteriaQuery cq = cb.createQuery( BasicEntity.class);
|
||||||
|
Root<BasicEntity> from = cq.from( BasicEntity.class );
|
||||||
|
|
||||||
|
cq.where( cb.notLike( from.get( "data" ), cb.literal( "Pr%$_%" ), cb.literal( '$' ) ) );
|
||||||
|
Query q = session.createQuery( cq );
|
||||||
|
List l = q.getResultList();
|
||||||
|
assertEquals( 7, l.size() );
|
||||||
|
|
||||||
|
cq.where( cb.notLike( from.get( "data" ), cb.literal( "Pr%$_%" ), '$' ) );
|
||||||
|
q = session.createQuery( cq );
|
||||||
|
l = q.getResultList();
|
||||||
|
assertEquals( 7, l.size() );
|
||||||
|
|
||||||
|
cq.where( cb.notLike( from.get( "data" ), "Pr%$_%", cb.literal( '$' ) ) );
|
||||||
|
q = session.createQuery( cq );
|
||||||
|
l = q.getResultList();
|
||||||
|
assertEquals( 7, l.size() );
|
||||||
|
|
||||||
|
cq.where( cb.notLike( from.get( "data" ), "Pr%$_%", '$' ) );
|
||||||
|
q = session.createQuery( cq );
|
||||||
|
l = q.getResultList();
|
||||||
|
assertEquals( 7, l.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIlike(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
|
||||||
|
CriteriaQuery cq = cb.createQuery( BasicEntity.class);
|
||||||
|
Root<BasicEntity> from = cq.from( BasicEntity.class );
|
||||||
|
|
||||||
|
cq.where( cb.ilike( from.get( "data" ), "Produ%" ) );
|
||||||
|
Query q = session.createQuery( cq );
|
||||||
|
List l = q.getResultList();
|
||||||
|
assertEquals( 5, l.size() );
|
||||||
|
|
||||||
|
cq.where( cb.ilike( from.get( "data" ), cb.literal( "Produ%" ) ) );
|
||||||
|
q = session.createQuery( cq );
|
||||||
|
l = q.getResultList();
|
||||||
|
assertEquals( 5, l.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIlikeEscape(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
|
||||||
|
CriteriaQuery cq = cb.createQuery( BasicEntity.class);
|
||||||
|
Root<BasicEntity> from = cq.from( BasicEntity.class );
|
||||||
|
|
||||||
|
cq.where( cb.ilike( from.get( "data" ), cb.literal( "Pr%$_%" ), cb.literal( '$' ) ) );
|
||||||
|
Query q = session.createQuery( cq );
|
||||||
|
List l = q.getResultList();
|
||||||
|
assertEquals( 3, l.size() );
|
||||||
|
|
||||||
|
cq.where( cb.ilike( from.get( "data" ), cb.literal( "Pr%$_%" ), '$' ) );
|
||||||
|
q = session.createQuery( cq );
|
||||||
|
l = q.getResultList();
|
||||||
|
assertEquals( 3, l.size() );
|
||||||
|
|
||||||
|
cq.where( cb.ilike( from.get( "data" ), "Pr%$_%", cb.literal( '$' ) ) );
|
||||||
|
q = session.createQuery( cq );
|
||||||
|
l = q.getResultList();
|
||||||
|
assertEquals( 3, l.size() );
|
||||||
|
|
||||||
|
cq.where( cb.ilike( from.get( "data" ), "Pr%$_%", '$' ) );
|
||||||
|
q = session.createQuery( cq );
|
||||||
|
l = q.getResultList();
|
||||||
|
assertEquals( 3, l.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotIlike(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
|
||||||
|
CriteriaQuery cq = cb.createQuery( BasicEntity.class);
|
||||||
|
Root<BasicEntity> from = cq.from( BasicEntity.class );
|
||||||
|
|
||||||
|
cq.where( cb.notIlike( from.get( "data" ), "Produ%" ) );
|
||||||
|
Query q = session.createQuery( cq );
|
||||||
|
List l = q.getResultList();
|
||||||
|
assertEquals( 4, l.size() );
|
||||||
|
|
||||||
|
cq.where( cb.notIlike( from.get( "data" ), cb.literal( "Produ%" ) ) );
|
||||||
|
q = session.createQuery( cq );
|
||||||
|
l = q.getResultList();
|
||||||
|
assertEquals( 4, l.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotIlikeEscape(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
|
||||||
|
CriteriaQuery cq = cb.createQuery( BasicEntity.class);
|
||||||
|
Root<BasicEntity> from = cq.from( BasicEntity.class );
|
||||||
|
|
||||||
|
cq.where( cb.notIlike( from.get( "data" ), cb.literal( "Pr%$_%" ), cb.literal( '$' ) ) );
|
||||||
|
Query q = session.createQuery( cq );
|
||||||
|
List l = q.getResultList();
|
||||||
|
assertEquals( 6, l.size() );
|
||||||
|
|
||||||
|
cq.where( cb.notIlike( from.get( "data" ), cb.literal( "Pr%$_%" ), '$' ) );
|
||||||
|
q = session.createQuery( cq );
|
||||||
|
l = q.getResultList();
|
||||||
|
assertEquals( 6, l.size() );
|
||||||
|
|
||||||
|
cq.where( cb.notIlike( from.get( "data" ), "Pr%$_%", cb.literal( '$' ) ) );
|
||||||
|
q = session.createQuery( cq );
|
||||||
|
l = q.getResultList();
|
||||||
|
assertEquals( 6, l.size() );
|
||||||
|
|
||||||
|
cq.where( cb.notIlike( from.get( "data" ), "Pr%$_%", '$' ) );
|
||||||
|
q = session.createQuery( cq );
|
||||||
|
l = q.getResultList();
|
||||||
|
assertEquals( 6, l.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.test.query.hql;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.query.Query;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||||
|
import org.hibernate.testing.orm.domain.gambit.BasicEntity;
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jan Schatteman
|
||||||
|
*/
|
||||||
|
@ServiceRegistry
|
||||||
|
@DomainModel(
|
||||||
|
standardModels = StandardDomainModel.GAMBIT
|
||||||
|
)
|
||||||
|
@SessionFactory
|
||||||
|
public class ILikeTest {
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void prepareData(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
em -> {
|
||||||
|
BasicEntity be1 = new BasicEntity(1, "Product_one");
|
||||||
|
em.persist( be1 );
|
||||||
|
BasicEntity be2 = new BasicEntity(2, "proDUct two");
|
||||||
|
em.persist( be2 );
|
||||||
|
BasicEntity be3 = new BasicEntity(3, "Product three");
|
||||||
|
em.persist( be3 );
|
||||||
|
BasicEntity be4 = new BasicEntity(4, "pROducT four");
|
||||||
|
em.persist( be4 );
|
||||||
|
BasicEntity be5 = new BasicEntity(5, "Product five");
|
||||||
|
em.persist( be5 );
|
||||||
|
BasicEntity be6 = new BasicEntity(6, "Prodact six");
|
||||||
|
em.persist( be6 );
|
||||||
|
BasicEntity be7 = new BasicEntity(7, "prodACt seven");
|
||||||
|
em.persist( be7 );
|
||||||
|
BasicEntity be8 = new BasicEntity(8, "Prod_act eight");
|
||||||
|
em.persist( be8 );
|
||||||
|
BasicEntity be9 = new BasicEntity(9, "prod_ACt nine");
|
||||||
|
em.persist( be9 );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void tearDown(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> session.createQuery( "delete from BasicEntity" ).executeUpdate()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLike(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
Query q = session.createQuery( "from BasicEntity be where be.data like 'Prod%'" );
|
||||||
|
List l = q.getResultList();
|
||||||
|
assertEquals( 5, l.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotLike(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
Query q = session.createQuery( "from BasicEntity be where be.data not like 'Prod%'" );
|
||||||
|
List l = q.getResultList();
|
||||||
|
assertEquals( 4, l.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLikeEscape(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
Query q = session.createQuery( "from BasicEntity be where be.data like 'Pr%$_%' escape '$'" );
|
||||||
|
List l = q.getResultList();
|
||||||
|
assertEquals( 2, l.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotLikeEscape(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
Query q = session.createQuery( "from BasicEntity be where be.data not like 'Pr%$_%' escape '$'" );
|
||||||
|
List l = q.getResultList();
|
||||||
|
assertEquals( 7, l.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIlike(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
Query q = session.createQuery( "from BasicEntity be where be.data ilike 'Produ%'" );
|
||||||
|
List l = q.getResultList();
|
||||||
|
assertEquals( 5, l.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotIlike(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
Query q = session.createQuery( "from BasicEntity be where be.data not ilike 'Produ%'" );
|
||||||
|
List l = q.getResultList();
|
||||||
|
assertEquals( 4, l.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIlikeEscape(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
Query q = session.createQuery( "from BasicEntity be where be.data ilike 'Pr%$_%' escape '$'" );
|
||||||
|
List l = q.getResultList();
|
||||||
|
assertEquals( 3, l.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotIlikeEscape(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
Query q = session.createQuery( "from BasicEntity be where be.data not ilike 'Pr%$_%' escape '$'" );
|
||||||
|
List l = q.getResultList();
|
||||||
|
assertEquals( 6, l.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue