HHH-16633 support new Order and Page objects as parameters of query methods
This commit is contained in:
parent
19a75aad9e
commit
f933b064e9
|
@ -46,12 +46,28 @@ import static java.lang.annotation.RetentionPolicy.CLASS;
|
||||||
* <li>an entity type,
|
* <li>an entity type,
|
||||||
* <li>{@link java.util.List},
|
* <li>{@link java.util.List},
|
||||||
* <li>{@link org.hibernate.query.Query},
|
* <li>{@link org.hibernate.query.Query},
|
||||||
|
* <li>{@link org.hibernate.query.SelectionQuery},
|
||||||
* <li>{@link jakarta.persistence.Query}, or
|
* <li>{@link jakarta.persistence.Query}, or
|
||||||
* <li>{@link jakarta.persistence.TypedQuery}.
|
* <li>{@link jakarta.persistence.TypedQuery}.
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* The method parameters must match the parameters of the HQL query,
|
* The method parameters must match the parameters of the HQL query,
|
||||||
* either by name or by position.
|
* either by name or by position:
|
||||||
|
* <ul>
|
||||||
|
* <li>an ordinal query parameter of form {@code ?n} is matched to
|
||||||
|
* the <em>n</em>th parameter of the method, and
|
||||||
|
* <li>a named query parameter of form {@code :name} is matched to
|
||||||
|
* the method parameter {@code name}.
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* As an exception, the method may have:
|
||||||
|
* <ul>
|
||||||
|
* <li>a parameter with type {@code Page}, and/or
|
||||||
|
* <li>a parameter with type {@code Order<? super E>},
|
||||||
|
* {@code List<Order<? super E>>}, or {@code Order<? super E>...}
|
||||||
|
* (varargs) where {@code E} is the entity type returned by the
|
||||||
|
* query.
|
||||||
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* Queries specified using this annotation are always validated by
|
* Queries specified using this annotation are always validated by
|
||||||
* the Metamodel Generator, and so it isn't necessary to specify the
|
* the Metamodel Generator, and so it isn't necessary to specify the
|
||||||
|
|
|
@ -51,7 +51,13 @@ import static java.lang.annotation.RetentionPolicy.CLASS;
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* The method parameters must match the parameters of the SQL query,
|
* The method parameters must match the parameters of the SQL query,
|
||||||
* either by name or by position.
|
* either by name or by position:
|
||||||
|
* <ul>
|
||||||
|
* <li>an ordinal query parameter of form {@code ?n} is matched to
|
||||||
|
* the <em>n</em>th parameter of the method, and
|
||||||
|
* <li>a named query parameter of form {@code :name} is matched to
|
||||||
|
* the method parameter {@code name}.
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Gavin King
|
* @author Gavin King
|
||||||
* @since 6.3
|
* @since 6.3
|
||||||
|
|
|
@ -17,6 +17,11 @@ import java.util.Objects;
|
||||||
* This is a convenience class which allows query result ordering
|
* This is a convenience class which allows query result ordering
|
||||||
* rules to be passed around the system before being applied to
|
* rules to be passed around the system before being applied to
|
||||||
* a {@link Query} by calling {@link SelectionQuery#setOrder}.
|
* a {@link Query} by calling {@link SelectionQuery#setOrder}.
|
||||||
|
* <p>
|
||||||
|
* A parameter of a {@linkplain org.hibernate.annotations.processing.HQL
|
||||||
|
* HQL query method} may be declared with type {@code Order<? super E>},
|
||||||
|
* {@code List<Order<? super E>>}, or {@code Order<? super E>...} (varargs)
|
||||||
|
* where {@code E} is the entity type returned by the query.
|
||||||
*
|
*
|
||||||
* @param <X> The result type of the query to be sorted
|
* @param <X> The result type of the query to be sorted
|
||||||
*
|
*
|
||||||
|
|
|
@ -15,6 +15,9 @@ import org.hibernate.Incubating;
|
||||||
* This is a convenience class which allows a reference to a page of
|
* This is a convenience class which allows a reference to a page of
|
||||||
* results to be passed around the system before being applied to
|
* results to be passed around the system before being applied to
|
||||||
* a {@link Query} by calling {@link Query#setPage(Page)}.
|
* a {@link Query} by calling {@link Query#setPage(Page)}.
|
||||||
|
* <p>
|
||||||
|
* A parameter of a {@linkplain org.hibernate.annotations.processing.HQL
|
||||||
|
* HQL query method} may be declared with type {@code Page}.
|
||||||
*
|
*
|
||||||
* @see Query#setPage(Page)
|
* @see Query#setPage(Page)
|
||||||
*
|
*
|
||||||
|
|
|
@ -76,6 +76,11 @@ public class ImportContextImpl implements ImportContext {
|
||||||
result = result.substring( 0, fqcn.indexOf( '[' ) );
|
result = result.substring( 0, fqcn.indexOf( '[' ) );
|
||||||
fqcn = result;
|
fqcn = result;
|
||||||
}
|
}
|
||||||
|
else if ( fqcn.endsWith( "..." ) ) {
|
||||||
|
additionalTypePart = "...";
|
||||||
|
result = result.substring( 0, fqcn.indexOf( "..." ) );
|
||||||
|
fqcn = result;
|
||||||
|
}
|
||||||
|
|
||||||
String pureFqcn = fqcn.replace( '$', '.' );
|
String pureFqcn = fqcn.replace( '$', '.' );
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@ import org.hibernate.jpamodelgen.validation.Validation;
|
||||||
|
|
||||||
import static java.util.Collections.emptySet;
|
import static java.util.Collections.emptySet;
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
import static org.hibernate.jpamodelgen.annotation.QueryMethod.isOrderParam;
|
||||||
|
import static org.hibernate.jpamodelgen.annotation.QueryMethod.isPageParam;
|
||||||
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
|
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
|
||||||
import static org.hibernate.jpamodelgen.util.TypeUtils.determineAnnotationSpecifiedAccessType;
|
import static org.hibernate.jpamodelgen.util.TypeUtils.determineAnnotationSpecifiedAccessType;
|
||||||
import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationMirror;
|
import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationMirror;
|
||||||
|
@ -385,7 +387,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
);
|
);
|
||||||
putMember( attribute.getPropertyName() + paramTypes, attribute );
|
putMember( attribute.getPropertyName() + paramTypes, attribute );
|
||||||
|
|
||||||
checkParameters( method, paramNames, mirror, hql );
|
checkParameters( method, paramNames, paramTypes, mirror, hql );
|
||||||
if ( !isNative ) {
|
if ( !isNative ) {
|
||||||
// checkHqlSyntax( method, mirror, hql );
|
// checkHqlSyntax( method, mirror, hql );
|
||||||
Validation.validate(
|
Validation.validate(
|
||||||
|
@ -399,10 +401,12 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkParameters(ExecutableElement method, List<String> paramNames, AnnotationMirror mirror, String hql) {
|
private void checkParameters(ExecutableElement method, List<String> paramNames, List<String> paramTypes, AnnotationMirror mirror, String hql) {
|
||||||
for (int i = 1; i <= paramNames.size(); i++) {
|
for (int i = 1; i <= paramNames.size(); i++) {
|
||||||
final String param = paramNames.get(i-1);
|
final String param = paramNames.get(i-1);
|
||||||
if ( !hql.contains(":" + param) && !hql.contains("?" + i) ) {
|
final String ptype = paramTypes.get(i-1);
|
||||||
|
if ( !hql.contains(":" + param) && !hql.contains("?" + i)
|
||||||
|
&& !isPageParam(ptype) && !isOrderParam(ptype)) {
|
||||||
context.message( method, mirror, "missing query parameter for '" + param
|
context.message( method, mirror, "missing query parameter for '" + param
|
||||||
+ "' (no parameter named :" + param + " or ?" + i + ")", Diagnostic.Kind.ERROR );
|
+ "' (no parameter named :" + param + " or ?" + i + ")", Diagnostic.Kind.ERROR );
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,9 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.jpamodelgen.model.MetaAttribute;
|
import org.hibernate.jpamodelgen.model.MetaAttribute;
|
||||||
import org.hibernate.jpamodelgen.model.Metamodel;
|
import org.hibernate.jpamodelgen.model.Metamodel;
|
||||||
|
import org.hibernate.query.Order;
|
||||||
|
import org.hibernate.query.Page;
|
||||||
|
import org.hibernate.query.SelectionQuery;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -58,6 +61,9 @@ public class QueryMethod implements MetaAttribute {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getAttributeDeclarationString() {
|
public String getAttributeDeclarationString() {
|
||||||
|
List<String> paramTypes = this.paramTypes.stream()
|
||||||
|
.map(ptype->isOrderParam(ptype) && ptype.endsWith("[]") ? ptype.replace("[]", "...") : ptype)
|
||||||
|
.collect(toList());
|
||||||
StringBuilder declaration = new StringBuilder();
|
StringBuilder declaration = new StringBuilder();
|
||||||
declaration
|
declaration
|
||||||
.append("\n/**\n * @see ")
|
.append("\n/**\n * @see ")
|
||||||
|
@ -65,9 +71,14 @@ public class QueryMethod implements MetaAttribute {
|
||||||
.append("#")
|
.append("#")
|
||||||
.append(methodName)
|
.append(methodName)
|
||||||
.append("(")
|
.append("(")
|
||||||
.append(join(",", paramTypes.stream().map(annotationMetaEntity::importType).toArray()))
|
.append(join(",", paramTypes.stream().map(this::strip).map(annotationMetaEntity::importType).toArray()))
|
||||||
.append(")")
|
.append(")")
|
||||||
.append("\n **/\n")
|
.append("\n **/\n");
|
||||||
|
if ( paramTypes.stream().anyMatch(ptype -> ptype.endsWith("..."))) {
|
||||||
|
declaration
|
||||||
|
.append("@SafeVarargs\n");
|
||||||
|
}
|
||||||
|
declaration
|
||||||
.append("public static ");
|
.append("public static ");
|
||||||
StringBuilder type = new StringBuilder();
|
StringBuilder type = new StringBuilder();
|
||||||
if (containerTypeName != null) {
|
if (containerTypeName != null) {
|
||||||
|
@ -88,11 +99,16 @@ public class QueryMethod implements MetaAttribute {
|
||||||
.append(" entityManager");
|
.append(" entityManager");
|
||||||
|
|
||||||
for (int i =0; i<paramNames.size(); i++) {
|
for (int i =0; i<paramNames.size(); i++) {
|
||||||
|
String ptype = paramTypes.get(i);
|
||||||
|
String param = paramNames.get(i);
|
||||||
|
String rptype = returnTypeName != null
|
||||||
|
? ptype.replace(returnTypeName, annotationMetaEntity.importType(returnTypeName))
|
||||||
|
: ptype;
|
||||||
declaration
|
declaration
|
||||||
.append(", ")
|
.append(", ")
|
||||||
.append(annotationMetaEntity.importType(paramTypes.get(i)))
|
.append(annotationMetaEntity.importType(rptype))
|
||||||
.append(" ")
|
.append(" ")
|
||||||
.append(paramNames.get(i));
|
.append(param);
|
||||||
}
|
}
|
||||||
declaration
|
declaration
|
||||||
.append(")")
|
.append(")")
|
||||||
|
@ -104,7 +120,7 @@ public class QueryMethod implements MetaAttribute {
|
||||||
}
|
}
|
||||||
declaration
|
declaration
|
||||||
.append("entityManager.")
|
.append("entityManager.")
|
||||||
.append(isNative ? "createNativeQuery" :"createQuery")
|
.append(isNative ? "createNativeQuery" : "createQuery")
|
||||||
.append("(")
|
.append("(")
|
||||||
.append(getConstantName());
|
.append(getConstantName());
|
||||||
if (returnTypeName != null) {
|
if (returnTypeName != null) {
|
||||||
|
@ -114,8 +130,10 @@ public class QueryMethod implements MetaAttribute {
|
||||||
.append(".class");
|
.append(".class");
|
||||||
}
|
}
|
||||||
declaration.append(")");
|
declaration.append(")");
|
||||||
|
boolean unwrapped = false;
|
||||||
for (int i = 1; i <= paramNames.size(); i++) {
|
for (int i = 1; i <= paramNames.size(); i++) {
|
||||||
String param = paramNames.get(i-1);
|
String param = paramNames.get(i-1);
|
||||||
|
String ptype = paramTypes.get(i-1);
|
||||||
if (queryString.contains(":" + param)) {
|
if (queryString.contains(":" + param)) {
|
||||||
declaration
|
declaration
|
||||||
.append("\n .setParameter(\"")
|
.append("\n .setParameter(\"")
|
||||||
|
@ -132,6 +150,32 @@ public class QueryMethod implements MetaAttribute {
|
||||||
.append(param)
|
.append(param)
|
||||||
.append(")");
|
.append(")");
|
||||||
}
|
}
|
||||||
|
else if (isPageParam(ptype)) {
|
||||||
|
unwrap( declaration, unwrapped );
|
||||||
|
unwrapped = true;
|
||||||
|
declaration
|
||||||
|
.append("\n .setPage(")
|
||||||
|
.append(param)
|
||||||
|
.append(")");
|
||||||
|
}
|
||||||
|
else if (isOrderParam(ptype)) {
|
||||||
|
unwrap( declaration, unwrapped );
|
||||||
|
unwrapped = true;
|
||||||
|
if (ptype.endsWith("...")) {
|
||||||
|
declaration
|
||||||
|
.append("\n .setOrder(")
|
||||||
|
.append(annotationMetaEntity.importType(List.class.getName()))
|
||||||
|
.append(".of(")
|
||||||
|
.append(param)
|
||||||
|
.append("))");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
declaration
|
||||||
|
.append("\n .setOrder(")
|
||||||
|
.append(param)
|
||||||
|
.append(")");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ( containerTypeName == null) {
|
if ( containerTypeName == null) {
|
||||||
declaration.append("\n .getSingleResult()");
|
declaration.append("\n .getSingleResult()");
|
||||||
|
@ -143,9 +187,34 @@ public class QueryMethod implements MetaAttribute {
|
||||||
return declaration.toString();
|
return declaration.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String strip(String type) {
|
||||||
|
int index = type.indexOf("<");
|
||||||
|
String stripped = index > 0 ? type.substring(0, index) : type;
|
||||||
|
return type.endsWith("...") ? stripped + "..." : stripped;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isPageParam(String ptype) {
|
||||||
|
return Page.class.getName().equals(ptype);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isOrderParam(String ptype) {
|
||||||
|
return ptype.startsWith(Order.class.getName())
|
||||||
|
|| ptype.startsWith(List.class.getName() + "<" + Order.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unwrap(StringBuilder declaration, boolean unwrapped) {
|
||||||
|
if ( !unwrapped ) {
|
||||||
|
declaration
|
||||||
|
.append("\n .unwrap(")
|
||||||
|
.append(annotationMetaEntity.importType(SelectionQuery.class.getName()))
|
||||||
|
.append(".class)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getAttributeNameDeclarationString() {
|
public String getAttributeNameDeclarationString() {
|
||||||
return new StringBuilder().append("public static final String ")
|
return new StringBuilder()
|
||||||
|
.append("public static final String ")
|
||||||
.append(getConstantName())
|
.append(getConstantName())
|
||||||
.append(" = \"")
|
.append(" = \"")
|
||||||
.append(queryString)
|
.append(queryString)
|
||||||
|
@ -160,7 +229,9 @@ public class QueryMethod implements MetaAttribute {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return stem + "_" + StringHelper.join("_",
|
return stem + "_" + StringHelper.join("_",
|
||||||
paramTypes.stream().map(StringHelper::unqualify).collect(toList()));
|
paramTypes.stream()
|
||||||
|
.filter(name -> !isPageParam(name) && !isOrderParam(name))
|
||||||
|
.map(StringHelper::unqualify).collect(toList()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,9 @@ package org.hibernate.jpamodelgen.test.hqlsql;
|
||||||
import jakarta.persistence.TypedQuery;
|
import jakarta.persistence.TypedQuery;
|
||||||
import org.hibernate.annotations.processing.HQL;
|
import org.hibernate.annotations.processing.HQL;
|
||||||
import org.hibernate.annotations.processing.SQL;
|
import org.hibernate.annotations.processing.SQL;
|
||||||
|
import org.hibernate.query.Order;
|
||||||
|
import org.hibernate.query.Page;
|
||||||
|
import org.hibernate.query.SelectionQuery;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -13,6 +16,15 @@ public interface Dao {
|
||||||
@HQL("from Book where title like ?1 order by title fetch first ?2 rows only")
|
@HQL("from Book where title like ?1 order by title fetch first ?2 rows only")
|
||||||
List<Book> findFirstNByTitle(String title, int N);
|
List<Book> findFirstNByTitle(String title, int N);
|
||||||
|
|
||||||
|
@HQL("from Book where title like :title")
|
||||||
|
List<Book> findByTitleWithPagination(String title, Order<? super Book> order, Page page);
|
||||||
|
|
||||||
|
@HQL("from Book where title like :title")
|
||||||
|
SelectionQuery<Book> findByTitleWithOrdering(String title, List<Order<? super Book>> order);
|
||||||
|
|
||||||
|
@HQL("from Book where title like :title")
|
||||||
|
SelectionQuery<Book> findByTitleWithOrderingByVarargs(String title, Order<? super Book>... order);
|
||||||
|
|
||||||
@HQL("from Book where isbn = :isbn")
|
@HQL("from Book where isbn = :isbn")
|
||||||
Book findByIsbn(String isbn);
|
Book findByIsbn(String isbn);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue