HHH-16633 allow finder and query methods to accept the session type as a parameter

This commit is contained in:
Gavin King 2023-07-14 00:56:22 +02:00
parent a80224f921
commit 803cd6aa1e
10 changed files with 257 additions and 180 deletions

View File

@ -7,7 +7,6 @@
package org.hibernate.jpamodelgen.annotation; package org.hibernate.jpamodelgen.annotation;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.Metamodel; import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.Constants; import org.hibernate.jpamodelgen.util.Constants;
@ -19,41 +18,24 @@ import static org.hibernate.jpamodelgen.util.StringUtil.getUpperUnderscoreCaseFr
/** /**
* @author Gavin King * @author Gavin King
*/ */
public abstract class AbstractFinderMethod implements MetaAttribute { public abstract class AbstractFinderMethod extends AbstractQueryMethod {
final Metamodel annotationMetaEntity;
final String methodName;
final String entity; final String entity;
final boolean belongsToDao;
final String sessionType;
final boolean usingEntityManager;
final boolean reactive;
private final boolean addNonnullAnnotation;
final List<String> fetchProfiles; final List<String> fetchProfiles;
final List<String> paramNames;
final List<String> paramTypes;
public AbstractFinderMethod( public AbstractFinderMethod(
Metamodel annotationMetaEntity, Metamodel annotationMetaEntity,
String methodName, String methodName,
String entity, String entity,
boolean belongsToDao, boolean belongsToDao,
String sessionType, String sessionType,
String sessionName,
List<String> fetchProfiles, List<String> fetchProfiles,
List<String> paramNames, List<String> paramNames,
List<String> paramTypes, List<String> paramTypes,
boolean addNonnullAnnotation) { boolean addNonnullAnnotation) {
this.annotationMetaEntity = annotationMetaEntity; super( annotationMetaEntity, methodName, paramNames, paramTypes, sessionType, sessionName, belongsToDao, addNonnullAnnotation );
this.methodName = methodName;
this.entity = entity; this.entity = entity;
this.belongsToDao = belongsToDao;
this.sessionType = sessionType;
this.fetchProfiles = fetchProfiles; this.fetchProfiles = fetchProfiles;
this.paramNames = paramNames;
this.paramTypes = paramTypes;
this.addNonnullAnnotation = addNonnullAnnotation;
this.usingEntityManager = Constants.ENTITY_MANAGER.equals(sessionType);
this.reactive = Constants.MUTINY_SESSION.equals(sessionType);
} }
@Override @Override
@ -66,26 +48,11 @@ public abstract class AbstractFinderMethod implements MetaAttribute {
return false; return false;
} }
@Override
public String getMetaType() {
throw new UnsupportedOperationException();
}
@Override
public String getPropertyName() {
return methodName;
}
@Override @Override
public String getTypeDeclaration() { public String getTypeDeclaration() {
return entity; return entity;
} }
@Override
public Metamodel getHostingEntity() {
return annotationMetaEntity;
}
abstract boolean isId(); abstract boolean isId();
@Override @Override
@ -104,20 +71,6 @@ public abstract class AbstractFinderMethod implements MetaAttribute {
.toString(); .toString();
} }
private String parameterList() {
return paramTypes.stream()
.map(this::strip)
.map(annotationMetaEntity::importType)
.reduce((x, y) -> x + ',' + y)
.orElse("");
}
private String strip(String type) {
int index = type.indexOf("<");
String stripped = index > 0 ? type.substring(0, index) : type;
return type.endsWith("...") ? stripped + "..." : stripped;
}
String constantName() { String constantName() {
return getUpperUnderscoreCaseFromLowerCamelCase(methodName) + "_BY_" return getUpperUnderscoreCaseFromLowerCamelCase(methodName) + "_BY_"
+ paramNames.stream() + paramNames.stream()
@ -212,7 +165,8 @@ public abstract class AbstractFinderMethod implements MetaAttribute {
parameters( declaration) ; parameters( declaration) ;
declaration declaration
.append(" {") .append(" {")
.append("\n\treturn entityManager"); .append("\n\treturn ")
.append(sessionName);
} }
private void entityType(StringBuilder declaration) { private void entityType(StringBuilder declaration) {
@ -237,12 +191,7 @@ public abstract class AbstractFinderMethod implements MetaAttribute {
void parameters(StringBuilder declaration) { void parameters(StringBuilder declaration) {
declaration declaration
.append("("); .append("(");
if ( !belongsToDao ) { sessionParameter( declaration );
notNull( declaration );
declaration
.append(annotationMetaEntity.importType(Constants.ENTITY_MANAGER))
.append(" entityManager");
}
for ( int i = 0; i < paramNames.size(); i ++ ) { for ( int i = 0; i < paramNames.size(); i ++ ) {
if ( !belongsToDao || i > 0 ) { if ( !belongsToDao || i > 0 ) {
declaration declaration
@ -259,13 +208,4 @@ public abstract class AbstractFinderMethod implements MetaAttribute {
declaration declaration
.append(')'); .append(')');
} }
private void notNull(StringBuilder declaration) {
if ( addNonnullAnnotation ) {
declaration
.append('@')
.append(annotationMetaEntity.importType("jakarta.annotation.Nonnull"))
.append(' ');
}
}
} }

View File

@ -0,0 +1,100 @@
/*
* 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 org.hibernate.jpamodelgen.util.Constants;
import java.util.List;
import java.util.Set;
import static org.hibernate.jpamodelgen.util.Constants.SESSION_TYPES;
/**
* @author Gavin King
*/
public abstract class AbstractQueryMethod implements MetaAttribute {
final Metamodel annotationMetaEntity;
final String methodName;
final List<String> paramNames;
final List<String> paramTypes;
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,
String sessionType,
String sessionName,
boolean belongsToDao,
boolean addNonnullAnnotation) {
this.annotationMetaEntity = annotationMetaEntity;
this.methodName = methodName;
this.paramNames = paramNames;
this.paramTypes = paramTypes;
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
public Metamodel getHostingEntity() {
return annotationMetaEntity;
}
@Override
public String getMetaType() {
throw new UnsupportedOperationException();
}
@Override
public String getPropertyName() {
return methodName;
}
String parameterList() {
return paramTypes.stream()
.map(this::strip)
.map(annotationMetaEntity::importType)
.reduce((x, y) -> x + ',' + y)
.orElse("");
}
String strip(String type) {
int index = type.indexOf("<");
String stripped = index > 0 ? type.substring(0, index) : type;
return type.endsWith("...") ? stripped + "..." : stripped;
}
void sessionParameter(StringBuilder declaration) {
if ( !belongsToDao ) {
notNull(declaration);
declaration
.append(annotationMetaEntity.importType(sessionType))
.append(' ')
.append(sessionName);
}
}
void notNull(StringBuilder declaration) {
if ( addNonnullAnnotation ) {
declaration
.append('@')
.append(annotationMetaEntity.importType("jakarta.annotation.Nonnull"))
.append(' ');
}
}
}

View File

@ -49,6 +49,7 @@ import static javax.lang.model.util.ElementFilter.fieldsIn;
import static javax.lang.model.util.ElementFilter.methodsIn; import static javax.lang.model.util.ElementFilter.methodsIn;
import static org.hibernate.jpamodelgen.annotation.QueryMethod.isOrderParam; import static org.hibernate.jpamodelgen.annotation.QueryMethod.isOrderParam;
import static org.hibernate.jpamodelgen.annotation.QueryMethod.isPageParam; import static org.hibernate.jpamodelgen.annotation.QueryMethod.isPageParam;
import static org.hibernate.jpamodelgen.util.Constants.SESSION_TYPES;
import static org.hibernate.jpamodelgen.util.NullnessUtil.castNonNull; import static org.hibernate.jpamodelgen.util.NullnessUtil.castNonNull;
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation; import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
import static org.hibernate.jpamodelgen.util.TypeUtils.determineAccessTypeForHierarchy; import static org.hibernate.jpamodelgen.util.TypeUtils.determineAccessTypeForHierarchy;
@ -494,7 +495,11 @@ public class AnnotationMetaEntity extends AnnotationMeta {
createCriteriaFinder( method, returnType, containerType, entity ); createCriteriaFinder( method, returnType, containerType, entity );
} }
else { else {
switch ( method.getParameters().size() ) { final long parameterCount =
method.getParameters().stream()
.filter(AnnotationMetaEntity::isFinderParameterMappingToAttribute)
.count();
switch ( (int) parameterCount ) {
case 0: case 0:
context.message( method, "missing parameter", Diagnostic.Kind.ERROR ); context.message( method, "missing parameter", Diagnostic.Kind.ERROR );
break; break;
@ -517,9 +522,13 @@ public class AnnotationMetaEntity extends AnnotationMeta {
final String methodName = method.getSimpleName().toString(); final String methodName = method.getSimpleName().toString();
final List<String> paramNames = parameterNames(method); final List<String> paramNames = parameterNames(method);
final List<String> paramTypes = parameterTypes(method); final List<String> paramTypes = parameterTypes(method);
final String[] sessionType = sessionTypeFromParameters( paramNames, paramTypes );
removeSessionFromParameters(paramNames, paramTypes);
final String methodKey = methodName + paramTypes; final String methodKey = methodName + paramTypes;
for ( VariableElement param : method.getParameters() ) { for ( VariableElement param : method.getParameters() ) {
validateFinderParameter(entity, param ); if ( isFinderParameterMappingToAttribute( param ) ) {
validateFinderParameter( entity, param );
}
} }
putMember( methodKey, putMember( methodKey,
new CriteriaFinderMethod( new CriteriaFinderMethod(
@ -531,13 +540,40 @@ public class AnnotationMetaEntity extends AnnotationMeta {
paramTypes, paramTypes,
false, false,
dao, dao,
sessionType, sessionType[0],
sessionType[1],
enabledFetchProfiles( method ), enabledFetchProfiles( method ),
context.addNonnullAnnotation() context.addNonnullAnnotation()
) )
); );
} }
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;
}
}
}
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) ) {
return new String[] { type, name };
}
}
return new String[] { sessionType, "entityManager" };
}
private static List<String> enabledFetchProfiles(ExecutableElement method) { private static List<String> enabledFetchProfiles(ExecutableElement method) {
final Object enabledFetchProfiles = final Object enabledFetchProfiles =
getAnnotationValue( castNonNull( getAnnotationMirror( method, Constants.FIND ) ), getAnnotationValue( castNonNull( getAnnotationMirror( method, Constants.FIND ) ),
@ -560,8 +596,10 @@ public class AnnotationMetaEntity extends AnnotationMeta {
final String methodName = method.getSimpleName().toString(); final String methodName = method.getSimpleName().toString();
final List<String> paramNames = parameterNames( method ); final List<String> paramNames = parameterNames( method );
final List<String> paramTypes = parameterTypes( method ); final List<String> paramTypes = parameterTypes( method );
final String[] sessionType = sessionTypeFromParameters( paramNames, paramTypes );
removeSessionFromParameters( paramNames, paramTypes );
final String methodKey = methodName + paramTypes; final String methodKey = methodName + paramTypes;
if ( !usingStatelessSession() // no byNaturalId() lookup API for SS if ( !usingStatelessSession(sessionType[0]) // no byNaturalId() lookup API for SS
&& matchesNaturalKey( method, entity ) ) { && matchesNaturalKey( method, entity ) ) {
putMember( methodKey, putMember( methodKey,
new NaturalIdFinderMethod( new NaturalIdFinderMethod(
@ -571,7 +609,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
paramNames, paramNames,
paramTypes, paramTypes,
dao, dao,
sessionType, sessionType[0],
sessionType[1],
enabledFetchProfiles( method ), enabledFetchProfiles( method ),
context.addNonnullAnnotation() context.addNonnullAnnotation()
) )
@ -588,7 +627,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
paramTypes, paramTypes,
false, false,
dao, dao,
sessionType, sessionType[0],
sessionType[1],
enabledFetchProfiles( method ), enabledFetchProfiles( method ),
context.addNonnullAnnotation() context.addNonnullAnnotation()
) )
@ -598,22 +638,30 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private void createSingleParameterFinder(ExecutableElement method, TypeMirror returnType, TypeElement entity) { private void createSingleParameterFinder(ExecutableElement method, TypeMirror returnType, TypeElement entity) {
final String methodName = method.getSimpleName().toString(); final String methodName = method.getSimpleName().toString();
final VariableElement parameter = method.getParameters().get(0); final VariableElement parameter =
method.getParameters().stream()
.filter(AnnotationMetaEntity::isFinderParameterMappingToAttribute)
.findFirst().orElseThrow();
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 ); final FieldType fieldType = validateFinderParameter( entity, parameter );
if ( fieldType != null ) { if ( fieldType != null ) {
final String methodKey = methodName + "!"; final String methodKey = methodName + "!";
final List<String> profiles = enabledFetchProfiles( method ); final List<String> profiles = enabledFetchProfiles( method );
switch ( pickStrategy( fieldType, profiles ) ) { switch ( pickStrategy( fieldType, sessionType[0], profiles ) ) {
case ID: case ID:
putMember( methodKey, putMember( methodKey,
new IdFinderMethod( new IdFinderMethod(
this, this,
methodName, methodName,
returnType.toString(), returnType.toString(),
parameter.getSimpleName().toString(), paramNames.get(0),
parameter.asType().toString(), paramTypes.get(0),
dao, dao,
sessionType, sessionType[0],
sessionType[1],
profiles, profiles,
context.addNonnullAnnotation() context.addNonnullAnnotation()
) )
@ -625,10 +673,11 @@ public class AnnotationMetaEntity extends AnnotationMeta {
this, this,
methodName, methodName,
returnType.toString(), returnType.toString(),
List.of( parameter.getSimpleName().toString() ), paramNames,
List.of( parameter.asType().toString() ), paramTypes,
dao, dao,
sessionType, sessionType[0],
sessionType[1],
profiles, profiles,
context.addNonnullAnnotation() context.addNonnullAnnotation()
) )
@ -641,11 +690,12 @@ public class AnnotationMetaEntity extends AnnotationMeta {
methodName, methodName,
returnType.toString(), returnType.toString(),
null, null,
List.of( parameter.getSimpleName().toString() ), paramNames,
List.of( parameter.asType().toString() ), paramTypes,
fieldType == FieldType.ID, fieldType == FieldType.ID,
dao, dao,
sessionType, sessionType[0],
sessionType[1],
profiles, profiles,
context.addNonnullAnnotation() context.addNonnullAnnotation()
) )
@ -655,16 +705,16 @@ public class AnnotationMetaEntity extends AnnotationMeta {
} }
} }
private FieldType pickStrategy(FieldType fieldType, List<String> profiles) { private FieldType pickStrategy(FieldType fieldType, String sessionType, List<String> profiles) {
switch (fieldType) { switch (fieldType) {
case ID: case ID:
// no byId() API for SS or M.S, only get() // no byId() API for SS or M.S, only get()
return (usingStatelessSession() || usingReactiveSession()) && !profiles.isEmpty() return (usingStatelessSession(sessionType) || usingReactiveSession(sessionType)) && !profiles.isEmpty()
? FieldType.BASIC : FieldType.ID; ? FieldType.BASIC : FieldType.ID;
case NATURAL_ID: case NATURAL_ID:
// no byNaturalId() lookup API for SS // no byNaturalId() lookup API for SS
// no byNaturalId() in M.S, but we do have Identifier workaround // no byNaturalId() in M.S, but we do have Identifier workaround
return usingStatelessSession() || (usingReactiveSession() && !profiles.isEmpty()) return usingStatelessSession(sessionType) || (usingReactiveSession(sessionType) && !profiles.isEmpty())
? FieldType.BASIC : FieldType.NATURAL_ID; ? FieldType.BASIC : FieldType.NATURAL_ID;
default: default:
return FieldType.BASIC; return FieldType.BASIC;
@ -673,12 +723,14 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private boolean matchesNaturalKey(ExecutableElement method, TypeElement entity) { private boolean matchesNaturalKey(ExecutableElement method, TypeElement entity) {
boolean result = true; boolean result = true;
List<? extends VariableElement> parameters = method.getParameters(); final List<? extends VariableElement> parameters = method.getParameters();
for ( VariableElement param : parameters) { for ( VariableElement param : parameters ) {
if ( validateFinderParameter( entity, param ) != FieldType.NATURAL_ID ) { if ( isFinderParameterMappingToAttribute( param ) ) {
// no short-circuit here because we want to validate if ( validateFinderParameter( entity, param ) != FieldType.NATURAL_ID ) {
// all of them and get the nice error report // no short-circuit here because we want to validate
result = false; // all of them and get the nice error report
result = false;
}
} }
} }
return result && countNaturalIdFields( entity ) == parameters.size() ; return result && countNaturalIdFields( entity ) == parameters.size() ;
@ -781,7 +833,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
final String hql = (String) query; final String hql = (String) query;
final List<String> paramNames = parameterNames( method ); final List<String> paramNames = parameterNames( method );
final List<String> paramTypes = parameterTypes( method ); final List<String> paramTypes = parameterTypes( method );
final String[] sessionType = sessionTypeFromParameters( paramNames, paramTypes );
removeSessionFromParameters(paramNames, paramTypes);
final QueryMethod attribute = final QueryMethod attribute =
new QueryMethod( new QueryMethod(
this, this,
@ -793,7 +846,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
paramTypes, paramTypes,
isNative, isNative,
dao, dao,
sessionType, sessionType[0],
sessionType[1],
context.addNonnullAnnotation() context.addNonnullAnnotation()
); );
putMember( attribute.getPropertyName() + paramTypes, attribute ); putMember( attribute.getPropertyName() + paramTypes, attribute );
@ -923,11 +977,11 @@ public class AnnotationMetaEntity extends AnnotationMeta {
&& !isOrderParam(type); && !isOrderParam(type);
} }
private boolean usingReactiveSession() { private boolean usingReactiveSession(String sessionType) {
return Constants.MUTINY_SESSION.equals(sessionType); return Constants.MUTINY_SESSION.equals(sessionType);
} }
private boolean usingStatelessSession() { private boolean usingStatelessSession(String sessionType) {
return Constants.HIB_STATELESS_SESSION.equals(sessionType); return Constants.HIB_STATELESS_SESSION.equals(sessionType);
} }
} }

View File

@ -29,9 +29,10 @@ public class CriteriaFinderMethod extends AbstractFinderMethod {
boolean isId, boolean isId,
boolean belongsToDao, boolean belongsToDao,
String sessionType, String sessionType,
String sessionName,
List<String> fetchProfiles, List<String> fetchProfiles,
boolean addNonnullAnnotation) { boolean addNonnullAnnotation) {
super( annotationMetaEntity, methodName, entity, belongsToDao, sessionType, fetchProfiles, super( annotationMetaEntity, methodName, entity, belongsToDao, sessionType, sessionName, fetchProfiles,
paramNames, paramTypes, addNonnullAnnotation ); paramNames, paramTypes, addNonnullAnnotation );
this.containerType = containerType; this.containerType = containerType;
this.isId = isId; this.isId = isId;
@ -61,7 +62,8 @@ public class CriteriaFinderMethod extends AbstractFinderMethod {
.append(" == null) throw new IllegalArgumentException(\"Null identifier\");"); .append(" == null) throw new IllegalArgumentException(\"Null identifier\");");
} }
declaration declaration
.append("\n\tvar builder = entityManager") .append("\n\tvar builder = ")
.append(sessionName)
.append(usingEntityManager .append(usingEntityManager
? ".getEntityManagerFactory()" ? ".getEntityManagerFactory()"
: ".getFactory()") : ".getFactory()")
@ -104,7 +106,9 @@ public class CriteriaFinderMethod extends AbstractFinderMethod {
} }
declaration declaration
.append("\n\t);") .append("\n\t);")
.append("\n\treturn entityManager.createQuery(query)"); .append("\n\treturn ")
.append(sessionName)
.append(".createQuery(query)");
final boolean hasEnabledFetchProfiles = !fetchProfiles.isEmpty(); 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 = final boolean unwrap =

View File

@ -25,9 +25,10 @@ public class IdFinderMethod extends AbstractFinderMethod {
String paramName, String paramType, String paramName, String paramType,
boolean belongsToDao, boolean belongsToDao,
String sessionType, String sessionType,
String sessionName,
List<String> fetchProfiles, List<String> fetchProfiles,
boolean addNonnullAnnotation) { boolean addNonnullAnnotation) {
super( annotationMetaEntity, methodName, entity, belongsToDao, sessionType, fetchProfiles, super( annotationMetaEntity, methodName, entity, belongsToDao, sessionType, sessionName, fetchProfiles,
List.of(paramName), List.of(paramType), addNonnullAnnotation ); List.of(paramName), List.of(paramType), addNonnullAnnotation );
this.paramName = paramName; this.paramName = paramName;
usingStatelessSession = Constants.HIB_STATELESS_SESSION.equals(sessionType); usingStatelessSession = Constants.HIB_STATELESS_SESSION.equals(sessionType);

View File

@ -21,9 +21,10 @@ public class NaturalIdFinderMethod extends AbstractFinderMethod {
List<String> paramNames, List<String> paramTypes, List<String> paramNames, List<String> paramTypes,
boolean belongsToDao, boolean belongsToDao,
String sessionType, String sessionType,
String sessionName,
List<String> fetchProfiles, List<String> fetchProfiles,
boolean addNonnullAnnotation) { boolean addNonnullAnnotation) {
super( annotationMetaEntity, methodName, entity, belongsToDao, sessionType, fetchProfiles, super( annotationMetaEntity, methodName, entity, belongsToDao, sessionType, sessionName, fetchProfiles,
paramNames, paramTypes, addNonnullAnnotation ); paramNames, paramTypes, addNonnullAnnotation );
} }

View File

@ -8,7 +8,6 @@ package org.hibernate.jpamodelgen.annotation;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.Metamodel; import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.Constants; import org.hibernate.jpamodelgen.util.Constants;
import org.hibernate.query.Order; import org.hibernate.query.Order;
@ -22,19 +21,11 @@ import static org.hibernate.jpamodelgen.util.StringUtil.getUpperUnderscoreCaseFr
/** /**
* @author Gavin King * @author Gavin King
*/ */
public class QueryMethod implements MetaAttribute { public class QueryMethod extends AbstractQueryMethod {
private final Metamodel annotationMetaEntity;
private final String methodName;
private final String queryString; private final String queryString;
private final @Nullable String returnTypeName; private final @Nullable String returnTypeName;
private final @Nullable String containerTypeName; private final @Nullable String containerTypeName;
private final List<String> paramNames;
private final List<String> paramTypes;
private final boolean isNative; private final boolean isNative;
private final boolean belongsToDao;
private final boolean usingEntityManager;
private final boolean reactive;
private final boolean addNonnullAnnotation;
public QueryMethod( public QueryMethod(
Metamodel annotationMetaEntity, Metamodel annotationMetaEntity,
@ -49,19 +40,13 @@ public class QueryMethod implements MetaAttribute {
boolean isNative, boolean isNative,
boolean belongsToDao, boolean belongsToDao,
String sessionType, String sessionType,
String sessionName,
boolean addNonnullAnnotation) { boolean addNonnullAnnotation) {
this.annotationMetaEntity = annotationMetaEntity; super( annotationMetaEntity, methodName, paramNames, paramTypes, sessionType, sessionName, belongsToDao, addNonnullAnnotation );
this.methodName = methodName;
this.queryString = queryString; this.queryString = queryString;
this.returnTypeName = returnTypeName; this.returnTypeName = returnTypeName;
this.containerTypeName = containerTypeName; this.containerTypeName = containerTypeName;
this.paramNames = paramNames;
this.paramTypes = paramTypes;
this.isNative = isNative; this.isNative = isNative;
this.belongsToDao = belongsToDao;
this.addNonnullAnnotation = addNonnullAnnotation;
this.usingEntityManager = Constants.ENTITY_MANAGER.equals(sessionType);
this.reactive = Constants.MUTINY_SESSION.equals(sessionType);
} }
@Override @Override
@ -98,8 +83,8 @@ public class QueryMethod implements MetaAttribute {
.append(") "); .append(") ");
} }
declaration declaration
.append("entityManager.") .append(sessionName)
.append(isNative ? "createNativeQuery" : "createQuery") .append(isNative ? ".createNativeQuery" : ".createQuery")
.append("(") .append("(")
.append(getConstantName()); .append(getConstantName());
if ( returnTypeName != null ) { if ( returnTypeName != null ) {
@ -210,30 +195,22 @@ public class QueryMethod implements MetaAttribute {
} }
private void parameters(List<String> paramTypes, StringBuilder declaration) { private void parameters(List<String> paramTypes, StringBuilder declaration) {
declaration.append("("); declaration
if ( !belongsToDao ) { .append("(");
notNull( declaration ); sessionParameter( declaration );
declaration for ( int i = 0; i < paramNames.size(); i++ ) {
.append(annotationMetaEntity.importType(Constants.ENTITY_MANAGER)) if ( !belongsToDao || i > 0 ) {
.append(" entityManager");
}
for (int i = 0; i<paramNames.size(); i++ ) {
String ptype = paramTypes.get(i);
String param = paramNames.get(i);
String rptype = returnTypeName != null
? ptype.replace(returnTypeName, annotationMetaEntity.importType(returnTypeName))
: ptype;
if ( !belongsToDao || i>0 ) {
declaration declaration
.append(", "); .append(", ");
} }
final String type = paramTypes.get(i);
final String paramType = returnTypeName != null
? type.replace(returnTypeName, annotationMetaEntity.importType(returnTypeName))
: type;
declaration declaration
.append(annotationMetaEntity.importType(rptype)) .append(annotationMetaEntity.importType(paramType))
.append(" ") .append(" ")
.append(param); .append(paramNames.get(i));
} }
declaration declaration
.append(")"); .append(")");
@ -301,20 +278,6 @@ public class QueryMethod implements MetaAttribute {
} }
} }
private String parameterList() {
return paramTypes.stream()
.map(this::strip)
.map(annotationMetaEntity::importType)
.reduce((x, y) -> x + ',' + y)
.orElse("");
}
private String strip(String type) {
int index = type.indexOf("<");
String stripped = index > 0 ? type.substring(0, index) : type;
return type.endsWith("...") ? stripped + "..." : stripped;
}
static boolean isPageParam(String parameterType) { static boolean isPageParam(String parameterType) {
return Page.class.getName().equals(parameterType); return Page.class.getName().equals(parameterType);
} }
@ -359,32 +322,7 @@ public class QueryMethod implements MetaAttribute {
} }
} }
private void notNull(StringBuilder declaration) {
if ( addNonnullAnnotation ) {
declaration
.append('@')
.append(annotationMetaEntity.importType("jakarta.annotation.Nonnull"))
.append(' ');
}
}
@Override
public String getMetaType() {
throw new UnsupportedOperationException();
}
@Override
public String getPropertyName() {
return methodName;
}
@Override
public String getTypeDeclaration() { public String getTypeDeclaration() {
return Constants.QUERY; return Constants.QUERY;
} }
@Override
public Metamodel getHostingEntity() {
return annotationMetaEntity;
}
} }

View File

@ -95,6 +95,14 @@ public final class Constants {
java.util.SortedMap.class.getName(), Constants.MAP_ATTRIBUTE java.util.SortedMap.class.getName(), Constants.MAP_ATTRIBUTE
); );
public static final Set<String> SESSION_TYPES =
Set.of(
Constants.ENTITY_MANAGER,
Constants.HIB_SESSION,
Constants.HIB_STATELESS_SESSION,
Constants.MUTINY_SESSION
);
//TODO: this is not even an exhaustive list of built-in basic types //TODO: this is not even an exhaustive list of built-in basic types
// so any logic that relies on incomplete this list is broken! // so any logic that relies on incomplete this list is broken!
public static final Set<String> BASIC_TYPES = Set.of( public static final Set<String> BASIC_TYPES = Set.of(

View File

@ -0,0 +1,29 @@
package org.hibernate.jpamodelgen.test.hqlsql;
import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import org.hibernate.Session;
import org.hibernate.StatelessSession;
import org.hibernate.annotations.processing.Find;
import org.hibernate.annotations.processing.HQL;
import java.util.List;
public abstract class Books {
@Find
abstract Book getBook(EntityManager entityManager, String isbn);
@Find
abstract Book getBook(StatelessSession session, String title, String isbn);
@Find
abstract Book getBookByNaturalKey(Session session, String authorName, String title);
@HQL("from Book where title like ?1")
abstract TypedQuery<Book> findByTitle(EntityManager entityManager, String title);
@HQL("from Book where title like ?1 order by title fetch first ?2 rows only")
abstract List<Book> findFirstNByTitle(Session session, String title, int N);
}

View File

@ -18,10 +18,12 @@ import static org.hibernate.jpamodelgen.test.util.TestUtil.assertMetamodelClassG
*/ */
public class QueryMethodTest extends CompilationTest { public class QueryMethodTest extends CompilationTest {
@Test @Test
@WithClasses({ Book.class, Dao.class }) @WithClasses({ Book.class, Dao.class, Books.class })
public void testQueryMethod() { public void testQueryMethod() {
System.out.println( TestUtil.getMetaModelSourceAsString( Dao.class ) ); System.out.println( TestUtil.getMetaModelSourceAsString( Dao.class ) );
System.out.println( TestUtil.getMetaModelSourceAsString( Books.class ) );
assertMetamodelClassGeneratedFor( Book.class ); assertMetamodelClassGeneratedFor( Book.class );
assertMetamodelClassGeneratedFor( Dao.class ); assertMetamodelClassGeneratedFor( Dao.class );
assertMetamodelClassGeneratedFor( Books.class );
} }
} }