allow @Find @Nullable and @Query @Nullable

to return null from a repository method

Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
Gavin King 2024-04-11 23:38:36 +02:00 committed by Christian Beikov
parent 37195c21e1
commit 2a8fef4386
13 changed files with 83 additions and 54 deletions

View File

@ -1,5 +1,6 @@
package org.hibernate.processor.test.data.basic;
import jakarta.annotation.Nullable;
import jakarta.data.Limit;
import jakarta.data.Order;
import jakarta.data.Sort;
@ -65,6 +66,9 @@ public interface BookAuthorRepository {
@Find
Optional<Book> bookIfAny(String isbn);
@Find @Nullable
Book bookOrNullIfNone(String isbn);
@Find
Author author(String ssn);
@ -116,6 +120,10 @@ public interface BookAuthorRepository {
@Query("from Book where title = :title")
Book bookWithTitle(String title);
@Nullable
@Query("from Book where title = :title")
Book bookWithTitleOrNullIfNone(String title);
@Query("from Book where title = :title")
Optional<Book> bookWithTitleMaybe(String title);

View File

@ -37,10 +37,11 @@ public abstract class AbstractCriteriaMethod extends AbstractFinderMethod {
boolean convertToDataExceptions,
List<Boolean> multivalued,
List<Boolean> paramPatterns,
String fullReturnType) {
String fullReturnType,
boolean nullable) {
super(annotationMetaEntity, method, methodName, entity, containerType, belongsToDao, sessionType, sessionName,
fetchProfiles, paramNames, paramTypes, orderBys, addNonnullAnnotation, convertToDataExceptions,
fullReturnType);
fullReturnType, nullable);
this.multivalued = multivalued;
this.paramPatterns = paramPatterns;
}

View File

@ -39,7 +39,8 @@ public abstract class AbstractFinderMethod extends AbstractQueryMethod {
List<OrderBy> orderBys,
boolean addNonnullAnnotation,
boolean convertToDataExceptions,
String fullReturnType) {
String fullReturnType,
boolean nullable) {
super( annotationMetaEntity, method,
methodName,
paramNames, paramTypes, entity,
@ -47,7 +48,8 @@ public abstract class AbstractFinderMethod extends AbstractQueryMethod {
belongsToDao, orderBys,
addNonnullAnnotation,
convertToDataExceptions,
fullReturnType );
fullReturnType,
nullable );
this.entity = entity;
this.containerType = containerType;
this.fetchProfiles = fetchProfiles;

View File

@ -50,6 +50,7 @@ public abstract class AbstractQueryMethod extends AbstractAnnotatedMethod {
final boolean addNonnullAnnotation;
final boolean dataRepository;
final String fullReturnType;
final boolean nullable;
AbstractQueryMethod(
AnnotationMetaEntity annotationMetaEntity,
@ -63,7 +64,8 @@ public abstract class AbstractQueryMethod extends AbstractAnnotatedMethod {
List<OrderBy> orderBys,
boolean addNonnullAnnotation,
boolean dataRepository,
String fullReturnType) {
String fullReturnType,
boolean nullable) {
super(annotationMetaEntity, method, sessionName, sessionType);
this.methodName = methodName;
this.paramNames = paramNames;
@ -74,6 +76,7 @@ public abstract class AbstractQueryMethod extends AbstractAnnotatedMethod {
this.addNonnullAnnotation = addNonnullAnnotation;
this.dataRepository = dataRepository;
this.fullReturnType = fullReturnType;
this.nullable = nullable;
}
@Override
@ -580,14 +583,6 @@ public abstract class AbstractQueryMethod extends AbstractAnnotatedMethod {
|| !orderBys.isEmpty();
}
boolean unwrapIfNecessary(StringBuilder declaration, @Nullable String containerType, boolean unwrapped) {
if ( OPTIONAL.equals(containerType) || isJakartaCursoredPage(containerType) ) {
unwrapQuery( declaration, unwrapped );
unwrapped = true;
}
return unwrapped;
}
protected void executeSelect(
StringBuilder declaration,
List<String> paramTypes,
@ -595,8 +590,15 @@ public abstract class AbstractQueryMethod extends AbstractAnnotatedMethod {
boolean unwrapped,
boolean mustUnwrap) {
if ( containerType == null ) {
declaration
.append("\t\t\t.getSingleResult();");
if ( nullable ) {
unwrapQuery(declaration, unwrapped);
declaration
.append("\t\t\t.getSingleResultOrNull();");
}
else {
declaration
.append("\t\t\t.getSingleResult();");
}
}
else {
switch (containerType) {
@ -612,6 +614,7 @@ public abstract class AbstractQueryMethod extends AbstractAnnotatedMethod {
}
break;
case OPTIONAL:
unwrapQuery(declaration, unwrapped);
declaration
.append("\t\t\t.uniqueResultOptional();");
break;

View File

@ -1561,7 +1561,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
orderByList( method, entity ),
context.addNonnullAnnotation(),
jakartaDataRepository,
fullReturnType(method)
fullReturnType(method),
hasAnnotation(method, NULLABLE)
)
);
}
@ -1832,7 +1833,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
orderByList( method, entity ),
context.addNonnullAnnotation(),
jakartaDataRepository,
fullReturnType(method)
fullReturnType(method),
hasAnnotation(method, NULLABLE)
)
);
}
@ -1868,7 +1870,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
profiles,
context.addNonnullAnnotation(),
jakartaDataRepository,
fullReturnType(method)
fullReturnType(method),
hasAnnotation(method, NULLABLE)
)
);
break;
@ -1916,7 +1919,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
orderByList( method, entity ),
context.addNonnullAnnotation(),
jakartaDataRepository,
fullReturnType(method)
fullReturnType(method),
hasAnnotation(method, NULLABLE)
)
);
break;
@ -2275,7 +2279,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
orderBys,
context.addNonnullAnnotation(),
jakartaDataRepository,
fullReturnType(method)
fullReturnType(method),
hasAnnotation(method, NULLABLE)
);
putMember( attribute.getPropertyName() + paramTypes, attribute );
}

View File

@ -35,7 +35,7 @@ public class CriteriaDeleteMethod extends AbstractCriteriaMethod {
String fullReturnType) {
super( annotationMetaEntity, method, methodName, entity, null, belongsToDao, sessionType,
sessionName, emptyList(), paramNames, paramTypes, emptyList(), addNonnullAnnotation, dataRepository,
multivalued, paramPatterns, fullReturnType);
multivalued, paramPatterns, fullReturnType, false );
this.paramNullability = paramNullability;
}

View File

@ -34,10 +34,11 @@ public class CriteriaFinderMethod extends AbstractCriteriaMethod {
List<OrderBy> orderBys,
boolean addNonnullAnnotation,
boolean dataRepository,
String fullReturnType) {
String fullReturnType,
boolean nullable) {
super( annotationMetaEntity, method, methodName, entity, containerType, belongsToDao, sessionType, sessionName,
fetchProfiles, paramNames, paramTypes, orderBys, addNonnullAnnotation, dataRepository, multivalued,
paramPatterns, fullReturnType);
paramPatterns, fullReturnType, nullable );
this.paramNullability = paramNullability;
}
@ -60,7 +61,8 @@ public class CriteriaFinderMethod extends AbstractCriteriaMethod {
castResult( declaration );
createQuery( declaration );
handlePageParameters( declaration, paramTypes, containerType );
boolean unwrapped = specialNeeds( declaration );
boolean unwrapped = !isUsingEntityManager();
unwrapped = enableFetchProfile( declaration, unwrapped );
unwrapped = applyOrder( declaration, paramTypes, containerType, unwrapped );
execute( declaration, paramTypes, unwrapped );
}
@ -75,13 +77,6 @@ public class CriteriaFinderMethod extends AbstractCriteriaMethod {
}
}
private boolean specialNeeds(StringBuilder declaration) {
boolean unwrapped = !isUsingEntityManager();
unwrapped = enableFetchProfile( declaration, unwrapped );
unwrapped = unwrapIfNecessary( declaration, containerType, unwrapped );
return unwrapped;
}
private void execute(StringBuilder declaration, List<String> paramTypes, boolean unwrapped) {
executeSelect( declaration, paramTypes, containerType, unwrapped, isHibernateQueryType(containerType) );
}

View File

@ -34,9 +34,11 @@ public class IdFinderMethod extends AbstractFinderMethod {
List<String> fetchProfiles,
boolean addNonnullAnnotation,
boolean dataRepository,
String fullReturnType) {
String fullReturnType,
boolean nullable) {
super( annotationMetaEntity, method, methodName, entity, containerType, belongsToDao, sessionType, sessionName,
fetchProfiles, paramNames, paramTypes, emptyList(), addNonnullAnnotation, dataRepository, fullReturnType );
fetchProfiles, paramNames, paramTypes, emptyList(), addNonnullAnnotation, dataRepository, fullReturnType,
nullable );
int idParameter = idParameter(paramNames, paramTypes);
this.paramName = paramNames.get(idParameter);
this.paramType = paramTypes.get(idParameter);
@ -84,7 +86,11 @@ public class IdFinderMethod extends AbstractFinderMethod {
}
private void throwIfNull(StringBuilder declaration) {
if ( containerType == null ) {
if (containerType != null) {
declaration
.append(')');
}
else if (!nullable) {
declaration
.append(";\n");
if (dataRepository) {
@ -102,7 +108,7 @@ public class IdFinderMethod extends AbstractFinderMethod {
.append(", \"")
.append(entity)
.append("\"));\n")
.append("\t\treturn _result;\n");
.append("\t\treturn _result");
}
else {
declaration
@ -113,13 +119,11 @@ public class IdFinderMethod extends AbstractFinderMethod {
.append(", \"")
.append(entity)
.append("\");\n")
.append("\treturn _result;\n");
.append("\treturn _result");
}
}
else {
declaration
.append(");\n");
}
declaration
.append(";\n");
}
private void varOrReturn(StringBuilder declaration) {
@ -127,15 +131,19 @@ public class IdFinderMethod extends AbstractFinderMethod {
declaration
.append("\ttry {\n\t");
}
if ( containerType == null ) {
if (containerType != null) {
declaration
.append("\treturn ")
.append(annotationMetaEntity.staticImport(containerType, "ofNullable"))
.append('(');
}
else if (!nullable) {
declaration
.append("\tvar _result = ");
}
else {
declaration
.append("\treturn ")
.append(annotationMetaEntity.staticImport(containerType, "ofNullable"))
.append('(');
.append("\treturn ");
}
declaration
.append(sessionName);

View File

@ -35,7 +35,8 @@ public class NaturalIdFinderMethod extends AbstractFinderMethod {
boolean dataRepository,
String fullReturnType) {
super( annotationMetaEntity, method, methodName, entity, containerType, belongsToDao, sessionType, sessionName,
fetchProfiles, paramNames, paramTypes, emptyList(), addNonnullAnnotation, dataRepository, fullReturnType );
fetchProfiles, paramNames, paramTypes, emptyList(), addNonnullAnnotation, dataRepository, fullReturnType,
true );
this.paramNullability = paramNullability;
}

View File

@ -47,7 +47,8 @@ public class QueryMethod extends AbstractQueryMethod {
List<OrderBy> orderBys,
boolean addNonnullAnnotation,
boolean dataRepository,
String fullReturnType) {
String fullReturnType,
boolean nullable) {
super( annotationMetaEntity, method,
methodName,
paramNames, paramTypes, returnTypeName,
@ -55,7 +56,8 @@ public class QueryMethod extends AbstractQueryMethod {
belongsToDao, orderBys,
addNonnullAnnotation,
dataRepository,
fullReturnType );
fullReturnType,
nullable );
this.queryString = queryString;
this.returnTypeClass = returnTypeClass;
this.containerType = containerType;
@ -97,7 +99,7 @@ public class QueryMethod extends AbstractQueryMethod {
createQuery( declaration );
setParameters( declaration, paramTypes, "");
handlePageParameters( declaration, paramTypes, containerType );
boolean unwrapped = specialNeeds( declaration );
boolean unwrapped = !isUsingEntityManager();
unwrapped = applyOrder( declaration, paramTypes, containerType, unwrapped );
execute( declaration, unwrapped );
convertExceptions( declaration );
@ -106,12 +108,6 @@ public class QueryMethod extends AbstractQueryMethod {
return declaration.toString();
}
private boolean specialNeeds(StringBuilder declaration) {
boolean unwrapped = !isUsingEntityManager();
unwrapped = unwrapIfNecessary( declaration, containerType, unwrapped );
return unwrapped;
}
@Override
void createQuery(StringBuilder declaration) {
declaration

View File

@ -130,6 +130,8 @@ public final class Constants {
public static final String OPTIONAL = "java.util.Optional";
public static final String STREAM = "java.util.stream.Stream";
public static final String NULLABLE = "jakarta.annotation.Nullable";
public static final String PANACHE_ORM_REPOSITORY_BASE = "io.quarkus.hibernate.orm.panache.PanacheRepositoryBase";
public static final String PANACHE_ORM_ENTITY_BASE = "io.quarkus.hibernate.orm.panache.PanacheEntityBase";
public static final String PANACHE_REACTIVE_REPOSITORY_BASE = "io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase";

View File

@ -1,5 +1,6 @@
package org.hibernate.processor.test.dao;
import jakarta.annotation.Nullable;
import jakarta.persistence.TypedQuery;
import org.hibernate.Session;
import org.hibernate.annotations.processing.Find;
@ -17,6 +18,9 @@ public interface StatefulDao {
@Find
Book getBook(String isbn);
@Find @Nullable
Book getBookOrNull(String isbn);
@Find
Optional<Book> getBookMaybe(String isbn);

View File

@ -1,5 +1,6 @@
package org.hibernate.processor.test.dao;
import jakarta.annotation.Nullable;
import jakarta.persistence.TypedQuery;
import org.hibernate.StatelessSession;
import org.hibernate.annotations.processing.Find;
@ -26,6 +27,9 @@ public interface StatelessDao {
@Find
Book getBook(String title, String author);
@Find @Nullable
Book getBookOrNull(String isbn);
@Find
Optional<Book> getBookMaybe(String title, String author);