HHH-16633 allow finder and query methods to accept the session (better impl)

This commit is contained in:
Gavin King 2023-07-14 13:58:57 +02:00
parent 803cd6aa1e
commit ded5451436
8 changed files with 211 additions and 170 deletions

View File

@ -33,7 +33,11 @@ public abstract class AbstractFinderMethod extends AbstractQueryMethod {
List<String> paramNames,
List<String> paramTypes,
boolean addNonnullAnnotation) {
super( annotationMetaEntity, methodName, paramNames, paramTypes, sessionType, sessionName, belongsToDao, addNonnullAnnotation );
super( annotationMetaEntity,
methodName,
paramNames, paramTypes, entity,
sessionType, sessionName,
belongsToDao, addNonnullAnnotation );
this.entity = entity;
this.fetchProfiles = fetchProfiles;
}
@ -111,14 +115,8 @@ public abstract class AbstractFinderMethod extends AbstractQueryMethod {
}
declaration
.append('.')
.append("\n *")
.append("\n * @see ")
.append(annotationMetaEntity.getQualifiedName())
.append("#")
.append(methodName)
.append("(")
.append(parameterList())
.append(")");
.append("\n *");
see( declaration );
// declaration
// .append("\n *");
// for (String param : paramNames) {
@ -133,7 +131,7 @@ public abstract class AbstractFinderMethod extends AbstractQueryMethod {
}
void unwrapSession(StringBuilder declaration) {
if ( usingEntityManager ) {
if ( isUsingEntityManager() ) {
declaration
.append(".unwrap(")
.append(annotationMetaEntity.importType(Constants.HIB_SESSION))
@ -162,7 +160,7 @@ public abstract class AbstractFinderMethod extends AbstractQueryMethod {
declaration
.append(" ")
.append(methodName);
parameters( declaration) ;
parameters( paramTypes, declaration) ;
declaration
.append(" {")
.append("\n\treturn ")
@ -170,14 +168,14 @@ public abstract class AbstractFinderMethod extends AbstractQueryMethod {
}
private void entityType(StringBuilder declaration) {
if ( reactive ) {
if ( isReactive() ) {
declaration
.append(annotationMetaEntity.importType(Constants.UNI))
.append('<');
}
declaration
.append(annotationMetaEntity.importType(entity));
if ( reactive ) {
if ( isReactive() ) {
declaration
.append('>');
}
@ -187,25 +185,4 @@ public abstract class AbstractFinderMethod extends AbstractQueryMethod {
declaration
.append(belongsToDao ? "@Override\npublic " : "public static ");
}
void parameters(StringBuilder declaration) {
declaration
.append("(");
sessionParameter( declaration );
for ( int i = 0; i < paramNames.size(); i ++ ) {
if ( !belongsToDao || i > 0 ) {
declaration
.append(", ");
}
if ( isId() ) {
notNull( declaration );
}
declaration
.append(annotationMetaEntity.importType(paramTypes.get(i)))
.append(" ")
.append(paramNames.get(i));
}
declaration
.append(')');
}
}

View File

@ -6,12 +6,12 @@
*/
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 org.hibernate.jpamodelgen.util.Constants;
import java.util.List;
import java.util.Set;
import static org.hibernate.jpamodelgen.util.Constants.SESSION_TYPES;
@ -23,17 +23,17 @@ public abstract class AbstractQueryMethod implements MetaAttribute {
final String methodName;
final List<String> paramNames;
final List<String> paramTypes;
final @Nullable String returnTypeName;
final String sessionType;
final String sessionName;
final boolean belongsToDao;
final boolean usingEntityManager;
final boolean reactive;
final boolean addNonnullAnnotation;
public AbstractQueryMethod(
Metamodel annotationMetaEntity,
String methodName,
List<String> paramNames, List<String> paramTypes,
@Nullable String returnTypeName,
String sessionType,
String sessionName,
boolean belongsToDao,
@ -42,12 +42,11 @@ public abstract class AbstractQueryMethod implements MetaAttribute {
this.methodName = methodName;
this.paramNames = paramNames;
this.paramTypes = paramTypes;
this.returnTypeName = returnTypeName;
this.sessionType = sessionType;
this.sessionName = sessionName;
this.belongsToDao = belongsToDao;
this.addNonnullAnnotation = addNonnullAnnotation;
this.usingEntityManager = Constants.ENTITY_MANAGER.equals(sessionType);
this.reactive = Constants.MUTINY_SESSION.equals(sessionType);
}
@Override
@ -65,6 +64,8 @@ public abstract class AbstractQueryMethod implements MetaAttribute {
return methodName;
}
abstract boolean isId();
String parameterList() {
return paramTypes.stream()
.map(this::strip)
@ -79,13 +80,49 @@ public abstract class AbstractQueryMethod implements MetaAttribute {
return type.endsWith("...") ? stripped + "..." : stripped;
}
void parameters(List<String> paramTypes, StringBuilder declaration) {
declaration
.append("(");
sessionParameter( declaration );
for ( int i = 0; i < paramNames.size(); i++ ) {
if ( i > 0 ) {
declaration
.append(", ");
}
final String paramType = paramTypes.get(i);
if ( isId() || isSessionParameter(paramType) ) {
notNull( declaration );
}
declaration
.append(annotationMetaEntity.importType(importReturnTypeArgument(paramType)))
.append(" ")
.append(paramNames.get(i));
}
declaration
.append(")");
}
static boolean isSessionParameter(String paramType) {
return SESSION_TYPES.contains(paramType);
}
private String importReturnTypeArgument(String type) {
return returnTypeName != null
? type.replace(returnTypeName, annotationMetaEntity.importType(returnTypeName))
: type;
}
void sessionParameter(StringBuilder declaration) {
if ( !belongsToDao ) {
if ( !belongsToDao && paramTypes.stream().noneMatch(SESSION_TYPES::contains) ) {
notNull(declaration);
declaration
.append(annotationMetaEntity.importType(sessionType))
.append(' ')
.append(sessionName);
if ( !paramNames.isEmpty() ) {
declaration
.append(", ");
}
}
}
@ -97,4 +134,27 @@ public abstract class AbstractQueryMethod implements MetaAttribute {
.append(' ');
}
}
void see(StringBuilder declaration) {
declaration
.append("\n * @see ")
.append(annotationMetaEntity.getQualifiedName())
.append("#")
.append(methodName)
.append("(")
.append(parameterList())
.append(")");
}
boolean isUsingEntityManager() {
return Constants.ENTITY_MANAGER.equals(sessionType);
}
boolean isUsingStatelessSession() {
return Constants.HIB_STATELESS_SESSION.equals(sessionType);
}
boolean isReactive() {
return Constants.MUTINY_SESSION.equals(sessionType);
}
}

View File

@ -523,7 +523,6 @@ public class AnnotationMetaEntity extends AnnotationMeta {
final List<String> paramNames = parameterNames(method);
final List<String> paramTypes = parameterTypes(method);
final String[] sessionType = sessionTypeFromParameters( paramNames, paramTypes );
removeSessionFromParameters(paramNames, paramTypes);
final String methodKey = methodName + paramTypes;
for ( VariableElement param : method.getParameters() ) {
if ( isFinderParameterMappingToAttribute( param ) ) {
@ -549,25 +548,14 @@ public class AnnotationMetaEntity extends AnnotationMeta {
}
private static boolean isFinderParameterMappingToAttribute(VariableElement param) {
return !SESSION_TYPES.contains(param.asType().toString());
}
private static void removeSessionFromParameters(List<String> paramNames, List<String> paramTypes) {
for ( int i = 0; i < paramNames.size(); i ++ ) {
final String type = paramTypes.get(i);
if ( SESSION_TYPES.contains(type) ) {
paramNames.remove(i);
paramTypes.remove(i);
break;
}
}
return !isSessionParameter( param.asType().toString() );
}
private String[] sessionTypeFromParameters(List<String> paramNames, List<String> paramTypes) {
for ( int i = 0; i < paramNames.size(); i ++ ) {
final String type = paramTypes.get(i);
final String name = paramNames.get(i);
if ( SESSION_TYPES.contains(type) ) {
if ( isSessionParameter(type) ) {
return new String[] { type, name };
}
}
@ -597,7 +585,6 @@ public class AnnotationMetaEntity extends AnnotationMeta {
final List<String> paramNames = parameterNames( method );
final List<String> paramTypes = parameterTypes( method );
final String[] sessionType = sessionTypeFromParameters( paramNames, paramTypes );
removeSessionFromParameters( paramNames, paramTypes );
final String methodKey = methodName + paramTypes;
if ( !usingStatelessSession(sessionType[0]) // no byNaturalId() lookup API for SS
&& matchesNaturalKey( method, entity ) ) {
@ -645,7 +632,6 @@ public class AnnotationMetaEntity extends AnnotationMeta {
final List<String> paramNames = parameterNames(method);
final List<String> paramTypes = parameterTypes(method);
final String[] sessionType = sessionTypeFromParameters( paramNames, paramTypes );
removeSessionFromParameters(paramNames, paramTypes);
final FieldType fieldType = validateFinderParameter( entity, parameter );
if ( fieldType != null ) {
final String methodKey = methodName + "!";
@ -657,8 +643,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
this,
methodName,
returnType.toString(),
paramNames.get(0),
paramTypes.get(0),
paramNames,
paramTypes,
dao,
sessionType[0],
sessionType[1],
@ -724,8 +710,10 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private boolean matchesNaturalKey(ExecutableElement method, TypeElement entity) {
boolean result = true;
final List<? extends VariableElement> parameters = method.getParameters();
int count = 0;
for ( VariableElement param : parameters ) {
if ( isFinderParameterMappingToAttribute( param ) ) {
count ++;
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
@ -733,7 +721,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
}
}
}
return result && countNaturalIdFields( entity ) == parameters.size() ;
return result && countNaturalIdFields( entity ) == count;
}
enum FieldType {
@ -834,7 +822,6 @@ public class AnnotationMetaEntity extends AnnotationMeta {
final List<String> paramNames = parameterNames( method );
final List<String> paramTypes = parameterTypes( method );
final String[] sessionType = sessionTypeFromParameters( paramNames, paramTypes );
removeSessionFromParameters(paramNames, paramTypes);
final QueryMethod attribute =
new QueryMethod(
this,
@ -973,10 +960,15 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private static boolean parameterIsMissing(String hql, int i, String param, String type) {
return !hql.matches(".*(:" + param + "|\\?" + i + ")\\b.*")
&& !isSessionParameter(type)
&& !isPageParam(type)
&& !isOrderParam(type);
}
private static boolean isSessionParameter(String type) {
return SESSION_TYPES.contains(type);
}
private boolean usingReactiveSession(String sessionType) {
return Constants.MUTINY_SESSION.equals(sessionType);
}

View File

@ -52,7 +52,7 @@ public class CriteriaFinderMethod extends AbstractFinderMethod {
.append(returnType())
.append(" ")
.append(methodName);
parameters( declaration );
parameters( paramTypes, declaration );
declaration
.append(" {");
if ( isId ) {
@ -64,7 +64,7 @@ public class CriteriaFinderMethod extends AbstractFinderMethod {
declaration
.append("\n\tvar builder = ")
.append(sessionName)
.append(usingEntityManager
.append(isUsingEntityManager()
? ".getEntityManagerFactory()"
: ".getFactory()")
.append(".getCriteriaBuilder();")
@ -75,34 +75,40 @@ public class CriteriaFinderMethod extends AbstractFinderMethod {
.append(annotationMetaEntity.importType(entity))
.append(".class);")
.append("\n\tquery.where(");
boolean first = true;
for ( int i = 0; i < paramNames.size(); i ++ ) {
if ( i>0 ) {
declaration
.append(", ");
}
final String paramName = paramNames.get(i);
final String paramType = paramTypes.get(i);
declaration
.append("\n\t\t\t");
if ( !isId && !isPrimitive( paramType ) ) { //TODO: check the entity to see if it's @Basic(optional=false)
if ( !isSessionParameter(paramType) ) {
if ( first ) {
first = false;
}
else {
declaration
.append(", ");
}
declaration
.append(paramName)
.append("==null")
.append("\n\t\t\t\t? ")
.append("entity.get(");
.append("\n\t\t\t");
if ( !isId && !isPrimitive(paramType) ) { //TODO: check the entity to see if it's @Basic(optional=false)
declaration
.append(paramName)
.append("==null")
.append("\n\t\t\t\t? ")
.append("entity.get(");
attributeRef( declaration, paramName );
declaration
.append(").isNull()")
.append("\n\t\t\t\t: ");
}
declaration
.append("builder.equal(entity.get(");
attributeRef( declaration, paramName );
declaration
.append(").isNull()")
.append("\n\t\t\t\t: ");
.append("), ")
//TODO: only safe if we are binding literals as parameters!!!
.append(paramName)
.append(")");
}
declaration
.append("builder.equal(entity.get(");
attributeRef( declaration, paramName );
declaration
.append("), ")
//TODO: only safe if we are binding literals as parameters!!!
.append(paramName)
.append(")");
}
declaration
.append("\n\t);")
@ -110,10 +116,11 @@ public class CriteriaFinderMethod extends AbstractFinderMethod {
.append(sessionName)
.append(".createQuery(query)");
final boolean hasEnabledFetchProfiles = !fetchProfiles.isEmpty();
final boolean hasNativeReturnType = containerType != null && containerType.startsWith("org.hibernate");
final boolean hasNativeReturnType =
containerType != null && containerType.startsWith("org.hibernate");
final boolean unwrap =
( hasEnabledFetchProfiles || hasNativeReturnType )
&& usingEntityManager;
&& isUsingEntityManager();
if ( unwrap ) {
declaration
.append("\n\t\t\t.unwrap(")
@ -156,7 +163,7 @@ public class CriteriaFinderMethod extends AbstractFinderMethod {
private StringBuilder returnType() {
StringBuilder type = new StringBuilder();
boolean returnsUni = reactive
boolean returnsUni = isReactive()
&& (containerType == null || Constants.LIST.equals(containerType));
if ( returnsUni ) {
type.append(annotationMetaEntity.importType(Constants.UNI)).append('<');

View File

@ -17,21 +17,28 @@ import java.util.List;
public class IdFinderMethod extends AbstractFinderMethod {
private final String paramName;
private final boolean usingStatelessSession;
public IdFinderMethod(
Metamodel annotationMetaEntity,
String methodName, String entity,
String paramName, String paramType,
List<String> paramNames, List<String> paramTypes,
boolean belongsToDao,
String sessionType,
String sessionName,
List<String> fetchProfiles,
boolean addNonnullAnnotation) {
super( annotationMetaEntity, methodName, entity, belongsToDao, sessionType, sessionName, fetchProfiles,
List.of(paramName), List.of(paramType), addNonnullAnnotation );
this.paramName = paramName;
usingStatelessSession = Constants.HIB_STATELESS_SESSION.equals(sessionType);
paramNames, paramTypes, addNonnullAnnotation );
this.paramName = idParameterName( paramNames, paramTypes );
}
private static String idParameterName(List<String> paramNames, List<String> paramTypes) {
for (int i = 0; i < paramNames.size(); i ++ ) {
if ( !isSessionParameter( paramTypes.get(i) ) ) {
return paramNames.get(i);
}
}
return ""; // should never occur!
}
@Override
@ -68,7 +75,7 @@ public class IdFinderMethod extends AbstractFinderMethod {
private void findWithNoFetchProfiles(StringBuilder declaration) {
declaration
.append(usingStatelessSession ? ".get(" : ".find(")
.append(isUsingStatelessSession() ? ".get(" : ".find(")
.append(annotationMetaEntity.importType(entity))
.append(".class, ")
.append(paramName)

View File

@ -40,7 +40,7 @@ public class NaturalIdFinderMethod extends AbstractFinderMethod {
comment( declaration );
preamble( declaration );
unwrapSession( declaration );
if ( reactive ) {
if ( isReactive() ) {
findReactively( declaration );
}
else {
@ -57,22 +57,25 @@ public class NaturalIdFinderMethod extends AbstractFinderMethod {
.append(".class)");
enableFetchProfile( declaration );
for ( int i = 0; i < paramNames.size(); i ++ ) {
final String paramName = paramNames.get(i);
declaration
.append("\n\t\t\t.using(")
.append(annotationMetaEntity.importType(entity + '_'))
.append('.')
.append(paramName)
.append(", ")
.append(paramName)
.append(")");
if ( !isSessionParameter( paramTypes.get(i) ) ) {
final String paramName = paramNames.get(i);
declaration
.append("\n\t\t\t.using(")
.append(annotationMetaEntity.importType(entity + '_'))
.append('.')
.append(paramName)
.append(", ")
.append(paramName)
.append(")");
}
}
declaration
.append("\n\t\t\t.load()");
}
private void findReactively(StringBuilder declaration) {
boolean composite = paramNames.size() > 1;
boolean composite = paramTypes.stream()
.filter(type -> !isSessionParameter(type)).count()>1;
declaration
.append(".find(");
if (composite) {
@ -87,25 +90,31 @@ public class NaturalIdFinderMethod extends AbstractFinderMethod {
.append(annotationMetaEntity.importType("org.hibernate.reactive.common.Identifier"))
.append(".composite(");
}
boolean first = true;
for ( int i = 0; i < paramNames.size(); i ++ ) {
if ( i>0 ) {
if ( !isSessionParameter( paramTypes.get(i) ) ) {
if ( first ) {
first = false;
}
else {
declaration
.append(", ");
}
if (composite) {
declaration
.append("\n\t\t\t\t");
}
final String paramName = paramNames.get(i);
declaration
.append(", ");
.append(annotationMetaEntity.importType("org.hibernate.reactive.common.Identifier"))
.append(".id(")
.append(annotationMetaEntity.importType(entity + '_'))
.append('.')
.append(paramName)
.append(", ")
.append(paramName)
.append(")");
}
if (composite) {
declaration
.append("\n\t\t\t\t");
}
final String paramName = paramNames.get(i);
declaration
.append(annotationMetaEntity.importType("org.hibernate.reactive.common.Identifier"))
.append(".id(")
.append(annotationMetaEntity.importType(entity + '_'))
.append('.')
.append(paramName)
.append(", ")
.append(paramName)
.append(")");
}
if (composite) {
declaration.append("\n\t\t\t)\n\t");

View File

@ -42,7 +42,11 @@ public class QueryMethod extends AbstractQueryMethod {
String sessionType,
String sessionName,
boolean addNonnullAnnotation) {
super( annotationMetaEntity, methodName, paramNames, paramTypes, sessionType, sessionName, belongsToDao, addNonnullAnnotation );
super( annotationMetaEntity,
methodName,
paramNames, paramTypes, returnTypeName,
sessionType, sessionName,
belongsToDao, addNonnullAnnotation );
this.queryString = queryString;
this.returnTypeName = returnTypeName;
this.containerTypeName = containerTypeName;
@ -59,6 +63,11 @@ public class QueryMethod extends AbstractQueryMethod {
return true;
}
@Override
boolean isId() {
return false;
}
@Override
public String getAttributeDeclarationString() {
final List<String> paramTypes = parameterTypes();
@ -75,7 +84,7 @@ public class QueryMethod extends AbstractQueryMethod {
.append(" {")
.append("\n\treturn ");
if ( isNative && returnTypeName != null && containerTypeName == null
&& usingEntityManager) {
&& isUsingEntityManager() ) {
// EntityManager.createNativeQuery() does not return TypedQuery,
// so we need to cast to the entity type
declaration.append("(")
@ -104,7 +113,7 @@ public class QueryMethod extends AbstractQueryMethod {
.append("\n\t\t\t.getResultList()");
}
else {
if ( usingEntityManager && !unwrapped
if ( isUsingEntityManager() && !unwrapped
&& ( containerTypeName.startsWith("org.hibernate")
|| isNative && returnTypeName != null ) ) {
declaration
@ -119,21 +128,24 @@ public class QueryMethod extends AbstractQueryMethod {
}
private boolean setParameters(List<String> paramTypes, StringBuilder declaration) {
boolean unwrapped = !usingEntityManager;
for (int i = 1; i <= paramNames.size(); i++) {
final String paramName = paramNames.get(i-1);
final String paramType = paramTypes.get(i-1);
if ( queryString.contains(":" + paramName) ) {
setNamedParameter( declaration, paramName );
}
else if ( queryString.contains("?" + i) ) {
setOrdinalParameter( declaration, i, paramName );
}
else if ( isPageParam(paramType) ) {
setPage( declaration, paramName );
}
else if ( isOrderParam(paramType) ) {
unwrapped = setOrder( declaration, unwrapped, paramName, paramType );
boolean unwrapped = !isUsingEntityManager();
for ( int i = 0; i < paramNames.size(); i++ ) {
final String paramName = paramNames.get(i);
final String paramType = paramTypes.get(i);
if ( !isSessionParameter(paramType) ) {
final int ordinal = i+1;
if ( queryString.contains(":" + paramName) ) {
setNamedParameter( declaration, paramName );
}
else if ( queryString.contains("?" + ordinal) ) {
setOrdinalParameter( declaration, ordinal, paramName );
}
else if ( isPageParam(paramType) ) {
setPage( declaration, paramName );
}
else if ( isOrderParam(paramType) ) {
unwrapped = setOrder( declaration, unwrapped, paramName, paramType );
}
}
}
return unwrapped;
@ -158,7 +170,7 @@ public class QueryMethod extends AbstractQueryMethod {
}
private void setPage(StringBuilder declaration, String paramName) {
if ( usingEntityManager ) {
if ( isUsingEntityManager() ) {
declaration
.append("\n\t\t\t.setFirstResult(")
.append(paramName)
@ -194,31 +206,9 @@ public class QueryMethod extends AbstractQueryMethod {
return true;
}
private void parameters(List<String> paramTypes, StringBuilder declaration) {
declaration
.append("(");
sessionParameter( declaration );
for ( int i = 0; i < paramNames.size(); i++ ) {
if ( !belongsToDao || i > 0 ) {
declaration
.append(", ");
}
final String type = paramTypes.get(i);
final String paramType = returnTypeName != null
? type.replace(returnTypeName, annotationMetaEntity.importType(returnTypeName))
: type;
declaration
.append(annotationMetaEntity.importType(paramType))
.append(" ")
.append(paramNames.get(i));
}
declaration
.append(")");
}
private StringBuilder returnType() {
StringBuilder type = new StringBuilder();
boolean returnsUni = reactive
boolean returnsUni = isReactive()
&& (containerTypeName == null || Constants.LIST.equals(containerTypeName));
if ( returnsUni ) {
type.append(annotationMetaEntity.importType(Constants.UNI)).append('<');
@ -248,13 +238,9 @@ public class QueryMethod extends AbstractQueryMethod {
private void comment(StringBuilder declaration) {
declaration
.append("\n/**\n * @see ")
.append(annotationMetaEntity.getQualifiedName())
.append("#")
.append(methodName)
.append("(")
.append(parameterList())
.append(")")
.append("\n/**");
see( declaration );
declaration
.append("\n **/\n");
}

View File

@ -19,10 +19,13 @@ public abstract class Books {
@Find
abstract Book getBookByNaturalKey(Session session, String authorName, String title);
@HQL("from Book where title like ?1")
@HQL("from Book where title is not null")
abstract List<Book> allbooks(StatelessSession ss);
@HQL("from Book where title like ?2")
abstract TypedQuery<Book> findByTitle(EntityManager entityManager, String title);
@HQL("from Book where title like ?1 order by title fetch first ?2 rows only")
@HQL("from Book where title like ?2 order by title fetch first ?3 rows only")
abstract List<Book> findFirstNByTitle(Session session, String title, int N);