HHH-17868 introduce @Pattern annotation

This commit is contained in:
Gavin King 2024-03-20 08:12:12 +01:00
parent b5bfe07d36
commit bcc3ea60de
6 changed files with 79 additions and 2 deletions

View File

@ -487,6 +487,14 @@ The natural syntax would be a parameter declaration like `String publisher.name`
List<Book> getBooksByPublisherName(String publisher$name); List<Book> getBooksByPublisherName(String publisher$name);
---- ----
The `@Pattern` annotation may be applied to a parameter of type `String`, indicating that the argument is a wildcarded pattern which will be compared using `like`.
[source,java]
----
@Find
List<Book> getBooksByTitle(@Pattern String title, Type type);
----
A finder method may specify <<fetch-profiles,fetch profiles>>, for example: A finder method may specify <<fetch-profiles,fetch profiles>>, for example:
[source,java] [source,java]

View File

@ -0,0 +1,42 @@
/*
* 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.annotations.processing;
import jakarta.persistence.criteria.Expression;
import org.hibernate.Incubating;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.CLASS;
/**
* Indicates that a parameter of type {@link String} of a
* {@linkplain Find finder method} is a pattern involving
* wildcard characters {@code _} and {@code %}.
* <p>
* For example:
* <pre>
* &#064;Find
* List&lt;Book&gt; getBooksWithTitle(@Pattern String title);
* </pre>
* <p>
* A parameter annotated {@code @Pattern} results in a
* {@link jakarta.persistence.criteria.CriteriaBuilder#like(Expression, String) like}
* condition in the generated code.
*
* @see Find
*
* @since 6.5
* @author Gavin King
*/
@Target(PARAMETER)
@Retention(CLASS)
@Incubating
public @interface Pattern {
}

View File

@ -1010,6 +1010,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
final String methodName = method.getSimpleName().toString(); final String methodName = method.getSimpleName().toString();
final List<String> paramNames = parameterNames( method, entity ); final List<String> paramNames = parameterNames( method, entity );
final List<String> paramTypes = parameterTypes( method ); final List<String> paramTypes = parameterTypes( method );
final List<Boolean> paramPatterns = parameterPatterns( method );
final String[] sessionType = sessionTypeFromParameters( paramNames, paramTypes ); final String[] sessionType = sessionTypeFromParameters( paramNames, paramTypes );
final String methodKey = methodName + paramTypes; final String methodKey = methodName + paramTypes;
final List<Boolean> multivalued = new ArrayList<>(); final List<Boolean> multivalued = new ArrayList<>();
@ -1044,6 +1045,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
paramTypes, paramTypes,
parameterNullability(method, entity), parameterNullability(method, entity),
multivalued, multivalued,
paramPatterns,
repository, repository,
sessionType[0], sessionType[0],
sessionType[1], sessionType[1],
@ -1243,6 +1245,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
); );
} }
else { else {
final List<Boolean> paramPatterns = parameterPatterns( method );
putMember( methodKey, putMember( methodKey,
new CriteriaFinderMethod( new CriteriaFinderMethod(
this, this,
@ -1253,6 +1256,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
paramTypes, paramTypes,
parameterNullability(method, entity), parameterNullability(method, entity),
multivalued, multivalued,
paramPatterns,
repository, repository,
sessionType[0], sessionType[0],
sessionType[1], sessionType[1],
@ -1316,6 +1320,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
break; break;
case BASIC: case BASIC:
case MULTIVALUED: case MULTIVALUED:
final List<Boolean> paramPatterns = parameterPatterns( method );
putMember( methodKey, putMember( methodKey,
new CriteriaFinderMethod( new CriteriaFinderMethod(
this, this,
@ -1329,6 +1334,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
.map(param -> isFinderParameterMappingToAttribute(param) .map(param -> isFinderParameterMappingToAttribute(param)
&& fieldType == FieldType.MULTIVALUED) && fieldType == FieldType.MULTIVALUED)
.collect(toList()), .collect(toList()),
paramPatterns,
repository, repository,
sessionType[0], sessionType[0],
sessionType[1], sessionType[1],
@ -1398,6 +1404,14 @@ public class AnnotationMetaEntity extends AnnotationMeta {
if ( checkParameterType( entityType, param, memberType( member ) ) ) { if ( checkParameterType( entityType, param, memberType( member ) ) ) {
return FieldType.MULTIVALUED; return FieldType.MULTIVALUED;
} }
else if ( containsAnnotation( param, PATTERN ) ) {
final AnnotationMirror mirror = getAnnotationMirror(param, PATTERN);
if ( mirror!=null && !param.asType().toString().equals( String.class.getName() ) ) {
context.message( param, mirror, "parameter annotated '@Pattern' is not of type 'String'",
Diagnostic.Kind.ERROR );
}
return FieldType.BASIC;
}
else if ( containsAnnotation( member, ID, EMBEDDED_ID ) ) { else if ( containsAnnotation( member, ID, EMBEDDED_ID ) ) {
return FieldType.ID; return FieldType.ID;
} }
@ -2042,6 +2056,12 @@ public class AnnotationMetaEntity extends AnnotationMeta {
.collect(toList()); .collect(toList());
} }
private static List<Boolean> parameterPatterns(ExecutableElement method) {
return method.getParameters().stream()
.map(param -> hasAnnotation(param, PATTERN))
.collect(toList());
}
private List<String> parameterNames(ExecutableElement method, TypeElement entity) { private List<String> parameterNames(ExecutableElement method, TypeElement entity) {
final String idName = final String idName =
// account for special @By("#id") hack in Jakarta Data // account for special @By("#id") hack in Jakarta Data

View File

@ -26,6 +26,7 @@ public class CriteriaFinderMethod extends AbstractFinderMethod {
private final @Nullable String containerType; private final @Nullable String containerType;
private final List<Boolean> paramNullability; private final List<Boolean> paramNullability;
private final List<Boolean> multivalued; private final List<Boolean> multivalued;
private final List<Boolean> paramPatterns;
CriteriaFinderMethod( CriteriaFinderMethod(
AnnotationMetaEntity annotationMetaEntity, AnnotationMetaEntity annotationMetaEntity,
@ -34,6 +35,7 @@ public class CriteriaFinderMethod extends AbstractFinderMethod {
List<String> paramNames, List<String> paramTypes, List<String> paramNames, List<String> paramTypes,
List<Boolean> paramNullability, List<Boolean> paramNullability,
List<Boolean> multivalued, List<Boolean> multivalued,
List<Boolean> paramPatterns,
boolean belongsToDao, boolean belongsToDao,
String sessionType, String sessionType,
String sessionName, String sessionName,
@ -46,6 +48,7 @@ public class CriteriaFinderMethod extends AbstractFinderMethod {
this.containerType = containerType; this.containerType = containerType;
this.paramNullability = paramNullability; this.paramNullability = paramNullability;
this.multivalued = multivalued; this.multivalued = multivalued;
this.paramPatterns = paramPatterns;
} }
@Override @Override
@ -221,7 +224,9 @@ public class CriteriaFinderMethod extends AbstractFinderMethod {
else { else {
//TODO: change to use Expression.equalTo() in JPA 3.2 //TODO: change to use Expression.equalTo() in JPA 3.2
declaration declaration
.append("_builder.equal(_entity"); .append("_builder.")
.append(paramPatterns.get(i) ? "like" : "equal")
.append("(_entity");
path( declaration, paramName ); path( declaration, paramName );
declaration declaration
.append(", ") .append(", ")

View File

@ -60,6 +60,7 @@ public final class Constants {
public static final String HQL = "org.hibernate.annotations.processing.HQL"; public static final String HQL = "org.hibernate.annotations.processing.HQL";
public static final String SQL = "org.hibernate.annotations.processing.SQL"; public static final String SQL = "org.hibernate.annotations.processing.SQL";
public static final String FIND = "org.hibernate.annotations.processing.Find"; public static final String FIND = "org.hibernate.annotations.processing.Find";
public static final String PATTERN = "org.hibernate.annotations.processing.Pattern";
public static final String JD_REPOSITORY = "jakarta.data.repository.Repository"; public static final String JD_REPOSITORY = "jakarta.data.repository.Repository";
public static final String JD_QUERY = "jakarta.data.repository.Query"; public static final String JD_QUERY = "jakarta.data.repository.Query";

View File

@ -4,6 +4,7 @@ import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery; import jakarta.persistence.TypedQuery;
import org.hibernate.annotations.processing.Find; import org.hibernate.annotations.processing.Find;
import org.hibernate.annotations.processing.HQL; import org.hibernate.annotations.processing.HQL;
import org.hibernate.annotations.processing.Pattern;
import org.hibernate.annotations.processing.SQL; import org.hibernate.annotations.processing.SQL;
import org.hibernate.query.Order; import org.hibernate.query.Order;
import org.hibernate.query.Page; import org.hibernate.query.Page;
@ -35,7 +36,7 @@ public interface Dao {
Book getBookFetching(String isbn); Book getBookFetching(String isbn);
@Find @Find
Book getBook(String title, String author); Book getBook(@Pattern String title, String author);
@Find(enabledFetchProfiles="Hello") @Find(enabledFetchProfiles="Hello")
Book getBookFetching(String title, String author); Book getBookFetching(String title, String author);