HHH-16633 add ability to generate @Find methods for @NaturalIds and arbitrary field lists

This commit is contained in:
Gavin King 2023-07-07 19:05:12 +02:00
parent 0c40711563
commit d7fd5bd78a
12 changed files with 695 additions and 182 deletions

View File

@ -16,8 +16,44 @@ import static java.lang.annotation.RetentionPolicy.CLASS;
/**
* Identifies a method of an abstract class or interface as defining
* the signature of a finder method, and is generated automatically by
* the Hibernate Metamodel Generator.
* the signature of a finder method, and being generated automatically
* by the Hibernate Metamodel Generator.
* <p>
* For example:
* <pre>
* &#064;Find
* Book getBookForIsbn(String isbn);
*
* &#064;Find
* List&lt;Book&gt; getBooksWithTitle(String title);
* </pre>
* <p>
* The return type of an annotated method must be an entity type {@code E},
* or one of the following types:
* <ul>
* <li>{@link java.util.List java.util.List&lt;E&gt;},
* <li>{@link org.hibernate.query.Query org.hibernate.query.Query&lt;E&gt;},
* <li>{@link org.hibernate.query.SelectionQuery org.hibernate.query.SelectionQuery&lt;E&gt;},
* <li>{@link jakarta.persistence.Query jakarta.persistence.Query&lt;E&gt;}, or
* <li>{@link jakarta.persistence.TypedQuery jakarta.persistence.TypedQuery&lt;E&gt;}.
* </ul>
* <p>
* The names and types of the parameters of a finder method must match
* exactly with the names and types of persistent fields of the entity
* type returned by the finder method.
* <ul>
* <li>If there is one parameter, and it matches the {@code @Id} or
* {@code @EmbeddedId} field of the entity, the finder method uses
* {@link jakarta.persistence.EntityManager#find(Class, Object)}
* to retrieve the entity.
* <li>If the parameters match exactly with the {@code @NaturalId}
* fieldd of the entity, the finder method uses
* {@link org.hibernate.Session#byNaturalId(Class)} to retrieve the
* entity.
* <li>Otherwise, the finder method builds and executes a
* {@linkplain jakarta.persistence.criteria.CriteriaBuilder criteria
* query}.
* </ul>
*
* @author Gavin King
* @since 6.3

View File

@ -30,7 +30,7 @@ import static java.lang.annotation.RetentionPolicy.CLASS;
* List&lt;Book&gt; findBooksByTitleWithPagination(String title, int max, int start);
*
* &#64;HQL("from Book where title like ?1")
* TypedQuery&lt;Book&gt; findBooksByTitle(String title);
* TypedQuery&lt;Book&gt; createBooksByTitleQuery(String title);
* }
* </pre>
* <p>

View File

@ -272,11 +272,9 @@ public class AnnotationMetaEntity extends AnnotationMeta {
final Element element = ((DeclaredType) type).asElement();
if ( element.getKind() == ElementKind.INTERFACE ) {
final Name name = ((TypeElement) element).getQualifiedName();
if ( name.contentEquals( Session.class.getName() )
|| name.contentEquals( EntityManager.class.getName() )
|| name.contentEquals( StatelessSession.class.getName() ) ) {
return true;
}
return name.contentEquals(Session.class.getName())
|| name.contentEquals(EntityManager.class.getName())
|| name.contentEquals(StatelessSession.class.getName());
}
}
return false;
@ -414,12 +412,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
ExecutableElement method,
@Nullable TypeMirror returnType,
@Nullable TypeElement containerType) {
if ( containerType != null ) {
context.message( method,
"incorrect return type '" + containerType.getQualifiedName() + "' is not an entity type",
Diagnostic.Kind.ERROR );
}
else if ( returnType == null || returnType.getKind() != TypeKind.DECLARED ) {
if ( returnType == null || returnType.getKind() != TypeKind.DECLARED ) {
context.message( method,
"incorrect return type '" + returnType + "' is not an entity type",
Diagnostic.Kind.ERROR );
@ -433,62 +426,184 @@ public class AnnotationMetaEntity extends AnnotationMeta {
Diagnostic.Kind.ERROR );
}
else {
switch ( method.getParameters().size() ) {
case 0:
context.message( method,
"missing parameter",
Diagnostic.Kind.ERROR );
break;
case 1:
final VariableElement parameter = method.getParameters().get(0);
validateFinderParameter( entity, parameter);
final String methodName = method.getSimpleName().toString();
putMember( methodName,
new FinderMethod(
this,
methodName,
returnType.toString(),
parameter.getSimpleName().toString(),
parameter.asType().toString(),
dao
)
);
break;
default:
context.message( method,
"too many parameters ('@IdClass' not yet supported)",
Diagnostic.Kind.ERROR );
if ( containerType != null ) {
// it has to be a criteria finder (multiple results)
createContainerResultFinder( method, returnType, containerType, entity );
}
else {
switch ( method.getParameters().size() ) {
case 0: {
context.message( method,
"missing parameter",
Diagnostic.Kind.ERROR );
break;
}
case 1: {
createSingleParameterFinder( method, returnType, entity );
break;
}
default: {
createMultipleParameterFinder( method, returnType, entity );
}
}
}
}
}
}
private void validateFinderParameter(TypeElement entity, VariableElement param) {
entity.getEnclosedElements().stream()
.filter(member -> member.getSimpleName().contentEquals( param.getSimpleName() )
&& member.getAnnotationMirrors().stream()
.anyMatch(annotation -> {
final TypeElement annotationType = (TypeElement)
annotation.getAnnotationType().asElement();
final Name annotatioName = annotationType.getQualifiedName();
return annotatioName.contentEquals(Constants.ID)
|| annotatioName.contentEquals(Constants.EMBEDDED_ID);
}))
.findAny()
.ifPresentOrElse(
member -> {
final String memberType = member.asType().toString();
final String paramType = param.asType().toString();
if ( !memberType.equals(paramType)) {
context.message( param,
"matching '@Id' field in entity class has type '" + memberType + "'",
Diagnostic.Kind.ERROR );
}
},
() -> context.message( param,
"no matching '@Id' field in entity class",
Diagnostic.Kind.ERROR )
);
private void createContainerResultFinder(ExecutableElement method, TypeMirror returnType, TypeElement containerType, TypeElement entity) {
final String methodName = method.getSimpleName().toString();
final List<String> paramNames = parameterNames(method);
final List<String> paramTypes = parameterTypes(method);
final String methodKey = methodName + paramTypes;
for ( VariableElement param : method.getParameters() ) {
validateFinderParameter(entity, param );
}
putMember( methodKey,
new CriteriaFinderMethod(
this,
methodName,
returnType.toString(),
containerType.toString(),
paramNames,
paramTypes,
dao
)
);
}
private void createMultipleParameterFinder(ExecutableElement method, TypeMirror returnType, TypeElement entity) {
final String methodName = method.getSimpleName().toString();
final List<String> paramNames = parameterNames(method);
final List<String> paramTypes = parameterTypes(method);
final String methodKey = methodName + paramTypes;
if ( matchesNaturalKey(method, entity) ) {
putMember( methodKey,
new NaturalIdFinderMethod(
this,
methodName,
returnType.toString(),
paramNames,
paramTypes,
dao
)
);
}
else {
putMember( methodKey,
new CriteriaFinderMethod(
this,
methodName,
returnType.toString(),
null,
paramNames,
paramTypes,
dao
)
);
}
}
private void createSingleParameterFinder(ExecutableElement method, TypeMirror returnType, TypeElement entity) {
final String methodName = method.getSimpleName().toString();
final VariableElement parameter = method.getParameters().get(0);
final FieldType fieldType = validateFinderParameter(entity, parameter );
if ( fieldType != null ) {
final String methodKey = methodName + "!";
switch ( fieldType ) {
case NATURAL_ID:
putMember( methodKey,
new NaturalIdFinderMethod(
this,
methodName,
returnType.toString(),
List.of( parameter.getSimpleName().toString() ),
List.of( parameter.asType().toString() ),
dao
)
);
break;
case ID:
putMember( methodKey,
new IdFinderMethod(
this,
methodName,
returnType.toString(),
parameter.getSimpleName().toString(),
parameter.asType().toString(),
dao
)
);
break;
case BASIC:
putMember( methodKey,
new CriteriaFinderMethod(
this,
methodName,
returnType.toString(),
null,
List.of( parameter.getSimpleName().toString() ),
List.of( parameter.asType().toString() ),
dao
)
);
break;
}
}
}
private boolean matchesNaturalKey(ExecutableElement method, TypeElement entity) {
boolean result = true;
List<? extends VariableElement> parameters = method.getParameters();
for ( VariableElement param : parameters) {
if ( validateFinderParameter( entity, param ) != FieldType.NATURAL_ID ) {
// no short-circuit here because we want to validate
// all of them and get the nice error report
result = false;
}
}
return result && countNaturalIdFields( entity ) == parameters.size() ;
}
enum FieldType {
ID, NATURAL_ID, BASIC
}
private int countNaturalIdFields(TypeElement entity) {
int count = 0;
for ( Element member : entity.getEnclosedElements() ) {
if ( containsAnnotation( member, Constants.NATURAL_ID ) ) {
count ++;
}
}
return count;
}
private @Nullable FieldType validateFinderParameter(TypeElement entity, VariableElement param) {
for ( Element member : entity.getEnclosedElements() ) {
if ( member.getSimpleName().contentEquals( param.getSimpleName() ) ) {
final String memberType = member.asType().toString();
final String paramType = param.asType().toString();
if ( !memberType.equals(paramType) ) {
context.message( param,
"matching field in entity class has type '" + memberType + "'",
Diagnostic.Kind.ERROR );
}
if ( containsAnnotation( member, Constants.ID, Constants.EMBEDDED_ID ) ) {
return FieldType.ID;
}
else if ( containsAnnotation( member, Constants.NATURAL_ID ) ) {
return FieldType.NATURAL_ID;
}
else {
return FieldType.BASIC;
}
}
}
context.message( param,
"no matching field named '" + param.getSimpleName() + "' in entity class",
Diagnostic.Kind.ERROR );
return null;
}
private void addQueryMethod(

View File

@ -0,0 +1,182 @@
/*
* 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.internal.util.StringHelper.join;
/**
* @author Gavin King
*/
public class CriteriaFinderMethod implements MetaAttribute {
private final Metamodel annotationMetaEntity;
private final String methodName;
private final String entity;
private final @Nullable String containerType;
private final List<String> paramNames;
private final List<String> paramTypes;
private final boolean belongsToDao;
public CriteriaFinderMethod(
Metamodel annotationMetaEntity,
String methodName, String entity,
@Nullable String containerType,
List<String> paramNames, List<String> paramTypes,
boolean belongsToDao) {
this.annotationMetaEntity = annotationMetaEntity;
this.methodName = methodName;
this.entity = entity;
this.containerType = containerType;
this.paramNames = paramNames;
this.paramTypes = paramTypes;
this.belongsToDao = belongsToDao;
}
@Override
public boolean hasTypedAttribute() {
return true;
}
@Override
public boolean hasStringAttribute() {
return false;
}
@Override
public String getAttributeDeclarationString() {
StringBuilder declaration = new StringBuilder();
declaration
.append("\n/**\n * @see ")
.append(annotationMetaEntity.getQualifiedName())
.append("#")
.append(methodName)
.append("(")
.append(join(",", paramTypes.stream().map(this::strip).map(annotationMetaEntity::importType).toArray()))
.append(")")
.append("\n **/\n");
if ( belongsToDao ) {
declaration
.append("@Override\npublic ");
}
else {
declaration
.append("public static ");
}
StringBuilder type = new StringBuilder();
if ( containerType != null ) {
type.append(annotationMetaEntity.importType(containerType)).append('<');
}
type.append(annotationMetaEntity.importType(entity));
if ( containerType != null ) {
type.append('>');
}
declaration
.append(type)
.append(" ")
.append(methodName)
.append("(");
if ( !belongsToDao ) {
declaration
.append(annotationMetaEntity.importType("jakarta.persistence.EntityManager"))
.append(" entityManager");
}
for ( int i = 0; i < paramNames.size(); i ++ ) {
if ( !belongsToDao || i > 0 ) {
declaration
.append(", ");
}
declaration
.append(annotationMetaEntity.importType(paramTypes.get(i)))
.append(" ")
.append(paramNames.get(i));
}
declaration
.append(") {")
.append("\n\tvar builder = entityManager.getEntityManagerFactory().getCriteriaBuilder();")
.append("\n\tvar query = builder.createQuery(")
.append(annotationMetaEntity.importType(entity))
.append(".class);")
.append("\n\tvar entity = query.from(")
.append(annotationMetaEntity.importType(entity))
.append(".class);")
.append("\n\tquery.where(");
for ( int i = 0; i < paramNames.size(); i ++ ) {
if ( i>0 ) {
declaration
.append(", ");
}
declaration
.append("\n\t\t\tbuilder.equal(entity.get(")
.append(annotationMetaEntity.importType(entity+'_'))
.append('.')
.append(paramNames.get(i))
.append("), ")
//TODO: only safe if we are binding literals as parameters!!!
.append(paramNames.get(i))
.append(")");
}
declaration
.append("\n\t);")
.append("\n\treturn ");
if ( containerType != null && containerType.startsWith("org.hibernate") ) {
declaration
.append("(")
.append(type)
.append(") ");
}
declaration
.append("entityManager.createQuery(query)");
if ( containerType == null) {
declaration
.append("\n\t\t\t.getSingleResult()");
}
else if ( containerType.equals("java.util.List") ) {
declaration
.append("\n\t\t\t.getResultList()");
}
declaration
.append(";\n}");
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;
}
@Override
public String getAttributeNameDeclarationString() {
throw new UnsupportedOperationException();
}
@Override
public String getMetaType() {
throw new UnsupportedOperationException();
}
@Override
public String getPropertyName() {
return methodName;
}
@Override
public String getTypeDeclaration() {
return entity;
}
@Override
public Metamodel getHostingEntity() {
return annotationMetaEntity;
}
}

View File

@ -1,111 +0,0 @@
/*
* 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.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.Metamodel;
/**
* @author Gavin King
*/
public class FinderMethod implements MetaAttribute {
private final Metamodel annotationMetaEntity;
private final String methodName;
private final String entity;
private final String paramName;
private final String paramType;
private final boolean belongsToDao;
public FinderMethod(Metamodel annotationMetaEntity, String methodName, String entity, String paramName, String paramType, boolean belongsToDao) {
this.annotationMetaEntity = annotationMetaEntity;
this.methodName = methodName;
this.entity = entity;
this.paramName = paramName;
this.paramType = paramType;
this.belongsToDao = belongsToDao;
}
@Override
public boolean hasTypedAttribute() {
return true;
}
@Override
public boolean hasStringAttribute() {
return false;
}
@Override
public String getAttributeDeclarationString() {
StringBuilder declaration = new StringBuilder();
declaration
.append("\n/**\n * @see ")
.append(annotationMetaEntity.getQualifiedName())
.append("#")
.append(methodName)
.append("(")
.append(annotationMetaEntity.importType(paramType))
.append(")")
.append("\n **/\n");
if ( belongsToDao ) {
declaration
.append("@Override\npublic ");
}
else {
declaration
.append("public static ");
}
declaration
.append(annotationMetaEntity.importType(entity));
declaration
.append(" ")
.append(methodName)
.append("(");
if ( !belongsToDao ) {
declaration
.append(annotationMetaEntity.importType("jakarta.persistence.EntityManager"))
.append(" entityManager, ");
}
declaration
.append(annotationMetaEntity.importType(paramType))
.append(" ")
.append(paramName)
.append(") {")
.append("\n\treturn entityManager.find(")
.append(annotationMetaEntity.importType(entity))
.append(".class, ")
.append(paramName)
.append(");")
.append("\n}");
return declaration.toString();
}
@Override
public String getAttributeNameDeclarationString() {
throw new UnsupportedOperationException();
}
@Override
public String getMetaType() {
throw new UnsupportedOperationException();
}
@Override
public String getPropertyName() {
return methodName;
}
@Override
public String getTypeDeclaration() {
return entity;
}
@Override
public Metamodel getHostingEntity() {
return annotationMetaEntity;
}
}

View File

@ -0,0 +1,115 @@
/*
* 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.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.Metamodel;
/**
* @author Gavin King
*/
public class IdFinderMethod implements MetaAttribute {
private final Metamodel annotationMetaEntity;
private final String methodName;
private final String entity;
private final String paramName;
private final String paramType;
private final boolean belongsToDao;
public IdFinderMethod(
Metamodel annotationMetaEntity,
String methodName, String entity,
String paramName, String paramType,
boolean belongsToDao) {
this.annotationMetaEntity = annotationMetaEntity;
this.methodName = methodName;
this.entity = entity;
this.paramName = paramName;
this.paramType = paramType;
this.belongsToDao = belongsToDao;
}
@Override
public boolean hasTypedAttribute() {
return true;
}
@Override
public boolean hasStringAttribute() {
return false;
}
@Override
public String getAttributeDeclarationString() {
StringBuilder declaration = new StringBuilder();
declaration
.append("\n/**\n * @see ")
.append(annotationMetaEntity.getQualifiedName())
.append("#")
.append(methodName)
.append("(")
.append(annotationMetaEntity.importType(paramType))
.append(")")
.append("\n **/\n");
if ( belongsToDao ) {
declaration
.append("@Override\npublic ");
}
else {
declaration
.append("public static ");
}
declaration
.append(annotationMetaEntity.importType(entity));
declaration
.append(" ")
.append(methodName)
.append("(");
if ( !belongsToDao ) {
declaration
.append(annotationMetaEntity.importType("jakarta.persistence.EntityManager"))
.append(" entityManager, ");
}
declaration
.append(annotationMetaEntity.importType(paramType))
.append(" ")
.append(paramName)
.append(") {")
.append("\n\treturn entityManager.find(")
.append(annotationMetaEntity.importType(entity))
.append(".class, ")
.append(paramName)
.append(");")
.append("\n}");
return declaration.toString();
}
@Override
public String getAttributeNameDeclarationString() {
throw new UnsupportedOperationException();
}
@Override
public String getMetaType() {
throw new UnsupportedOperationException();
}
@Override
public String getPropertyName() {
return methodName;
}
@Override
public String getTypeDeclaration() {
return entity;
}
@Override
public Metamodel getHostingEntity() {
return annotationMetaEntity;
}
}

View File

@ -0,0 +1,146 @@
/*
* 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.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.Metamodel;
import java.util.List;
import static org.hibernate.internal.util.StringHelper.join;
/**
* @author Gavin King
*/
public class NaturalIdFinderMethod implements MetaAttribute {
private final Metamodel annotationMetaEntity;
private final String methodName;
private final String entity;
private final List<String> paramNames;
private final List<String> paramTypes;
private final boolean belongsToDao;
public NaturalIdFinderMethod(
Metamodel annotationMetaEntity,
String methodName, String entity,
List<String> paramNames, List<String> paramTypes,
boolean belongsToDao) {
this.annotationMetaEntity = annotationMetaEntity;
this.methodName = methodName;
this.entity = entity;
this.paramNames = paramNames;
this.paramTypes = paramTypes;
this.belongsToDao = belongsToDao;
}
@Override
public boolean hasTypedAttribute() {
return true;
}
@Override
public boolean hasStringAttribute() {
return false;
}
@Override
public String getAttributeDeclarationString() {
StringBuilder declaration = new StringBuilder();
declaration
.append("\n/**\n * @see ")
.append(annotationMetaEntity.getQualifiedName())
.append("#")
.append(methodName)
.append("(")
.append(join(",", paramTypes.stream().map(this::strip).map(annotationMetaEntity::importType).toArray()))
.append(")")
.append("\n **/\n");
if ( belongsToDao ) {
declaration
.append("@Override\npublic ");
}
else {
declaration
.append("public static ");
}
declaration
.append(annotationMetaEntity.importType(entity));
declaration
.append(" ")
.append(methodName)
.append("(");
if ( !belongsToDao ) {
declaration
.append(annotationMetaEntity.importType("jakarta.persistence.EntityManager"))
.append(" entityManager");
}
for ( int i = 0; i < paramNames.size(); i ++ ) {
if ( !belongsToDao || i > 0 ) {
declaration
.append(", ");
}
declaration
.append(annotationMetaEntity.importType(paramTypes.get(i)))
.append(" ")
.append(paramNames.get(i));
}
declaration
.append(") {")
.append("\n\treturn entityManager")
//TODO: skip if unnecessary:
.append(".unwrap(")
.append(annotationMetaEntity.importType("org.hibernate.Session"))
.append(".class)\n\t\t\t")
.append(".byNaturalId(")
.append(annotationMetaEntity.importType(entity))
.append(".class)");
for ( int i = 0; i < paramNames.size(); i ++ ) {
declaration
.append("\n\t\t\t.using(")
.append(annotationMetaEntity.importType(entity+'_'))
.append('.')
.append(paramNames.get(i))
.append(", ")
.append(paramNames.get(i))
.append(")");
}
declaration
.append("\n\t\t\t.load();\n}");
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;
}
@Override
public String getAttributeNameDeclarationString() {
throw new UnsupportedOperationException();
}
@Override
public String getMetaType() {
throw new UnsupportedOperationException();
}
@Override
public String getPropertyName() {
return methodName;
}
@Override
public String getTypeDeclaration() {
return entity;
}
@Override
public Metamodel getHostingEntity() {
return annotationMetaEntity;
}
}

View File

@ -70,7 +70,9 @@ public class QueryMethod implements MetaAttribute {
@Override
public String getAttributeDeclarationString() {
List<String> paramTypes = this.paramTypes.stream()
.map(ptype->isOrderParam(ptype) && ptype.endsWith("[]") ? ptype.substring(0, ptype.length()-2) + "..." : ptype)
.map(ptype -> isOrderParam(ptype) && ptype.endsWith("[]")
? ptype.substring(0, ptype.length()-2) + "..."
: ptype)
.collect(toList());
StringBuilder declaration = new StringBuilder();
declaration
@ -79,7 +81,8 @@ public class QueryMethod implements MetaAttribute {
.append("#")
.append(methodName)
.append("(")
.append(join(",", paramTypes.stream().map(this::strip).map(annotationMetaEntity::importType).toArray()))
.append(join(",", paramTypes.stream().map(this::strip)
.map(annotationMetaEntity::importType).toArray()))
.append(")")
.append("\n **/\n");
boolean hasVarargs = paramTypes.stream().anyMatch(ptype -> ptype.endsWith("..."));
@ -103,7 +106,8 @@ public class QueryMethod implements MetaAttribute {
if (containerTypeName != null) {
type.append(annotationMetaEntity.importType(containerTypeName));
if (returnTypeName != null) {
type.append("<").append(annotationMetaEntity.importType(returnTypeName)).append(">");
type.append("<")
.append(annotationMetaEntity.importType(returnTypeName)).append(">");
}
}
else if (returnTypeName != null) {
@ -205,10 +209,12 @@ public class QueryMethod implements MetaAttribute {
}
}
if ( containerTypeName == null) {
declaration.append("\n\t\t\t.getSingleResult()");
declaration
.append("\n\t\t\t.getSingleResult()");
}
else if ( containerTypeName.equals("java.util.List") ) {
declaration.append("\n\t\t\t.getResultList()");
declaration
.append("\n\t\t\t.getResultList()");
}
declaration.append(";\n}");
return declaration.toString();

View File

@ -19,6 +19,7 @@ public final class Constants {
public static final String EMBEDDABLE = "jakarta.persistence.Embeddable";
public static final String ID = "jakarta.persistence.Id";
public static final String EMBEDDED_ID = "jakarta.persistence.EmbeddedId";
public static final String NATURAL_ID = "org.hibernate.annotations.NaturalId";
public static final String TRANSIENT = "jakarta.persistence.Transient";
public static final String BASIC = "jakarta.persistence.Basic";
public static final String ONE_TO_ONE = "jakarta.persistence.OneToOne";

View File

@ -2,10 +2,12 @@ package org.hibernate.jpamodelgen.test.dao;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.hibernate.annotations.NaturalId;
@Entity
public class Book {
@Id String isbn;
String title;
@NaturalId String title;
@NaturalId String author;
String text;
}

View File

@ -5,6 +5,7 @@ import jakarta.persistence.TypedQuery;
import org.hibernate.annotations.processing.Find;
import org.hibernate.annotations.processing.HQL;
import org.hibernate.annotations.processing.SQL;
import org.hibernate.query.SelectionQuery;
import java.util.List;
@ -15,6 +16,18 @@ public interface Dao {
@Find
Book getBook(String isbn);
@Find
Book getBook(String title, String author);
@Find
Book getBook(String title, String isbn, String author);
@Find
List<Book> getBooks(String title);
@Find
SelectionQuery<Book> createBooksSelectionQuery(String title);
@HQL("from Book where title like ?1")
TypedQuery<Book> findByTitle(String title);

View File

@ -1,6 +1,7 @@
package org.hibernate.jpamodelgen.test.hqlsql;
import jakarta.persistence.TypedQuery;
import org.hibernate.annotations.processing.Find;
import org.hibernate.annotations.processing.HQL;
import org.hibernate.annotations.processing.SQL;
import org.hibernate.query.Order;
@ -10,6 +11,13 @@ import org.hibernate.query.SelectionQuery;
import java.util.List;
public interface Dao {
@Find
Book getBook(String isbn);
@Find
Book getBook(String title, String isbn);
@HQL("from Book where title like ?1")
TypedQuery<Book> findByTitle(String title);