HHH-17773 case-insensitive ordering

This commit is contained in:
Gavin King 2024-02-23 18:33:50 +01:00
parent d0f3cdeb96
commit 484fcb2984
12 changed files with 101 additions and 10 deletions

View File

@ -40,6 +40,7 @@ public class Order<X> {
private final String attributeName; private final String attributeName;
private final NullPrecedence nullPrecedence; private final NullPrecedence nullPrecedence;
private final int element; private final int element;
private final boolean ignoreCase;
private Order(SortDirection order, NullPrecedence nullPrecedence, SingularAttribute<X, ?> attribute) { private Order(SortDirection order, NullPrecedence nullPrecedence, SingularAttribute<X, ?> attribute) {
this.order = order; this.order = order;
@ -48,6 +49,17 @@ private Order(SortDirection order, NullPrecedence nullPrecedence, SingularAttrib
this.entityClass = attribute.getDeclaringType().getJavaType(); this.entityClass = attribute.getDeclaringType().getJavaType();
this.nullPrecedence = nullPrecedence; this.nullPrecedence = nullPrecedence;
this.element = 1; this.element = 1;
this.ignoreCase = false;
}
private Order(SortDirection order, NullPrecedence nullPrecedence, SingularAttribute<X, ?> attribute, boolean ignoreCase) {
this.order = order;
this.attribute = attribute;
this.attributeName = attribute.getName();
this.entityClass = attribute.getDeclaringType().getJavaType();
this.nullPrecedence = nullPrecedence;
this.element = 1;
this.ignoreCase = ignoreCase;
} }
private Order(SortDirection order, NullPrecedence nullPrecedence, Class<X> entityClass, String attributeName) { private Order(SortDirection order, NullPrecedence nullPrecedence, Class<X> entityClass, String attributeName) {
@ -57,6 +69,7 @@ private Order(SortDirection order, NullPrecedence nullPrecedence, Class<X> entit
this.attribute = null; this.attribute = null;
this.nullPrecedence = nullPrecedence; this.nullPrecedence = nullPrecedence;
this.element = 1; this.element = 1;
this.ignoreCase = false;
} }
private Order(SortDirection order, NullPrecedence nullPrecedence, int element) { private Order(SortDirection order, NullPrecedence nullPrecedence, int element) {
@ -66,6 +79,17 @@ private Order(SortDirection order, NullPrecedence nullPrecedence, int element) {
this.attribute = null; this.attribute = null;
this.nullPrecedence = nullPrecedence; this.nullPrecedence = nullPrecedence;
this.element = element; this.element = element;
this.ignoreCase = false;
}
private Order(SortDirection order, NullPrecedence nullPrecedence, Class<X> entityClass, String attributeName, boolean ignoreCase) {
this.order = order;
this.entityClass = entityClass;
this.attributeName = attributeName;
this.attribute = null;
this.nullPrecedence = nullPrecedence;
this.element = 1;
this.ignoreCase = ignoreCase;
} }
public static <T> Order<T> asc(SingularAttribute<T,?> attribute) { public static <T> Order<T> asc(SingularAttribute<T,?> attribute) {
@ -80,6 +104,10 @@ public static <T> Order<T> by(SingularAttribute<T,?> attribute, SortDirection di
return new Order<>(direction, NullPrecedence.NONE, attribute); return new Order<>(direction, NullPrecedence.NONE, attribute);
} }
public static <T> Order<T> by(SingularAttribute<T,?> attribute, SortDirection direction, boolean ignoreCase) {
return new Order<>(direction, NullPrecedence.NONE, attribute, ignoreCase);
}
public static <T> Order<T> by(SingularAttribute<T,?> attribute, SortDirection direction, NullPrecedence nullPrecedence) { public static <T> Order<T> by(SingularAttribute<T,?> attribute, SortDirection direction, NullPrecedence nullPrecedence) {
return new Order<>(direction, nullPrecedence, attribute); return new Order<>(direction, nullPrecedence, attribute);
} }
@ -96,6 +124,10 @@ public static <T> Order<T> by(Class<T> entityClass, String attributeName, SortDi
return new Order<>( direction, NullPrecedence.NONE, entityClass, attributeName ); return new Order<>( direction, NullPrecedence.NONE, entityClass, attributeName );
} }
public static <T> Order<T> by(Class<T> entityClass, String attributeName, SortDirection direction, boolean ignoreCase) {
return new Order<>( direction, NullPrecedence.NONE, entityClass, attributeName, ignoreCase );
}
public static <T> Order<T> by(Class<T> entityClass, String attributeName, SortDirection direction, NullPrecedence nullPrecedence) { public static <T> Order<T> by(Class<T> entityClass, String attributeName, SortDirection direction, NullPrecedence nullPrecedence) {
return new Order<>( direction, nullPrecedence, entityClass, attributeName ); return new Order<>( direction, nullPrecedence, entityClass, attributeName );
} }
@ -124,6 +156,10 @@ public NullPrecedence getNullPrecedence() {
return nullPrecedence; return nullPrecedence;
} }
public boolean isCaseInsensitive() {
return ignoreCase;
}
public SingularAttribute<X, ?> getAttribute() { public SingularAttribute<X, ?> getAttribute() {
return attribute; return attribute;
} }

View File

@ -1001,6 +1001,7 @@ <Y extends Comparable<? super Y>> JpaPredicate between(
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Ordering // Ordering
JpaOrder sort(JpaExpression<?> sortExpression, SortDirection sortOrder, NullPrecedence nullPrecedence, boolean ignoreCase);
JpaOrder sort(JpaExpression<?> sortExpression, SortDirection sortOrder, NullPrecedence nullPrecedence); JpaOrder sort(JpaExpression<?> sortExpression, SortDirection sortOrder, NullPrecedence nullPrecedence);
JpaOrder sort(JpaExpression<?> sortExpression, SortDirection sortOrder); JpaOrder sort(JpaExpression<?> sortExpression, SortDirection sortOrder);
JpaOrder sort(JpaExpression<?> sortExpression); JpaOrder sort(JpaExpression<?> sortExpression);

View File

@ -58,7 +58,6 @@
import org.hibernate.query.criteria.JpaWindowFrame; import org.hibernate.query.criteria.JpaWindowFrame;
import org.hibernate.query.NullPrecedence; import org.hibernate.query.NullPrecedence;
import org.hibernate.query.sqm.TemporalUnit; import org.hibernate.query.sqm.TemporalUnit;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import jakarta.persistence.Tuple; import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CollectionJoin; import jakarta.persistence.criteria.CollectionJoin;
@ -1210,6 +1209,11 @@ public JpaOrder sort(JpaExpression<?> sortExpression, SortDirection sortOrder, N
return criteriaBuilder.sort( sortExpression, sortOrder, nullPrecedence ); return criteriaBuilder.sort( sortExpression, sortOrder, nullPrecedence );
} }
@Override
public JpaOrder sort(JpaExpression<?> sortExpression, SortDirection sortOrder, NullPrecedence nullPrecedence, boolean ignoreCase) {
return criteriaBuilder.sort( sortExpression, sortOrder, nullPrecedence, ignoreCase );
}
@Override @Override
public JpaOrder sort(JpaExpression<?> sortExpression, SortDirection sortOrder) { public JpaOrder sort(JpaExpression<?> sortExpression, SortDirection sortOrder) {
return criteriaBuilder.sort( sortExpression, sortOrder ); return criteriaBuilder.sort( sortExpression, sortOrder );

View File

@ -1022,6 +1022,13 @@ SqmSortSpecification sort(
SortDirection sortOrder, SortDirection sortOrder,
NullPrecedence nullPrecedence); NullPrecedence nullPrecedence);
@Override
SqmSortSpecification sort(
JpaExpression<?> sortExpression,
SortDirection sortOrder,
NullPrecedence nullPrecedence,
boolean ignoreCase);
@Override @Override
SqmSortSpecification sort(JpaExpression<?> sortExpression, SortDirection sortOrder); SqmSortSpecification sort(JpaExpression<?> sortExpression, SortDirection sortOrder);

View File

@ -548,6 +548,11 @@ public SqmSortSpecification sort(JpaExpression<?> sortExpression, SortDirection
return new SqmSortSpecification( (SqmExpression<?>) sortExpression, sortOrder, nullPrecedence ); return new SqmSortSpecification( (SqmExpression<?>) sortExpression, sortOrder, nullPrecedence );
} }
@Override
public SqmSortSpecification sort(JpaExpression<?> sortExpression, SortDirection sortOrder, NullPrecedence nullPrecedence, boolean ignoreCase) {
return new SqmSortSpecification( (SqmExpression<?>) sortExpression, sortOrder, nullPrecedence, ignoreCase );
}
@Override @Override
public SqmSortSpecification sort(JpaExpression<?> sortExpression, SortDirection sortOrder) { public SqmSortSpecification sort(JpaExpression<?> sortExpression, SortDirection sortOrder) {
return new SqmSortSpecification( (SqmExpression<?>) sortExpression, sortOrder ); return new SqmSortSpecification( (SqmExpression<?>) sortExpression, sortOrder );

View File

@ -592,7 +592,7 @@ static <T> JpaOrder sortSpecification(SqmSelectStatement<T> sqm, Order<? super T
throw new IllegalQueryOperationException("Select item was of wrong entity type"); throw new IllegalQueryOperationException("Select item was of wrong entity type");
} }
final SqmPath<Object> path = root.get( order.getAttributeName() ); final SqmPath<Object> path = root.get( order.getAttributeName() );
return builder.sort( path, order.getDirection(), order.getNullPrecedence() ); return builder.sort( path, order.getDirection(), order.getNullPrecedence(), order.isCaseInsensitive() );
} }
else { else {
throw new IllegalQueryOperationException("Select item was not an entity type"); throw new IllegalQueryOperationException("Select item was not an entity type");

View File

@ -2541,7 +2541,8 @@ public SortSpecification visitSortSpecification(SqmSortSpecification sortSpecifi
return new SortSpecification( return new SortSpecification(
expression, expression,
sortSpecification.getSortDirection(), sortSpecification.getSortDirection(),
sortSpecification.getNullPrecedence() sortSpecification.getNullPrecedence(),
sortSpecification.isIgnoreCase()
); );
} }

View File

@ -21,19 +21,28 @@
public class SqmSortSpecification implements JpaOrder { public class SqmSortSpecification implements JpaOrder {
private final SqmExpression sortExpression; private final SqmExpression sortExpression;
private final SortDirection sortOrder; private final SortDirection sortOrder;
private final boolean ignoreCase;
private NullPrecedence nullPrecedence; private NullPrecedence nullPrecedence;
public SqmSortSpecification( public SqmSortSpecification(
SqmExpression sortExpression, SqmExpression sortExpression,
SortDirection sortOrder, SortDirection sortOrder,
NullPrecedence nullPrecedence) { NullPrecedence nullPrecedence) {
this( sortExpression, sortOrder, nullPrecedence, false );
}
public SqmSortSpecification(
SqmExpression sortExpression,
SortDirection sortOrder,
NullPrecedence nullPrecedence,
boolean ignoreCase) {
assert sortExpression != null; assert sortExpression != null;
assert sortOrder != null; assert sortOrder != null;
assert nullPrecedence != null; assert nullPrecedence != null;
this.sortExpression = sortExpression; this.sortExpression = sortExpression;
this.sortOrder = sortOrder; this.sortOrder = sortOrder;
this.nullPrecedence = nullPrecedence; this.nullPrecedence = nullPrecedence;
this.ignoreCase = ignoreCase;
} }
public SqmSortSpecification(SqmExpression sortExpression) { public SqmSortSpecification(SqmExpression sortExpression) {
@ -45,7 +54,7 @@ public SqmSortSpecification(SqmExpression sortExpression, SortDirection sortOrde
} }
public SqmSortSpecification copy(SqmCopyContext context) { public SqmSortSpecification copy(SqmCopyContext context) {
return new SqmSortSpecification( sortExpression.copy( context ), sortOrder, nullPrecedence ); return new SqmSortSpecification( sortExpression.copy( context ), sortOrder, nullPrecedence, ignoreCase );
} }
public SqmExpression<?> getSortExpression() { public SqmExpression<?> getSortExpression() {
@ -57,6 +66,10 @@ public SortDirection getSortDirection() {
return sortOrder; return sortOrder;
} }
public boolean isIgnoreCase() {
return ignoreCase;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// JPA // JPA
@ -75,7 +88,7 @@ public NullPrecedence getNullPrecedence() {
@Override @Override
public JpaOrder reverse() { public JpaOrder reverse() {
SortDirection newSortOrder = this.sortOrder == null ? SortDirection.DESCENDING : sortOrder.reverse(); SortDirection newSortOrder = this.sortOrder == null ? SortDirection.DESCENDING : sortOrder.reverse();
return new SqmSortSpecification( sortExpression, newSortOrder, nullPrecedence ); return new SqmSortSpecification( sortExpression, newSortOrder, nullPrecedence, ignoreCase );
} }
@Override @Override

View File

@ -4370,21 +4370,26 @@ public void visitSortSpecification(SortSpecification sortSpecification) {
final Expression sortExpression = sortSpecification.getSortExpression(); final Expression sortExpression = sortSpecification.getSortExpression();
final NullPrecedence nullPrecedence = sortSpecification.getNullPrecedence(); final NullPrecedence nullPrecedence = sortSpecification.getNullPrecedence();
final SortDirection sortOrder = sortSpecification.getSortOrder(); final SortDirection sortOrder = sortSpecification.getSortOrder();
final boolean ignoreCase = sortSpecification.isIgnoreCase();
final SqlTuple sqlTuple = SqlTupleContainer.getSqlTuple( sortExpression ); final SqlTuple sqlTuple = SqlTupleContainer.getSqlTuple( sortExpression );
if ( sqlTuple != null ) { if ( sqlTuple != null ) {
String separator = NO_SEPARATOR; String separator = NO_SEPARATOR;
for ( Expression expression : sqlTuple.getExpressions() ) { for ( Expression expression : sqlTuple.getExpressions() ) {
appendSql( separator ); appendSql( separator );
visitSortSpecification( expression, sortOrder, nullPrecedence ); visitSortSpecification( expression, sortOrder, nullPrecedence, ignoreCase );
separator = COMMA_SEPARATOR; separator = COMMA_SEPARATOR;
} }
} }
else { else {
visitSortSpecification( sortExpression, sortOrder, nullPrecedence ); visitSortSpecification( sortExpression, sortOrder, nullPrecedence, ignoreCase );
} }
} }
protected void visitSortSpecification(Expression sortExpression, SortDirection sortOrder, NullPrecedence nullPrecedence) { protected void visitSortSpecification(
Expression sortExpression,
SortDirection sortOrder,
NullPrecedence nullPrecedence,
boolean ignoreCase) {
if ( nullPrecedence == null || nullPrecedence == NullPrecedence.NONE ) { if ( nullPrecedence == null || nullPrecedence == NullPrecedence.NONE ) {
nullPrecedence = sessionFactory.getSessionFactoryOptions().getDefaultNullPrecedence(); nullPrecedence = sessionFactory.getSessionFactoryOptions().getDefaultNullPrecedence();
} }
@ -4395,6 +4400,10 @@ protected void visitSortSpecification(Expression sortExpression, SortDirection s
emulateSortSpecificationNullPrecedence( sortExpression, nullPrecedence ); emulateSortSpecificationNullPrecedence( sortExpression, nullPrecedence );
} }
if ( ignoreCase ) {
appendSql("lower(");
}
if ( inOverOrWithinGroupClause() ) { if ( inOverOrWithinGroupClause() ) {
resolveAliasedExpression( sortExpression ).accept( this ); resolveAliasedExpression( sortExpression ).accept( this );
} }
@ -4402,6 +4411,10 @@ protected void visitSortSpecification(Expression sortExpression, SortDirection s
sortExpression.accept( this ); sortExpression.accept( this );
} }
if ( ignoreCase ) {
appendSql(")");
}
if ( sortOrder == SortDirection.DESCENDING ) { if ( sortOrder == SortDirection.DESCENDING ) {
appendSql( " desc" ); appendSql( " desc" );
} }

View File

@ -19,18 +19,24 @@ public class SortSpecification implements SqlAstNode {
private final Expression sortExpression; private final Expression sortExpression;
private final SortDirection sortOrder; private final SortDirection sortOrder;
private final NullPrecedence nullPrecedence; private final NullPrecedence nullPrecedence;
private final boolean ignoreCase;
public SortSpecification(Expression sortExpression, SortDirection sortOrder) { public SortSpecification(Expression sortExpression, SortDirection sortOrder) {
this( sortExpression, sortOrder, NullPrecedence.NONE ); this( sortExpression, sortOrder, NullPrecedence.NONE );
} }
public SortSpecification(Expression sortExpression, SortDirection sortOrder, NullPrecedence nullPrecedence) { public SortSpecification(Expression sortExpression, SortDirection sortOrder, NullPrecedence nullPrecedence) {
this(sortExpression, sortOrder, nullPrecedence, false);
}
public SortSpecification(Expression sortExpression, SortDirection sortOrder, NullPrecedence nullPrecedence, boolean ignoreCase) {
assert sortExpression != null; assert sortExpression != null;
assert sortOrder != null; assert sortOrder != null;
assert nullPrecedence != null; assert nullPrecedence != null;
this.sortExpression = sortExpression; this.sortExpression = sortExpression;
this.sortOrder = sortOrder; this.sortOrder = sortOrder;
this.nullPrecedence = nullPrecedence; this.nullPrecedence = nullPrecedence;
this.ignoreCase = ignoreCase;
} }
public Expression getSortExpression() { public Expression getSortExpression() {
@ -45,6 +51,10 @@ public NullPrecedence getNullPrecedence() {
return nullPrecedence; return nullPrecedence;
} }
public boolean isIgnoreCase() {
return ignoreCase;
}
@Override @Override
public void accept(SqlAstWalker sqlTreeWalker) { public void accept(SqlAstWalker sqlTreeWalker) {
sqlTreeWalker.visitSortSpecification( this ); sqlTreeWalker.visitSortSpecification( this );

View File

@ -248,7 +248,7 @@ boolean setOrder(StringBuilder declaration, boolean unwrapped, String paramName,
.append("sort.property()") .append("sort.property()")
.append(",\n\t\t\t\t\t\t") .append(",\n\t\t\t\t\t\t")
.append("sort.isAscending() ? ASCENDING : DESCENDING") .append("sort.isAscending() ? ASCENDING : DESCENDING")
.append(")));\n\t\t\t}})"); .append(", sort.ignoreCase())));\n\t\t\t}})");
return true; return true;
} }
else { else {

View File

@ -234,6 +234,7 @@ private static void orderBy(StringBuilder declaration, OrderBy orderBy) {
} }
private static void orderBy(StringBuilder declaration, String paramName) { private static void orderBy(StringBuilder declaration, String paramName) {
// TODO: Sort.ignoreCase()
declaration declaration
.append("\n\t\t") .append("\n\t\t")
.append(paramName) .append(paramName)