HHH-16633 support mutation query methods

This commit is contained in:
Gavin King 2023-07-16 17:42:16 +02:00
parent 4d005b3d19
commit 0918791f47
2 changed files with 95 additions and 45 deletions

View File

@ -417,7 +417,11 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private void addQueryMethod(ExecutableElement method) { private void addQueryMethod(ExecutableElement method) {
final TypeMirror returnType = method.getReturnType(); final TypeMirror returnType = method.getReturnType();
if ( returnType.getKind() == TypeKind.DECLARED ) { final TypeKind kind = returnType.getKind();
if ( kind == TypeKind.VOID || kind.isPrimitive() ) {
addQueryMethod( method, returnType, null );
}
else if ( kind == TypeKind.DECLARED ) {
final DeclaredType declaredType = ununi((DeclaredType) returnType); final DeclaredType declaredType = ununi((DeclaredType) returnType);
final TypeElement typeElement = (TypeElement) declaredType.asElement(); final TypeElement typeElement = (TypeElement) declaredType.asElement();
final List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments(); final List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
@ -925,6 +929,10 @@ public class AnnotationMetaEntity extends AnnotationMeta {
containerType == null ? null : containerType.getQualifiedName().toString(), containerType == null ? null : containerType.getQualifiedName().toString(),
paramNames, paramNames,
paramTypes, paramTypes,
// update/delete/insert query methods must return int or void
returnType != null
&& returnType.getKind() != TypeKind.DECLARED
&& returnType.getKind() != TypeKind.ARRAY,
isNative, isNative,
dao, dao,
sessionType[0], sessionType[0],
@ -959,45 +967,11 @@ public class AnnotationMetaEntity extends AnnotationMeta {
ProcessorSessionFactory.create( context.getProcessingEnvironment() ) ProcessorSessionFactory.create( context.getProcessingEnvironment() )
); );
if ( statement != null ) { if ( statement != null ) {
if ( statement instanceof SqmSelectStatement && returnType != null ) { if ( statement instanceof SqmSelectStatement ) {
final SqmSelectStatement<?> select = (SqmSelectStatement<?>) statement; validateSelectHql( method, returnType, mirror, value, (SqmSelectStatement<?>) statement );
final JpaSelection<?> selection = select.getSelection(); }
boolean returnTypeCorrect; else {
if ( selection.isCompoundSelection() ) { validateUpdateHql( method, returnType, mirror, value );
switch ( returnType.getKind() ) {
case ARRAY:
returnTypeCorrect = checkReturnedArrayType((ArrayType) returnType);
break;
case DECLARED:
if ( !checkConstructorReturn( (DeclaredType) returnType, selection ) ) {
context.message(method, mirror, value,
"return type '" + returnType
+ "' of method has no constructor matching query selection list",
Diagnostic.Kind.ERROR);
}
returnTypeCorrect = true;
break;
default:
returnTypeCorrect = false;
}
}
else if ( selection instanceof JpaEntityJoin ) {
final JpaEntityJoin<?> from = (JpaEntityJoin<?>) selection;
returnTypeCorrect = checkReturnedEntity( from.getModel(), returnType );
}
else if ( selection instanceof JpaRoot ) {
final JpaRoot<?> from = (JpaRoot<?>) selection;
returnTypeCorrect = checkReturnedEntity( from.getModel(), returnType );
}
else {
// TODO: anything more we can do here? e.g. check constructor
returnTypeCorrect = true;
}
if ( !returnTypeCorrect ) {
context.message(method, mirror, value,
"return type of query did not match return type '" + returnType + "' of method",
Diagnostic.Kind.ERROR);
}
} }
for ( SqmParameter<?> param : statement.getSqmParameters() ) { for ( SqmParameter<?> param : statement.getSqmParameters() ) {
checkParameter( param, paramNames, paramTypes, method, mirror, value); checkParameter( param, paramNames, paramTypes, method, mirror, value);
@ -1005,6 +979,67 @@ public class AnnotationMetaEntity extends AnnotationMeta {
} }
} }
private void validateUpdateHql(
ExecutableElement method,
@Nullable TypeMirror returnType,
AnnotationMirror mirror,
AnnotationValue value) {
if ( returnType == null
|| returnType.getKind() != TypeKind.VOID
&& returnType.getKind() != TypeKind.INT ) {
context.message( method, mirror, value,
"return type of mutation query method must be 'int' or 'void'",
Diagnostic.Kind.ERROR );
}
}
private void validateSelectHql(
ExecutableElement method,
@Nullable TypeMirror returnType,
AnnotationMirror mirror,
AnnotationValue value,
SqmSelectStatement<?> statement) {
if ( returnType != null ) {
final JpaSelection<?> selection = statement.getSelection();
boolean returnTypeCorrect;
if ( selection.isCompoundSelection() ) {
switch ( returnType.getKind() ) {
case ARRAY:
returnTypeCorrect = checkReturnedArrayType( (ArrayType) returnType );
break;
case DECLARED:
if ( !checkConstructorReturn( (DeclaredType) returnType, selection ) ) {
context.message(method, mirror, value,
"return type '" + returnType
+ "' of method has no constructor matching query selection list",
Diagnostic.Kind.ERROR);
}
returnTypeCorrect = true;
break;
default:
returnTypeCorrect = false;
}
}
else if ( selection instanceof JpaEntityJoin ) {
final JpaEntityJoin<?> from = (JpaEntityJoin<?>) selection;
returnTypeCorrect = checkReturnedEntity( from.getModel(), returnType );
}
else if ( selection instanceof JpaRoot ) {
final JpaRoot<?> from = (JpaRoot<?>) selection;
returnTypeCorrect = checkReturnedEntity( from.getModel(), returnType );
}
else {
// TODO: anything more we can do here? e.g. check constructor
returnTypeCorrect = true;
}
if ( !returnTypeCorrect ) {
context.message(method, mirror, value,
"return type of query did not match return type '" + returnType + "' of method",
Diagnostic.Kind.ERROR);
}
}
}
private static boolean checkConstructorReturn(DeclaredType returnType, JpaSelection<?> selection) { private static boolean checkConstructorReturn(DeclaredType returnType, JpaSelection<?> selection) {
final List<? extends JpaSelection<?>> selectionItems = selection.getSelectionItems(); final List<? extends JpaSelection<?>> selectionItems = selection.getSelectionItems();
if ( selectionItems == null ) { if ( selectionItems == null ) {

View File

@ -24,6 +24,7 @@ public class QueryMethod extends AbstractQueryMethod {
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 boolean isUpdate;
private final boolean isNative; private final boolean isNative;
public QueryMethod( public QueryMethod(
@ -36,6 +37,7 @@ public class QueryMethod extends AbstractQueryMethod {
String containerTypeName, String containerTypeName,
List<String> paramNames, List<String> paramNames,
List<String> paramTypes, List<String> paramTypes,
boolean isUpdate,
boolean isNative, boolean isNative,
boolean belongsToDao, boolean belongsToDao,
String sessionType, String sessionType,
@ -49,6 +51,7 @@ public class QueryMethod extends AbstractQueryMethod {
this.queryString = queryString; this.queryString = queryString;
this.returnTypeName = returnTypeName; this.returnTypeName = returnTypeName;
this.containerTypeName = containerTypeName; this.containerTypeName = containerTypeName;
this.isUpdate = isUpdate;
this.isNative = isNative; this.isNative = isNative;
} }
@ -81,7 +84,11 @@ public class QueryMethod extends AbstractQueryMethod {
parameters( paramTypes, declaration ); parameters( paramTypes, declaration );
declaration declaration
.append(" {") .append(" {")
.append("\n\treturn "); .append("\n\t");
if ( returnTypeName == null || !returnTypeName.equals("void") ) {
declaration
.append("return ");
}
if ( isNative && returnTypeName != null && containerTypeName == null if ( isNative && returnTypeName != null && containerTypeName == null
&& isUsingEntityManager() ) { && isUsingEntityManager() ) {
// EntityManager.createNativeQuery() does not return TypedQuery, // EntityManager.createNativeQuery() does not return TypedQuery,
@ -95,7 +102,7 @@ public class QueryMethod extends AbstractQueryMethod {
.append(isNative ? ".createNativeQuery" : ".createQuery") .append(isNative ? ".createNativeQuery" : ".createQuery")
.append("(") .append("(")
.append(getConstantName()); .append(getConstantName());
if ( returnTypeName != null ) { if ( returnTypeName != null && !isUpdate ) {
declaration declaration
.append(", ") .append(", ")
.append(annotationMetaEntity.importType(returnTypeName)) .append(annotationMetaEntity.importType(returnTypeName))
@ -103,7 +110,17 @@ public class QueryMethod extends AbstractQueryMethod {
} }
declaration.append(")"); declaration.append(")");
boolean unwrapped = setParameters( paramTypes, declaration ); boolean unwrapped = setParameters( paramTypes, declaration );
if ( containerTypeName == null) { execute( declaration, unwrapped );
declaration.append(";\n}");
return declaration.toString();
}
private void execute(StringBuilder declaration, boolean unwrapped) {
if ( isUpdate ) {
declaration
.append("\n\t\t\t.executeUpdate()");
}
else if ( containerTypeName == null) {
declaration declaration
.append("\n\t\t\t.getSingleResult()"); .append("\n\t\t\t.getSingleResult()");
} }
@ -122,8 +139,6 @@ public class QueryMethod extends AbstractQueryMethod {
} }
} }
declaration.append(";\n}");
return declaration.toString();
} }
private boolean setParameters(List<String> paramTypes, StringBuilder declaration) { private boolean setParameters(List<String> paramTypes, StringBuilder declaration) {