HHH-16633 introduce query methods to JPA metamodel generator
This commit is contained in:
parent
d3e15a7cc1
commit
698b245753
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.CLASS;
|
||||
|
||||
/**
|
||||
* Identifies a method of an abstract class or interface
|
||||
* as defining the signature of a method which is used to
|
||||
* execute the given {@linkplain #value HQL query}, and is
|
||||
* generated automatically by the Hibernate Metamodel
|
||||
* Generator.
|
||||
* <p>
|
||||
* For example:
|
||||
* <pre>
|
||||
* public interface Books {
|
||||
* @Hql("from Book where isbn = :isbn")
|
||||
* Book findBookByIsbn(String isbn);
|
||||
*
|
||||
* @Hql("from Book where title like ?1 order by title offset ?3 fetch first ?2 rows only")
|
||||
* List<Book> findBooksByTitleWithPagination(String title, int max, int start);
|
||||
*
|
||||
* @Hql("from Book where title like ?1")
|
||||
* TypedQuery<Book> findBooksByTitle(String title);
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* The Metamodel Generator automatically creates an
|
||||
* "implementation" of these methods in the static metamodel
|
||||
* class {@code Books_}.
|
||||
* <pre>
|
||||
* Book book = Books_.findBookByIsbn(session, isbn);
|
||||
* List<Book> books = Books_.findBooksByTitleWithPagination(session, pattern, 10, 0);
|
||||
* </pre>
|
||||
* <p>
|
||||
* The return type of an annotated method must be:
|
||||
* <ul>
|
||||
* <li>an entity type,
|
||||
* <li>{@link java.util.List},
|
||||
* <li>{@link org.hibernate.query.Query},
|
||||
* <li>{@link jakarta.persistence.Query}, or
|
||||
* <li>{@link jakarta.persistence.TypedQuery}.
|
||||
* </ul>
|
||||
* <p>
|
||||
* The method parameters must match the parameters of the
|
||||
* HQL query, either by name or by position.
|
||||
*
|
||||
* @author Gavin King
|
||||
* @since 6.3
|
||||
*/
|
||||
@Target(METHOD)
|
||||
@Retention(CLASS)
|
||||
@Incubating
|
||||
public @interface Hql {
|
||||
String value();
|
||||
}
|
|
@ -21,6 +21,8 @@ ext {
|
|||
dependencies {
|
||||
implementation jakartaLibs.jaxbApi
|
||||
implementation jakartaLibs.jaxb
|
||||
implementation libs.antlrRuntime
|
||||
implementation project( ':hibernate-core' )
|
||||
|
||||
xjc jakartaLibs.xjc
|
||||
xjc jakartaLibs.jaxb
|
||||
|
|
|
@ -116,7 +116,8 @@ public final class ClassWriter {
|
|||
List<MetaAttribute> members = entity.getMembers();
|
||||
for (MetaAttribute metaMember : members) {
|
||||
if (metaMember.hasTypedAttribute()) {
|
||||
pw.println(" " + metaMember.getAttributeDeclarationString());
|
||||
metaMember.getAttributeDeclarationString().lines()
|
||||
.forEach(line -> pw.println(" " + line));
|
||||
}
|
||||
}
|
||||
pw.println();
|
||||
|
|
|
@ -39,6 +39,9 @@ import org.hibernate.jpamodelgen.xml.JpaDescriptorParser;
|
|||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import static org.hibernate.jpamodelgen.util.Constants.QUERY_METHOD;
|
||||
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
|
||||
|
||||
/**
|
||||
* Main annotation processor.
|
||||
*
|
||||
|
@ -137,6 +140,16 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
|
|||
context.logMessage( Diagnostic.Kind.OTHER, "Processing annotated class " + element.toString() );
|
||||
handleRootElementAuxiliaryAnnotationMirrors( element );
|
||||
}
|
||||
else if ( element instanceof TypeElement ) {
|
||||
for ( Element enclosedElement : element.getEnclosedElements() ) {
|
||||
if ( containsAnnotation( enclosedElement, QUERY_METHOD ) ) {
|
||||
AnnotationMetaEntity metaEntity =
|
||||
AnnotationMetaEntity.create( (TypeElement) element, context, false );
|
||||
context.addMetaAuxiliary( metaEntity.getQualifiedName(), metaEntity );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createMetaModelClasses();
|
||||
|
@ -228,7 +241,7 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
|
|||
}
|
||||
|
||||
private boolean isJPAEntity(Element element) {
|
||||
return TypeUtils.containsAnnotation(
|
||||
return containsAnnotation(
|
||||
element,
|
||||
Constants.ENTITY,
|
||||
Constants.MAPPED_SUPERCLASS,
|
||||
|
@ -237,7 +250,7 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
|
|||
}
|
||||
|
||||
private boolean hasAuxiliaryAnnotations(Element element) {
|
||||
return TypeUtils.containsAnnotation(
|
||||
return containsAnnotation(
|
||||
element,
|
||||
Constants.NAMED_QUERY,
|
||||
Constants.NAMED_QUERIES,
|
||||
|
@ -275,8 +288,8 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
|
|||
|
||||
boolean requiresLazyMemberInitialization = false;
|
||||
AnnotationMetaEntity metaEntity;
|
||||
if ( TypeUtils.containsAnnotation( element, Constants.EMBEDDABLE ) ||
|
||||
TypeUtils.containsAnnotation( element, Constants.MAPPED_SUPERCLASS ) ) {
|
||||
if ( containsAnnotation( element, Constants.EMBEDDABLE ) ||
|
||||
containsAnnotation( element, Constants.MAPPED_SUPERCLASS ) ) {
|
||||
requiresLazyMemberInitialization = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.jpamodelgen.annotation;
|
||||
|
||||
import org.hibernate.jpamodelgen.model.MetaAttribute;
|
||||
import org.hibernate.jpamodelgen.model.Metamodel;
|
||||
import org.hibernate.jpamodelgen.util.Constants;
|
||||
import org.hibernate.jpamodelgen.util.TypeUtils;
|
||||
|
@ -66,5 +67,5 @@ public abstract class AnnotationMeta implements Metamodel {
|
|||
});
|
||||
}
|
||||
|
||||
abstract void putMember(String name, NameMetaAttribute nameMetaAttribute);
|
||||
abstract void putMember(String name, MetaAttribute nameMetaAttribute);
|
||||
}
|
||||
|
|
|
@ -7,19 +7,36 @@
|
|||
package org.hibernate.jpamodelgen.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.PackageElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.ExecutableType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.ElementFilter;
|
||||
import javax.tools.Diagnostic;
|
||||
|
||||
import org.antlr.v4.runtime.ANTLRErrorListener;
|
||||
import org.antlr.v4.runtime.BailErrorStrategy;
|
||||
import org.antlr.v4.runtime.DefaultErrorStrategy;
|
||||
import org.antlr.v4.runtime.Parser;
|
||||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.Recognizer;
|
||||
import org.antlr.v4.runtime.atn.ATNConfigSet;
|
||||
import org.antlr.v4.runtime.atn.PredictionMode;
|
||||
import org.antlr.v4.runtime.dfa.DFA;
|
||||
import org.antlr.v4.runtime.misc.ParseCancellationException;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.hibernate.grammars.hql.HqlLexer;
|
||||
import org.hibernate.grammars.hql.HqlParser;
|
||||
import org.hibernate.jpamodelgen.Context;
|
||||
import org.hibernate.jpamodelgen.ImportContextImpl;
|
||||
import org.hibernate.jpamodelgen.model.ImportContext;
|
||||
|
@ -30,6 +47,14 @@ import org.hibernate.jpamodelgen.util.AccessTypeInformation;
|
|||
import org.hibernate.jpamodelgen.util.Constants;
|
||||
import org.hibernate.jpamodelgen.util.NullnessUtil;
|
||||
import org.hibernate.jpamodelgen.util.TypeUtils;
|
||||
import org.hibernate.query.hql.internal.HqlParseTreeBuilder;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
|
||||
import static org.hibernate.jpamodelgen.util.TypeUtils.determineAnnotationSpecifiedAccessType;
|
||||
import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationMirror;
|
||||
import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationValue;
|
||||
import static org.hibernate.query.hql.internal.StandardHqlTranslator.prettifyAntlrError;
|
||||
|
||||
/**
|
||||
* Class used to collect meta information about an annotated type (entity, embeddable or mapped superclass).
|
||||
|
@ -37,6 +62,7 @@ import org.hibernate.jpamodelgen.util.TypeUtils;
|
|||
* @author Max Andersen
|
||||
* @author Hardy Ferentschik
|
||||
* @author Emmanuel Bernard
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class AnnotationMetaEntity extends AnnotationMeta {
|
||||
|
||||
|
@ -86,6 +112,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
return entityAccessTypeInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
@ -168,7 +195,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
}
|
||||
|
||||
@Override
|
||||
void putMember(String name, NameMetaAttribute nameMetaAttribute) {
|
||||
void putMember(String name, MetaAttribute nameMetaAttribute) {
|
||||
members.put( name, nameMetaAttribute );
|
||||
}
|
||||
|
||||
|
@ -194,20 +221,26 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
|
||||
List<? extends Element> methodsOfClass = ElementFilter.methodsIn( element.getEnclosedElements() );
|
||||
List<Element> gettersAndSettersOfClass = new ArrayList<>();
|
||||
List<ExecutableElement> queryMethods = new ArrayList<>();
|
||||
|
||||
for ( Element rawMethodOfClass: methodsOfClass ) {
|
||||
if ( isGetterOrSetter( rawMethodOfClass ) ) {
|
||||
gettersAndSettersOfClass.add( rawMethodOfClass );
|
||||
}
|
||||
else if ( rawMethodOfClass instanceof ExecutableElement
|
||||
&& containsAnnotation( rawMethodOfClass, Constants.QUERY_METHOD ) ) {
|
||||
queryMethods.add( (ExecutableElement) rawMethodOfClass );
|
||||
}
|
||||
}
|
||||
addPersistentMembers( gettersAndSettersOfClass, AccessType.PROPERTY );
|
||||
|
||||
addAuxiliaryMembers();
|
||||
|
||||
addQueryMethods( queryMethods );
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if method respects Java Bean conventions for getter and setters.
|
||||
*
|
||||
|
@ -221,31 +254,30 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
List<? extends TypeMirror> methodParameterTypes = methodType.getParameterTypes();
|
||||
TypeMirror returnType = methodType.getReturnType();
|
||||
|
||||
if(
|
||||
methodSimpleName.startsWith("set") &&
|
||||
methodParameterTypes.size() == 1 &&
|
||||
"void".equalsIgnoreCase( returnType.toString() ) ) {
|
||||
return true;
|
||||
}
|
||||
else if(
|
||||
( methodSimpleName.startsWith("get") || methodSimpleName.startsWith("is") ) &&
|
||||
methodParameterTypes.isEmpty() &&
|
||||
!"void".equalsIgnoreCase( returnType.toString() ) ) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
return isSetter(methodSimpleName, methodParameterTypes, returnType)
|
||||
|| isGetter(methodSimpleName, methodParameterTypes, returnType);
|
||||
}
|
||||
|
||||
private static boolean isGetter(String methodSimpleName, List<? extends TypeMirror> methodParameterTypes, TypeMirror returnType) {
|
||||
return (methodSimpleName.startsWith("get") || methodSimpleName.startsWith("is"))
|
||||
&& methodParameterTypes.isEmpty()
|
||||
&& !"void".equalsIgnoreCase(returnType.toString());
|
||||
}
|
||||
|
||||
private static boolean isSetter(String methodSimpleName, List<? extends TypeMirror> methodParameterTypes, TypeMirror returnType) {
|
||||
return methodSimpleName.startsWith("set")
|
||||
&& methodParameterTypes.size() == 1
|
||||
&& "void".equalsIgnoreCase(returnType.toString());
|
||||
}
|
||||
|
||||
private void addPersistentMembers(List<? extends Element> membersOfClass, AccessType membersKind) {
|
||||
for ( Element memberOfClass : membersOfClass ) {
|
||||
AccessType forcedAccessType = TypeUtils.determineAnnotationSpecifiedAccessType( memberOfClass );
|
||||
AccessType forcedAccessType = determineAnnotationSpecifiedAccessType( memberOfClass );
|
||||
if ( entityAccessTypeInfo.getAccessType() != membersKind && forcedAccessType == null ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( TypeUtils.containsAnnotation( memberOfClass, Constants.TRANSIENT )
|
||||
if ( containsAnnotation( memberOfClass, Constants.TRANSIENT )
|
||||
|| memberOfClass.getModifiers().contains( Modifier.TRANSIENT )
|
||||
|| memberOfClass.getModifiers().contains( Modifier.STATIC ) ) {
|
||||
continue;
|
||||
|
@ -259,4 +291,159 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
}
|
||||
}
|
||||
|
||||
private void addQueryMethods(List<ExecutableElement> queryMethods) {
|
||||
for ( ExecutableElement method : queryMethods) {
|
||||
addQueryMethod(method);
|
||||
}
|
||||
}
|
||||
|
||||
private void addQueryMethod(ExecutableElement method) {
|
||||
final String methodName = method.getSimpleName().toString();
|
||||
final TypeMirror returnType = method.getReturnType();
|
||||
if ( returnType instanceof DeclaredType ) {
|
||||
final DeclaredType declaredType = (DeclaredType) returnType;
|
||||
final List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
|
||||
if ( typeArguments.size() == 0 ) {
|
||||
if ( containsAnnotation( declaredType.asElement(), Constants.ENTITY ) ) {
|
||||
addQueryMethod(method, methodName, declaredType.toString(), null);
|
||||
}
|
||||
else {
|
||||
final String containerTypeName = declaredType.toString();
|
||||
if (isLegalRawResultType(containerTypeName)) {
|
||||
addQueryMethod(method, methodName, null, containerTypeName);
|
||||
}
|
||||
else {
|
||||
displayError(method, "incorrect return type '" + containerTypeName + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( typeArguments.size() == 1 ) {
|
||||
final String containerTypeName = declaredType.asElement().toString();
|
||||
final String returnTypeName = typeArguments.get(0).toString();
|
||||
if (isLegalGenericResultType(containerTypeName)) {
|
||||
addQueryMethod(method, methodName, returnTypeName, containerTypeName);
|
||||
}
|
||||
else {
|
||||
displayError(method, "incorrect return type '" + containerTypeName + "'");
|
||||
}
|
||||
}
|
||||
else {
|
||||
displayError(method, "incorrect return type '" + declaredType + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isLegalRawResultType(String containerTypeName) {
|
||||
return containerTypeName.equals("java.util.List")
|
||||
|| containerTypeName.equals("jakarta.persistence.Query")
|
||||
|| containerTypeName.equals("org.hibernate.query.Query");
|
||||
}
|
||||
|
||||
private static boolean isLegalGenericResultType(String containerTypeName) {
|
||||
return containerTypeName.equals("java.util.List")
|
||||
|| containerTypeName.equals("jakarta.persistence.TypedQuery")
|
||||
|| containerTypeName.equals("org.hibernate.query.Query");
|
||||
}
|
||||
|
||||
private void addQueryMethod(
|
||||
ExecutableElement method,
|
||||
String methodName,
|
||||
@Nullable String returnTypeName, @Nullable String containerTypeName) {
|
||||
final AnnotationMirror mirror = getAnnotationMirror(method, Constants.QUERY_METHOD );
|
||||
if ( mirror != null ) {
|
||||
final Object queryString = getAnnotationValue( mirror, "value" );
|
||||
if ( queryString instanceof String ) {
|
||||
final List<String> paramNames =
|
||||
method.getParameters().stream()
|
||||
.map(param -> param.getSimpleName().toString())
|
||||
.collect(toList());
|
||||
final List<String> paramTypes =
|
||||
method.getParameters().stream()
|
||||
.map(param -> param.asType().toString())
|
||||
.collect(toList());
|
||||
final String hql = (String) queryString;
|
||||
final QueryMethod attribute =
|
||||
new QueryMethod(
|
||||
this,
|
||||
methodName,
|
||||
hql,
|
||||
returnTypeName,
|
||||
containerTypeName,
|
||||
paramNames,
|
||||
paramTypes
|
||||
);
|
||||
putMember( attribute.getPropertyName(), attribute );
|
||||
|
||||
checkParameters(method, paramNames, mirror, hql);
|
||||
checkHqlSyntax(method, mirror, hql);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkParameters(ExecutableElement method, List<String> paramNames, AnnotationMirror mirror, String hql) {
|
||||
for (int i = 1; i <= paramNames.size(); i++) {
|
||||
final String param = paramNames.get(i-1);
|
||||
if ( !hql.contains(":" + param) && !hql.contains("?" + i) ) {
|
||||
displayError(method, mirror, "missing query parameter for '" + param
|
||||
+ "' (no parameter named :" + param + " or ?" + i + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkHqlSyntax(ExecutableElement method, AnnotationMirror mirror, String queryString) {
|
||||
ANTLRErrorListener errorListener = new ANTLRErrorListener() {
|
||||
@Override
|
||||
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String message, RecognitionException e) {
|
||||
displayError(method, mirror, "illegal HQL syntax - "
|
||||
+ prettifyAntlrError( offendingSymbol, line, charPositionInLine, message, e, queryString, false ));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportAmbiguity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, boolean exact, BitSet ambigAlts, ATNConfigSet configs) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportAttemptingFullContext(Parser recognizer, DFA dfa, int startIndex, int stopIndex, BitSet conflictingAlts, ATNConfigSet configs) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportContextSensitivity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, int prediction, ATNConfigSet configs) {
|
||||
}
|
||||
};
|
||||
|
||||
final HqlLexer hqlLexer = HqlParseTreeBuilder.INSTANCE.buildHqlLexer( queryString );
|
||||
final HqlParser hqlParser = HqlParseTreeBuilder.INSTANCE.buildHqlParser( queryString, hqlLexer );
|
||||
hqlLexer.addErrorListener( errorListener );
|
||||
hqlParser.getInterpreter().setPredictionMode( PredictionMode.SLL );
|
||||
hqlParser.removeErrorListeners();
|
||||
hqlParser.addErrorListener( errorListener );
|
||||
hqlParser.setErrorHandler( new BailErrorStrategy() );
|
||||
|
||||
try {
|
||||
hqlParser.statement();
|
||||
}
|
||||
catch ( ParseCancellationException e) {
|
||||
// reset the input token stream and parser state
|
||||
hqlLexer.reset();
|
||||
hqlParser.reset();
|
||||
|
||||
// fall back to LL(k)-based parsing
|
||||
hqlParser.getInterpreter().setPredictionMode( PredictionMode.LL );
|
||||
hqlParser.setErrorHandler( new DefaultErrorStrategy() );
|
||||
|
||||
hqlParser.statement();
|
||||
}
|
||||
}
|
||||
|
||||
private void displayError(ExecutableElement method, String message) {
|
||||
context.getProcessingEnvironment().getMessager()
|
||||
.printMessage( Diagnostic.Kind.ERROR, message, method );
|
||||
}
|
||||
private void displayError(ExecutableElement method, AnnotationMirror mirror, String message) {
|
||||
context.getProcessingEnvironment().getMessager()
|
||||
.printMessage( Diagnostic.Kind.ERROR, message, method, mirror,
|
||||
mirror.getElementValues().entrySet().stream()
|
||||
.filter( entry -> entry.getKey().getSimpleName().toString().equals("value") )
|
||||
.map(Map.Entry::getValue).findAny().orElseThrow() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ public class AnnotationMetaPackage extends AnnotationMeta {
|
|||
return new AnnotationMetaPackage( element, context );
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
@ -121,7 +122,7 @@ public class AnnotationMetaPackage extends AnnotationMeta {
|
|||
}
|
||||
|
||||
@Override
|
||||
void putMember(String name, NameMetaAttribute nameMetaAttribute) {
|
||||
void putMember(String name, MetaAttribute nameMetaAttribute) {
|
||||
members.put( name, nameMetaAttribute );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.hibernate.jpamodelgen.model.MetaAttribute;
|
||||
import org.hibernate.jpamodelgen.model.Metamodel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.hibernate.jpamodelgen.util.StringUtil.getUpperUnderscoreCaseFromLowerCamelCase;
|
||||
|
||||
/**
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class QueryMethod implements MetaAttribute {
|
||||
private final Metamodel annotationMetaEntity;
|
||||
private final String methodName;
|
||||
private final String queryString;
|
||||
private final @Nullable String returnTypeName;
|
||||
private final @Nullable String containerTypeName;
|
||||
private final List<String> paramNames;
|
||||
private final List<String> paramTypes;
|
||||
|
||||
public QueryMethod(
|
||||
Metamodel annotationMetaEntity,
|
||||
String methodName,
|
||||
String queryString,
|
||||
@Nullable
|
||||
String returnTypeName,
|
||||
@Nullable
|
||||
String containerTypeName,
|
||||
List<String> paramNames,
|
||||
List<String> paramTypes
|
||||
) {
|
||||
this.annotationMetaEntity = annotationMetaEntity;
|
||||
this.methodName = methodName;
|
||||
this.queryString = queryString;
|
||||
this.returnTypeName = returnTypeName;
|
||||
this.containerTypeName = containerTypeName;
|
||||
this.paramNames = paramNames;
|
||||
this.paramTypes = paramTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTypedAttribute() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttributeDeclarationString() {
|
||||
StringBuilder declaration = new StringBuilder();
|
||||
declaration.append("public static ");
|
||||
if (containerTypeName != null) {
|
||||
declaration
|
||||
.append(annotationMetaEntity.importType(containerTypeName));
|
||||
if (returnTypeName != null) {
|
||||
declaration
|
||||
.append("<")
|
||||
.append(annotationMetaEntity.importType(returnTypeName))
|
||||
.append(">");
|
||||
}
|
||||
}
|
||||
else if (returnTypeName != null) {
|
||||
declaration.append(annotationMetaEntity.importType(returnTypeName));
|
||||
}
|
||||
declaration
|
||||
.append(" ")
|
||||
.append(methodName)
|
||||
.append("(")
|
||||
.append(annotationMetaEntity.importType("jakarta.persistence.EntityManager"))
|
||||
.append(" entityManager");
|
||||
|
||||
for (int i =0; i<paramNames.size(); i++) {
|
||||
declaration
|
||||
.append(", ")
|
||||
.append(annotationMetaEntity.importType(paramTypes.get(i)))
|
||||
.append(" ")
|
||||
.append(paramNames.get(i));
|
||||
}
|
||||
declaration
|
||||
.append(")")
|
||||
.append(" {")
|
||||
.append("\n return entityManager.createQuery(")
|
||||
.append(getUpperUnderscoreCaseFromLowerCamelCase(methodName));
|
||||
if (returnTypeName != null) {
|
||||
declaration
|
||||
.append(", ")
|
||||
.append(annotationMetaEntity.importType(returnTypeName))
|
||||
.append(".class");
|
||||
}
|
||||
declaration.append(")");
|
||||
for (int i = 1; i <= paramNames.size(); i++) {
|
||||
String param = paramNames.get(i-1);
|
||||
if (queryString.contains(":" + param)) {
|
||||
declaration
|
||||
.append("\n .setParameter(\"")
|
||||
.append(param)
|
||||
.append("\", ")
|
||||
.append(param)
|
||||
.append(")");
|
||||
}
|
||||
else if (queryString.contains("?" + i)) {
|
||||
declaration
|
||||
.append("\n .setParameter(")
|
||||
.append(i)
|
||||
.append(", ")
|
||||
.append(param)
|
||||
.append(")");
|
||||
}
|
||||
}
|
||||
if ( containerTypeName == null) {
|
||||
declaration.append("\n .getSingleResult()");
|
||||
}
|
||||
else if ( containerTypeName.equals("java.util.List") ) {
|
||||
declaration.append("\n .getResultList()");
|
||||
}
|
||||
declaration.append(";\n}");
|
||||
return declaration.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttributeNameDeclarationString() {
|
||||
return new StringBuilder().append("public static final String ")
|
||||
.append(getUpperUnderscoreCaseFromLowerCamelCase(methodName))
|
||||
.append(" = \"")
|
||||
.append(queryString)
|
||||
.append("\";")
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMetaType() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPropertyName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeDeclaration() {
|
||||
return "jakarta.persistence.Query";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Metamodel getHostingEntity() {
|
||||
return annotationMetaEntity;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.jpamodelgen.model;
|
||||
|
||||
import org.hibernate.jpamodelgen.Context;
|
||||
|
||||
import java.util.List;
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
|
@ -30,4 +32,6 @@ public interface Metamodel extends ImportContext {
|
|||
Element getElement();
|
||||
|
||||
boolean isMetaComplete();
|
||||
|
||||
Context getContext();
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ public final class Constants {
|
|||
public static final String HIB_FILTER_DEF = "org.hibernate.annotations.FilterDef";
|
||||
public static final String HIB_FILTER_DEFS = "org.hibernate.annotations.FilterDefs";
|
||||
|
||||
public static final String QUERY_METHOD = "org.hibernate.annotations.Hql";
|
||||
|
||||
public static final Map<String, String> COLLECTIONS = allCollectionTypes();
|
||||
|
||||
private static Map<String, String> allCollectionTypes() {
|
||||
|
|
|
@ -627,4 +627,9 @@ public class XmlMetaEntity implements Metamodel {
|
|||
return ElementKind.METHOD;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,10 @@ import static org.hibernate.jpamodelgen.test.util.TestUtil.assertPresenceOfNameF
|
|||
*/
|
||||
public class AuxiliaryTest extends CompilationTest {
|
||||
@Test
|
||||
@WithClasses({ Book.class, Main.class })
|
||||
@WithClasses({ Book.class, Main.class, Dao.class })
|
||||
public void testGeneratedAnnotationNotGenerated() {
|
||||
System.out.println( TestUtil.getMetaModelSourceAsString( Main.class ) );
|
||||
System.out.println( TestUtil.getMetaModelSourceAsString( Dao.class ) );
|
||||
assertMetamodelClassGeneratedFor( Book.class );
|
||||
assertMetamodelClassGeneratedFor( Main.class );
|
||||
assertPresenceOfNameFieldInMetamodelFor(
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package org.hibernate.jpamodelgen.test.namedquery;
|
||||
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import org.hibernate.annotations.Hql;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface Dao {
|
||||
@Hql("from Book where title like ?1")
|
||||
TypedQuery<Book> findByTitle(String title);
|
||||
|
||||
@Hql("from Book where title like ?1 order by title fetch first ?2 rows only")
|
||||
List<Book> findFirstNByTitle(String title, int N);
|
||||
|
||||
@Hql("from Book where isbn = :isbn")
|
||||
Book findByIsbn(String isbn);
|
||||
}
|
Loading…
Reference in New Issue