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 NullPrecedence nullPrecedence;
private final int element;
private final boolean ignoreCase;
private Order(SortDirection order, NullPrecedence nullPrecedence, SingularAttribute<X, ?> attribute) {
this.order = order;
@ -48,6 +49,17 @@ public class Order<X> {
this.entityClass = attribute.getDeclaringType().getJavaType();
this.nullPrecedence = nullPrecedence;
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) {
@ -57,6 +69,7 @@ public class Order<X> {
this.attribute = null;
this.nullPrecedence = nullPrecedence;
this.element = 1;
this.ignoreCase = false;
}
private Order(SortDirection order, NullPrecedence nullPrecedence, int element) {
@ -66,6 +79,17 @@ public class Order<X> {
this.attribute = null;
this.nullPrecedence = nullPrecedence;
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) {
@ -80,6 +104,10 @@ public class Order<X> {
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) {
return new Order<>(direction, nullPrecedence, attribute);
}
@ -96,6 +124,10 @@ public class Order<X> {
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) {
return new Order<>( direction, nullPrecedence, entityClass, attributeName );
}
@ -124,6 +156,10 @@ public class Order<X> {
return nullPrecedence;
}
public boolean isCaseInsensitive() {
return ignoreCase;
}
public SingularAttribute<X, ?> getAttribute() {
return attribute;
}

View File

@ -1001,6 +1001,7 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 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);
JpaOrder sort(JpaExpression<?> sortExpression);

View File

@ -58,7 +58,6 @@ import org.hibernate.query.criteria.JpaWindow;
import org.hibernate.query.criteria.JpaWindowFrame;
import org.hibernate.query.NullPrecedence;
import org.hibernate.query.sqm.TemporalUnit;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CollectionJoin;
@ -1210,6 +1209,11 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
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
public JpaOrder sort(JpaExpression<?> sortExpression, SortDirection sortOrder) {
return criteriaBuilder.sort( sortExpression, sortOrder );

View File

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

View File

@ -548,6 +548,11 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
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
public SqmSortSpecification sort(JpaExpression<?> sortExpression, SortDirection sortOrder) {
return new SqmSortSpecification( (SqmExpression<?>) sortExpression, sortOrder );

View File

@ -592,7 +592,7 @@ public class SqmUtil {
throw new IllegalQueryOperationException("Select item was of wrong entity type");
}
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 {
throw new IllegalQueryOperationException("Select item was not an entity type");

View File

@ -2541,7 +2541,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return new SortSpecification(
expression,
sortSpecification.getSortDirection(),
sortSpecification.getNullPrecedence()
sortSpecification.getNullPrecedence(),
sortSpecification.isIgnoreCase()
);
}

View File

@ -21,19 +21,28 @@ import java.util.Objects;
public class SqmSortSpecification implements JpaOrder {
private final SqmExpression sortExpression;
private final SortDirection sortOrder;
private final boolean ignoreCase;
private NullPrecedence nullPrecedence;
public SqmSortSpecification(
SqmExpression sortExpression,
SortDirection sortOrder,
NullPrecedence nullPrecedence) {
this( sortExpression, sortOrder, nullPrecedence, false );
}
public SqmSortSpecification(
SqmExpression sortExpression,
SortDirection sortOrder,
NullPrecedence nullPrecedence,
boolean ignoreCase) {
assert sortExpression != null;
assert sortOrder != null;
assert nullPrecedence != null;
this.sortExpression = sortExpression;
this.sortOrder = sortOrder;
this.nullPrecedence = nullPrecedence;
this.ignoreCase = ignoreCase;
}
public SqmSortSpecification(SqmExpression sortExpression) {
@ -45,7 +54,7 @@ public class SqmSortSpecification implements JpaOrder {
}
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() {
@ -57,6 +66,10 @@ public class SqmSortSpecification implements JpaOrder {
return sortOrder;
}
public boolean isIgnoreCase() {
return ignoreCase;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// JPA
@ -75,7 +88,7 @@ public class SqmSortSpecification implements JpaOrder {
@Override
public JpaOrder reverse() {
SortDirection newSortOrder = this.sortOrder == null ? SortDirection.DESCENDING : sortOrder.reverse();
return new SqmSortSpecification( sortExpression, newSortOrder, nullPrecedence );
return new SqmSortSpecification( sortExpression, newSortOrder, nullPrecedence, ignoreCase );
}
@Override

View File

@ -4370,21 +4370,26 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
final Expression sortExpression = sortSpecification.getSortExpression();
final NullPrecedence nullPrecedence = sortSpecification.getNullPrecedence();
final SortDirection sortOrder = sortSpecification.getSortOrder();
final boolean ignoreCase = sortSpecification.isIgnoreCase();
final SqlTuple sqlTuple = SqlTupleContainer.getSqlTuple( sortExpression );
if ( sqlTuple != null ) {
String separator = NO_SEPARATOR;
for ( Expression expression : sqlTuple.getExpressions() ) {
appendSql( separator );
visitSortSpecification( expression, sortOrder, nullPrecedence );
visitSortSpecification( expression, sortOrder, nullPrecedence, ignoreCase );
separator = COMMA_SEPARATOR;
}
}
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 ) {
nullPrecedence = sessionFactory.getSessionFactoryOptions().getDefaultNullPrecedence();
}
@ -4395,6 +4400,10 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
emulateSortSpecificationNullPrecedence( sortExpression, nullPrecedence );
}
if ( ignoreCase ) {
appendSql("lower(");
}
if ( inOverOrWithinGroupClause() ) {
resolveAliasedExpression( sortExpression ).accept( this );
}
@ -4402,6 +4411,10 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
sortExpression.accept( this );
}
if ( ignoreCase ) {
appendSql(")");
}
if ( sortOrder == SortDirection.DESCENDING ) {
appendSql( " desc" );
}

View File

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

View File

@ -248,7 +248,7 @@ public abstract class AbstractQueryMethod implements MetaAttribute {
.append("sort.property()")
.append(",\n\t\t\t\t\t\t")
.append("sort.isAscending() ? ASCENDING : DESCENDING")
.append(")));\n\t\t\t}})");
.append(", sort.ignoreCase())));\n\t\t\t}})");
return true;
}
else {

View File

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