HHH-16633 introduce native query methods to JPA metamodel generator
This commit is contained in:
parent
698b245753
commit
cfe545ec3d
|
@ -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 SQL query}, and is
|
||||||
|
* generated automatically by the Hibernate Metamodel
|
||||||
|
* Generator.
|
||||||
|
* <p>
|
||||||
|
* For example:
|
||||||
|
* <pre>
|
||||||
|
* public interface Books {
|
||||||
|
* @Sql("select * from Book where isbn = :isbn")
|
||||||
|
* Book findBookByIsbn(String isbn);
|
||||||
|
*
|
||||||
|
* @Sql("select * 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);
|
||||||
|
*
|
||||||
|
* @Sql("select * from Book where title like ?1")
|
||||||
|
* Query 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 org.hibernate.query.NativeQuery}.
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* The method parameters must match the parameters of the
|
||||||
|
* SQL query, either by name or by position.
|
||||||
|
*
|
||||||
|
* @author Gavin King
|
||||||
|
* @since 6.3
|
||||||
|
*/
|
||||||
|
@Target(METHOD)
|
||||||
|
@Retention(CLASS)
|
||||||
|
@Incubating
|
||||||
|
public @interface Sql {
|
||||||
|
String value();
|
||||||
|
}
|
|
@ -39,7 +39,8 @@ import org.hibernate.jpamodelgen.xml.JpaDescriptorParser;
|
||||||
|
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import static org.hibernate.jpamodelgen.util.Constants.QUERY_METHOD;
|
import static org.hibernate.jpamodelgen.util.Constants.HQL;
|
||||||
|
import static org.hibernate.jpamodelgen.util.Constants.SQL;
|
||||||
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
|
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -142,7 +143,7 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
|
||||||
}
|
}
|
||||||
else if ( element instanceof TypeElement ) {
|
else if ( element instanceof TypeElement ) {
|
||||||
for ( Element enclosedElement : element.getEnclosedElements() ) {
|
for ( Element enclosedElement : element.getEnclosedElements() ) {
|
||||||
if ( containsAnnotation( enclosedElement, QUERY_METHOD ) ) {
|
if ( containsAnnotation( enclosedElement, HQL, SQL ) ) {
|
||||||
AnnotationMetaEntity metaEntity =
|
AnnotationMetaEntity metaEntity =
|
||||||
AnnotationMetaEntity.create( (TypeElement) element, context, false );
|
AnnotationMetaEntity.create( (TypeElement) element, context, false );
|
||||||
context.addMetaAuxiliary( metaEntity.getQualifiedName(), metaEntity );
|
context.addMetaAuxiliary( metaEntity.getQualifiedName(), metaEntity );
|
||||||
|
|
|
@ -228,7 +228,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
gettersAndSettersOfClass.add( rawMethodOfClass );
|
gettersAndSettersOfClass.add( rawMethodOfClass );
|
||||||
}
|
}
|
||||||
else if ( rawMethodOfClass instanceof ExecutableElement
|
else if ( rawMethodOfClass instanceof ExecutableElement
|
||||||
&& containsAnnotation( rawMethodOfClass, Constants.QUERY_METHOD ) ) {
|
&& containsAnnotation( rawMethodOfClass, Constants.HQL, Constants.SQL ) ) {
|
||||||
queryMethods.add( (ExecutableElement) rawMethodOfClass );
|
queryMethods.add( (ExecutableElement) rawMethodOfClass );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -348,33 +348,53 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
private void addQueryMethod(
|
private void addQueryMethod(
|
||||||
ExecutableElement method,
|
ExecutableElement method,
|
||||||
String methodName,
|
String methodName,
|
||||||
@Nullable String returnTypeName, @Nullable String containerTypeName) {
|
@Nullable String returnTypeName,
|
||||||
final AnnotationMirror mirror = getAnnotationMirror(method, Constants.QUERY_METHOD );
|
@Nullable String containerTypeName) {
|
||||||
if ( mirror != null ) {
|
final AnnotationMirror hql = getAnnotationMirror(method, Constants.HQL);
|
||||||
final Object queryString = getAnnotationValue( mirror, "value" );
|
if ( hql != null ) {
|
||||||
if ( queryString instanceof String ) {
|
addQueryMethod(method, methodName, returnTypeName, containerTypeName, hql, false);
|
||||||
final List<String> paramNames =
|
}
|
||||||
method.getParameters().stream()
|
final AnnotationMirror sql = getAnnotationMirror(method, Constants.SQL);
|
||||||
.map(param -> param.getSimpleName().toString())
|
if ( sql != null ) {
|
||||||
.collect(toList());
|
addQueryMethod(method, methodName, returnTypeName, containerTypeName, sql, true);
|
||||||
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);
|
private void addQueryMethod(
|
||||||
|
ExecutableElement method,
|
||||||
|
String methodName,
|
||||||
|
@Nullable
|
||||||
|
String returnTypeName,
|
||||||
|
@Nullable
|
||||||
|
String containerTypeName,
|
||||||
|
AnnotationMirror mirror,
|
||||||
|
boolean isNative) {
|
||||||
|
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,
|
||||||
|
isNative
|
||||||
|
);
|
||||||
|
putMember( attribute.getPropertyName(), attribute );
|
||||||
|
|
||||||
|
checkParameters(method, paramNames, mirror, hql);
|
||||||
|
if (!isNative) {
|
||||||
checkHqlSyntax(method, mirror, hql);
|
checkHqlSyntax(method, mirror, hql);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ public class QueryMethod implements MetaAttribute {
|
||||||
private final @Nullable String containerTypeName;
|
private final @Nullable String containerTypeName;
|
||||||
private final List<String> paramNames;
|
private final List<String> paramNames;
|
||||||
private final List<String> paramTypes;
|
private final List<String> paramTypes;
|
||||||
|
private final boolean isNative;
|
||||||
|
|
||||||
public QueryMethod(
|
public QueryMethod(
|
||||||
Metamodel annotationMetaEntity,
|
Metamodel annotationMetaEntity,
|
||||||
|
@ -35,8 +36,8 @@ public class QueryMethod implements MetaAttribute {
|
||||||
@Nullable
|
@Nullable
|
||||||
String containerTypeName,
|
String containerTypeName,
|
||||||
List<String> paramNames,
|
List<String> paramNames,
|
||||||
List<String> paramTypes
|
List<String> paramTypes,
|
||||||
) {
|
boolean isNative) {
|
||||||
this.annotationMetaEntity = annotationMetaEntity;
|
this.annotationMetaEntity = annotationMetaEntity;
|
||||||
this.methodName = methodName;
|
this.methodName = methodName;
|
||||||
this.queryString = queryString;
|
this.queryString = queryString;
|
||||||
|
@ -44,6 +45,7 @@ public class QueryMethod implements MetaAttribute {
|
||||||
this.containerTypeName = containerTypeName;
|
this.containerTypeName = containerTypeName;
|
||||||
this.paramNames = paramNames;
|
this.paramNames = paramNames;
|
||||||
this.paramTypes = paramTypes;
|
this.paramTypes = paramTypes;
|
||||||
|
this.isNative = isNative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -55,20 +57,18 @@ public class QueryMethod implements MetaAttribute {
|
||||||
public String getAttributeDeclarationString() {
|
public String getAttributeDeclarationString() {
|
||||||
StringBuilder declaration = new StringBuilder();
|
StringBuilder declaration = new StringBuilder();
|
||||||
declaration.append("public static ");
|
declaration.append("public static ");
|
||||||
|
StringBuilder type = new StringBuilder();
|
||||||
if (containerTypeName != null) {
|
if (containerTypeName != null) {
|
||||||
declaration
|
type.append(annotationMetaEntity.importType(containerTypeName));
|
||||||
.append(annotationMetaEntity.importType(containerTypeName));
|
|
||||||
if (returnTypeName != null) {
|
if (returnTypeName != null) {
|
||||||
declaration
|
type.append("<").append(annotationMetaEntity.importType(returnTypeName)).append(">");
|
||||||
.append("<")
|
|
||||||
.append(annotationMetaEntity.importType(returnTypeName))
|
|
||||||
.append(">");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (returnTypeName != null) {
|
else if (returnTypeName != null) {
|
||||||
declaration.append(annotationMetaEntity.importType(returnTypeName));
|
type.append(annotationMetaEntity.importType(returnTypeName));
|
||||||
}
|
}
|
||||||
declaration
|
declaration
|
||||||
|
.append(type)
|
||||||
.append(" ")
|
.append(" ")
|
||||||
.append(methodName)
|
.append(methodName)
|
||||||
.append("(")
|
.append("(")
|
||||||
|
@ -85,7 +85,14 @@ public class QueryMethod implements MetaAttribute {
|
||||||
declaration
|
declaration
|
||||||
.append(")")
|
.append(")")
|
||||||
.append(" {")
|
.append(" {")
|
||||||
.append("\n return entityManager.createQuery(")
|
.append("\n return ");
|
||||||
|
if ( isNative && returnTypeName != null ) {
|
||||||
|
declaration.append("(").append(type).append(") ");
|
||||||
|
}
|
||||||
|
declaration
|
||||||
|
.append("entityManager.")
|
||||||
|
.append(isNative ? "createNativeQuery" :"createQuery")
|
||||||
|
.append("(")
|
||||||
.append(getUpperUnderscoreCaseFromLowerCamelCase(methodName));
|
.append(getUpperUnderscoreCaseFromLowerCamelCase(methodName));
|
||||||
if (returnTypeName != null) {
|
if (returnTypeName != null) {
|
||||||
declaration
|
declaration
|
||||||
|
|
|
@ -50,7 +50,8 @@ public final class Constants {
|
||||||
public static final String HIB_FILTER_DEF = "org.hibernate.annotations.FilterDef";
|
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 HIB_FILTER_DEFS = "org.hibernate.annotations.FilterDefs";
|
||||||
|
|
||||||
public static final String QUERY_METHOD = "org.hibernate.annotations.Hql";
|
public static final String HQL = "org.hibernate.annotations.Hql";
|
||||||
|
public static final String SQL = "org.hibernate.annotations.Sql";
|
||||||
|
|
||||||
public static final Map<String, String> COLLECTIONS = allCollectionTypes();
|
public static final Map<String, String> COLLECTIONS = allCollectionTypes();
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.hibernate.jpamodelgen.test.namedquery;
|
||||||
|
|
||||||
import jakarta.persistence.TypedQuery;
|
import jakarta.persistence.TypedQuery;
|
||||||
import org.hibernate.annotations.Hql;
|
import org.hibernate.annotations.Hql;
|
||||||
|
import org.hibernate.annotations.Sql;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -14,4 +15,7 @@ public interface Dao {
|
||||||
|
|
||||||
@Hql("from Book where isbn = :isbn")
|
@Hql("from Book where isbn = :isbn")
|
||||||
Book findByIsbn(String isbn);
|
Book findByIsbn(String isbn);
|
||||||
|
|
||||||
|
@Sql("select * from Book where isbn = :isbn")
|
||||||
|
Book findByIsbnNative(String isbn);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue