From 0918791f47da561e3c0a660bc7051436d5f14d13 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 16 Jul 2023 17:42:16 +0200 Subject: [PATCH] HHH-16633 support mutation query methods --- .../annotation/AnnotationMetaEntity.java | 115 ++++++++++++------ .../jpamodelgen/annotation/QueryMethod.java | 25 +++- 2 files changed, 95 insertions(+), 45 deletions(-) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java index 744fdceed9..c5a4e5a507 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java @@ -417,7 +417,11 @@ public class AnnotationMetaEntity extends AnnotationMeta { private void addQueryMethod(ExecutableElement method) { 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 TypeElement typeElement = (TypeElement) declaredType.asElement(); final List typeArguments = declaredType.getTypeArguments(); @@ -925,6 +929,10 @@ public class AnnotationMetaEntity extends AnnotationMeta { containerType == null ? null : containerType.getQualifiedName().toString(), paramNames, paramTypes, + // update/delete/insert query methods must return int or void + returnType != null + && returnType.getKind() != TypeKind.DECLARED + && returnType.getKind() != TypeKind.ARRAY, isNative, dao, sessionType[0], @@ -959,45 +967,11 @@ public class AnnotationMetaEntity extends AnnotationMeta { ProcessorSessionFactory.create( context.getProcessingEnvironment() ) ); if ( statement != null ) { - if ( statement instanceof SqmSelectStatement && returnType != null ) { - final SqmSelectStatement select = (SqmSelectStatement) statement; - final JpaSelection selection = select.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); - } + if ( statement instanceof SqmSelectStatement ) { + validateSelectHql( method, returnType, mirror, value, (SqmSelectStatement) statement ); + } + else { + validateUpdateHql( method, returnType, mirror, value ); } for ( SqmParameter param : statement.getSqmParameters() ) { 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) { final List> selectionItems = selection.getSelectionItems(); if ( selectionItems == null ) { diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/QueryMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/QueryMethod.java index 539f60460e..b7b2680ee0 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/QueryMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/QueryMethod.java @@ -24,6 +24,7 @@ public class QueryMethod extends AbstractQueryMethod { private final String queryString; private final @Nullable String returnTypeName; private final @Nullable String containerTypeName; + private final boolean isUpdate; private final boolean isNative; public QueryMethod( @@ -36,6 +37,7 @@ public class QueryMethod extends AbstractQueryMethod { String containerTypeName, List paramNames, List paramTypes, + boolean isUpdate, boolean isNative, boolean belongsToDao, String sessionType, @@ -49,6 +51,7 @@ public class QueryMethod extends AbstractQueryMethod { this.queryString = queryString; this.returnTypeName = returnTypeName; this.containerTypeName = containerTypeName; + this.isUpdate = isUpdate; this.isNative = isNative; } @@ -81,7 +84,11 @@ public class QueryMethod extends AbstractQueryMethod { parameters( paramTypes, declaration ); declaration .append(" {") - .append("\n\treturn "); + .append("\n\t"); + if ( returnTypeName == null || !returnTypeName.equals("void") ) { + declaration + .append("return "); + } if ( isNative && returnTypeName != null && containerTypeName == null && isUsingEntityManager() ) { // EntityManager.createNativeQuery() does not return TypedQuery, @@ -95,7 +102,7 @@ public class QueryMethod extends AbstractQueryMethod { .append(isNative ? ".createNativeQuery" : ".createQuery") .append("(") .append(getConstantName()); - if ( returnTypeName != null ) { + if ( returnTypeName != null && !isUpdate ) { declaration .append(", ") .append(annotationMetaEntity.importType(returnTypeName)) @@ -103,7 +110,17 @@ public class QueryMethod extends AbstractQueryMethod { } declaration.append(")"); 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 .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 paramTypes, StringBuilder declaration) {