HHH-17772 support @OrderBy for @Query methods

This commit is contained in:
Gavin King 2024-02-26 10:49:34 +01:00
parent 1196f72798
commit 568ad5804e
5 changed files with 92 additions and 44 deletions

View File

@ -11,7 +11,6 @@ import org.hibernate.AssertionFailure;
import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.ImportContextImpl;
import org.hibernate.jpamodelgen.ProcessLaterException;
import org.hibernate.jpamodelgen.annotation.CriteriaFinderMethod.OrderBy;
import org.hibernate.jpamodelgen.model.ImportContext;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.Metamodel;
@ -21,7 +20,6 @@ import org.hibernate.jpamodelgen.util.Constants;
import org.hibernate.jpamodelgen.validation.ProcessorSessionFactory;
import org.hibernate.jpamodelgen.validation.Validation;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.Order;
import org.hibernate.query.criteria.JpaEntityJoin;
import org.hibernate.query.criteria.JpaRoot;
import org.hibernate.query.criteria.JpaSelection;
@ -67,26 +65,7 @@ import static javax.lang.model.util.ElementFilter.methodsIn;
import static org.hibernate.internal.util.StringHelper.qualify;
import static org.hibernate.jpamodelgen.annotation.QueryMethod.isOrderParam;
import static org.hibernate.jpamodelgen.annotation.QueryMethod.isPageParam;
import static org.hibernate.jpamodelgen.util.Constants.FIND;
import static org.hibernate.jpamodelgen.util.Constants.HIB_ORDER;
import static org.hibernate.jpamodelgen.util.Constants.HIB_SESSION;
import static org.hibernate.jpamodelgen.util.Constants.HIB_STATELESS_SESSION;
import static org.hibernate.jpamodelgen.util.Constants.HQL;
import static org.hibernate.jpamodelgen.util.Constants.ITERABLE;
import static org.hibernate.jpamodelgen.util.Constants.JD_DELETE;
import static org.hibernate.jpamodelgen.util.Constants.JD_FIND;
import static org.hibernate.jpamodelgen.util.Constants.JD_INSERT;
import static org.hibernate.jpamodelgen.util.Constants.JD_ORDER;
import static org.hibernate.jpamodelgen.util.Constants.JD_PAGE_REQUEST;
import static org.hibernate.jpamodelgen.util.Constants.JD_QUERY;
import static org.hibernate.jpamodelgen.util.Constants.JD_REPOSITORY;
import static org.hibernate.jpamodelgen.util.Constants.JD_SAVE;
import static org.hibernate.jpamodelgen.util.Constants.JD_SORT;
import static org.hibernate.jpamodelgen.util.Constants.JD_UPDATE;
import static org.hibernate.jpamodelgen.util.Constants.LIST;
import static org.hibernate.jpamodelgen.util.Constants.MUTINY_SESSION;
import static org.hibernate.jpamodelgen.util.Constants.SESSION_TYPES;
import static org.hibernate.jpamodelgen.util.Constants.SQL;
import static org.hibernate.jpamodelgen.util.Constants.*;
import static org.hibernate.jpamodelgen.util.NullnessUtil.castNonNull;
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
import static org.hibernate.jpamodelgen.util.TypeUtils.determineAccessTypeForHierarchy;
@ -1327,16 +1306,10 @@ public class AnnotationMetaEntity extends AnnotationMeta {
final List<String> paramNames = parameterNames( method );
final List<String> paramTypes = parameterTypes( method );
final String[] sessionType = sessionTypeFromParameters( paramNames, paramTypes );
if ( returnType != null && returnType.getKind() == TypeKind.DECLARED ) {
final DeclaredType declaredType = (DeclaredType) returnType;
if ( !declaredType.getTypeArguments().isEmpty() ) {
context.message( method, mirror, value,
"query result type may not be a generic type"
+ " (change '" + returnType +
"' to '" + context.getTypeUtils().erasure( returnType ) + "')",
Diagnostic.Kind.ERROR );
}
}
final DeclaredType resultType = resultType( method, returnType, mirror, value );
final List<OrderBy> orderBys = resultType == null
? emptyList()
: orderByList( method, (TypeElement) resultType.asElement() );
final QueryMethod attribute =
new QueryMethod(
this,
@ -1351,6 +1324,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
repository,
sessionType[0],
sessionType[1],
orderBys,
context.addNonnullAnnotation(),
jakartaDataRepository
);
@ -1369,6 +1343,24 @@ public class AnnotationMetaEntity extends AnnotationMeta {
}
}
private @Nullable DeclaredType resultType(
ExecutableElement method, @Nullable TypeMirror returnType, AnnotationMirror mirror, AnnotationValue value) {
if ( returnType != null && returnType.getKind() == TypeKind.DECLARED ) {
final DeclaredType resultType = (DeclaredType) returnType;
if ( !resultType.getTypeArguments().isEmpty() ) {
context.message(method, mirror, value,
"query result type may not be a generic type"
+ " (change '" + returnType +
"' to '" + context.getTypeUtils().erasure(returnType) + "')",
Diagnostic.Kind.ERROR );
}
return resultType;
}
else {
return null;
}
}
private static boolean isInsertUpdateDelete(String hql) {
final String trimmed = hql.trim();
final String keyword = trimmed.length() > 6 ? trimmed.substring(0, 6) : "";

View File

@ -361,15 +361,4 @@ public class CriteriaFinderMethod extends AbstractFinderMethod {
}
return type;
}
static class OrderBy {
String fieldName;
boolean descending;
boolean ignoreCase;
public OrderBy(String fieldName, boolean descending, boolean ignoreCase) {
this.fieldName = fieldName;
this.descending = descending;
this.ignoreCase = ignoreCase;
}
}
}

View File

@ -0,0 +1,19 @@
/*
* 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.jpamodelgen.annotation;
class OrderBy {
String fieldName;
boolean descending;
boolean ignoreCase;
public OrderBy(String fieldName, boolean descending, boolean ignoreCase) {
this.fieldName = fieldName;
this.descending = descending;
this.ignoreCase = ignoreCase;
}
}

View File

@ -10,6 +10,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.jpamodelgen.util.Constants;
import java.util.Arrays;
import java.util.List;
import static org.hibernate.jpamodelgen.util.StringUtil.getUpperUnderscoreCaseFromLowerCamelCase;
@ -23,8 +24,9 @@ public class QueryMethod extends AbstractQueryMethod {
private final @Nullable String containerTypeName;
private final boolean isUpdate;
private final boolean isNative;
private final List<OrderBy> orderBys;
public QueryMethod(
QueryMethod(
AnnotationMetaEntity annotationMetaEntity,
String methodName,
String queryString,
@ -39,6 +41,7 @@ public class QueryMethod extends AbstractQueryMethod {
boolean belongsToDao,
String sessionType,
String sessionName,
List<OrderBy> orderBys,
boolean addNonnullAnnotation,
boolean dataRepository) {
super( annotationMetaEntity,
@ -51,6 +54,7 @@ public class QueryMethod extends AbstractQueryMethod {
this.containerTypeName = containerTypeName;
this.isUpdate = isUpdate;
this.isNative = isNative;
this.orderBys = orderBys;
}
@Override
@ -85,12 +89,51 @@ public class QueryMethod extends AbstractQueryMethod {
castResult( declaration, returnType );
createQuery( declaration );
boolean unwrapped = setParameters( paramTypes, declaration );
unwrapped = orderBy( declaration, unwrapped);
execute( declaration, unwrapped );
convertExceptions( declaration );
closingBrace( declaration );
return declaration.toString();
}
private boolean orderBy(StringBuilder declaration, boolean unwrapped) {
if ( !orderBys.isEmpty() && returnTypeName!=null ) {
unwrapQuery( declaration, unwrapped );
declaration.append("\n\t\t\t.setOrder(");
if ( orderBys.size() > 1) {
annotationMetaEntity.staticImport(Arrays.class.getName(), "asList");
declaration
.append("asList(");
}
boolean first = true;
for (OrderBy orderBy : orderBys) {
if (first) {
first = false;
}
else {
declaration
.append(",\n\t\t\t\t\t\t\t");
}
declaration
.append(annotationMetaEntity.importType(Constants.HIB_ORDER))
.append(orderBy.descending ? ".desc(" : ".asc(")
.append(annotationMetaEntity.importType(returnTypeName))
.append(".class, \"")
.append(orderBy.fieldName)
.append("\")");
}
if ( orderBys.size() > 1) {
declaration
.append(')');
}
declaration.append(')');
return true;
}
else {
return unwrapped;
}
}
private void createQuery(StringBuilder declaration) {
declaration
.append(sessionName)

View File

@ -101,4 +101,9 @@ public interface BookAuthorRepository {
@Find
List<Book> everyBook2(PageRequest<? super Book> pageRequest);
@Query("from Book")
@OrderBy("isbn")
@OrderBy(value = "publicationDate", descending = true)
List<Book> everyBook3(PageRequest<? super Book> pageRequest);
}