spec-compliant inference of entity type in @Query
Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
parent
f5ddee44dc
commit
2955e0b91d
|
@ -561,15 +561,15 @@ public final class StringHelper {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isNotEmpty(String string) {
|
public static boolean isNotEmpty(@Nullable String string) {
|
||||||
return string != null && !string.isEmpty();
|
return string != null && !string.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isEmpty(String string) {
|
public static boolean isEmpty(@Nullable String string) {
|
||||||
return string == null || string.isEmpty();
|
return string == null || string.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isBlank(String string) {
|
public static boolean isBlank(@Nullable String string) {
|
||||||
return string == null || string.isBlank();
|
return string == null || string.isBlank();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.hibernate.processor.test.data.eg;
|
||||||
|
|
||||||
import jakarta.data.repository.CrudRepository;
|
import jakarta.data.repository.CrudRepository;
|
||||||
import jakarta.data.repository.Find;
|
import jakarta.data.repository.Find;
|
||||||
|
import jakarta.data.repository.Query;
|
||||||
import jakarta.data.repository.Repository;
|
import jakarta.data.repository.Repository;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
|
|
||||||
|
@ -12,4 +13,7 @@ public interface Bookshop extends CrudRepository<Book,String> {
|
||||||
@Find
|
@Find
|
||||||
@Transactional
|
@Transactional
|
||||||
List<Book> byPublisher(String publisher_name);
|
List<Book> byPublisher(String publisher_name);
|
||||||
|
|
||||||
|
@Query("select isbn where title like ?1 order by isbn")
|
||||||
|
String[] ssns(String title);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,10 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.processor.annotation;
|
package org.hibernate.processor.annotation;
|
||||||
|
|
||||||
|
import org.antlr.v4.runtime.Token;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.hibernate.AssertionFailure;
|
import org.hibernate.AssertionFailure;
|
||||||
|
import org.hibernate.grammars.hql.HqlLexer;
|
||||||
import org.hibernate.processor.Context;
|
import org.hibernate.processor.Context;
|
||||||
import org.hibernate.processor.ImportContextImpl;
|
import org.hibernate.processor.ImportContextImpl;
|
||||||
import org.hibernate.processor.ProcessLaterException;
|
import org.hibernate.processor.ProcessLaterException;
|
||||||
|
@ -23,6 +25,7 @@ import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
import org.hibernate.query.criteria.JpaEntityJoin;
|
import org.hibernate.query.criteria.JpaEntityJoin;
|
||||||
import org.hibernate.query.criteria.JpaRoot;
|
import org.hibernate.query.criteria.JpaRoot;
|
||||||
import org.hibernate.query.criteria.JpaSelection;
|
import org.hibernate.query.criteria.JpaSelection;
|
||||||
|
import org.hibernate.query.hql.internal.HqlParseTreeBuilder;
|
||||||
import org.hibernate.query.sql.internal.ParameterParser;
|
import org.hibernate.query.sql.internal.ParameterParser;
|
||||||
import org.hibernate.query.sql.spi.ParameterRecognizer;
|
import org.hibernate.query.sql.spi.ParameterRecognizer;
|
||||||
import org.hibernate.query.sqm.SqmExpressible;
|
import org.hibernate.query.sqm.SqmExpressible;
|
||||||
|
@ -66,6 +69,12 @@ import static java.util.Collections.emptyList;
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
import static javax.lang.model.util.ElementFilter.fieldsIn;
|
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.grammars.hql.HqlLexer.FROM;
|
||||||
|
import static org.hibernate.grammars.hql.HqlLexer.GROUP;
|
||||||
|
import static org.hibernate.grammars.hql.HqlLexer.HAVING;
|
||||||
|
import static org.hibernate.grammars.hql.HqlLexer.ORDER;
|
||||||
|
import static org.hibernate.grammars.hql.HqlLexer.WHERE;
|
||||||
|
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
|
||||||
import static org.hibernate.internal.util.StringHelper.qualify;
|
import static org.hibernate.internal.util.StringHelper.qualify;
|
||||||
import static org.hibernate.processor.annotation.AbstractQueryMethod.isSessionParameter;
|
import static org.hibernate.processor.annotation.AbstractQueryMethod.isSessionParameter;
|
||||||
import static org.hibernate.processor.annotation.AbstractQueryMethod.isSpecialParam;
|
import static org.hibernate.processor.annotation.AbstractQueryMethod.isSpecialParam;
|
||||||
|
@ -2077,13 +2086,6 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
final List<String> paramNames = parameterNames( method );
|
final List<String> paramNames = parameterNames( method );
|
||||||
final List<String> paramTypes = parameterTypes( method );
|
final List<String> paramTypes = parameterTypes( method );
|
||||||
|
|
||||||
if ( isNative ) {
|
|
||||||
validateSql( method, mirror, queryString, paramNames, value );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
validateHql( method, returnType, mirror, value, queryString, paramNames, paramTypes );
|
|
||||||
}
|
|
||||||
|
|
||||||
// now check that the query has a parameter for every method parameter
|
// now check that the query has a parameter for every method parameter
|
||||||
checkParameters( method, returnType, paramNames, paramTypes, mirror, value, queryString );
|
checkParameters( method, returnType, paramNames, paramTypes, mirror, value, queryString );
|
||||||
|
|
||||||
|
@ -2093,11 +2095,21 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
? emptyList()
|
? emptyList()
|
||||||
: orderByList( method, (TypeElement) resultType.asElement() );
|
: orderByList( method, (TypeElement) resultType.asElement() );
|
||||||
|
|
||||||
|
final String processedQuery;
|
||||||
|
if ( isNative ) {
|
||||||
|
processedQuery = queryString;
|
||||||
|
validateSql( method, mirror, processedQuery, paramNames, value );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
processedQuery = addFromClauseIfNecessary( queryString, implicitEntityName(resultType) );
|
||||||
|
validateHql( method, returnType, mirror, value, processedQuery, paramNames, paramTypes );
|
||||||
|
}
|
||||||
|
|
||||||
final QueryMethod attribute =
|
final QueryMethod attribute =
|
||||||
new QueryMethod(
|
new QueryMethod(
|
||||||
this, method,
|
this, method,
|
||||||
method.getSimpleName().toString(),
|
method.getSimpleName().toString(),
|
||||||
queryString,
|
processedQuery,
|
||||||
returnType == null ? null : returnType.toString(),
|
returnType == null ? null : returnType.toString(),
|
||||||
containerTypeName,
|
containerTypeName,
|
||||||
paramNames,
|
paramNames,
|
||||||
|
@ -2116,6 +2128,51 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private @Nullable String implicitEntityName(@Nullable DeclaredType resultType) {
|
||||||
|
if ( resultType != null && hasAnnotation(resultType.asElement(), ENTITY) ) {
|
||||||
|
final AnnotationMirror annotation =
|
||||||
|
getAnnotationMirror(resultType.asElement(), ENTITY);
|
||||||
|
if ( annotation == null ) {
|
||||||
|
throw new AssertionFailure("@Entity annotation should not be missing");
|
||||||
|
}
|
||||||
|
final String name = (String) getAnnotationValue(annotation, "name");
|
||||||
|
return isNotEmpty(name) ? resultType.asElement().getSimpleName().toString() : name;
|
||||||
|
}
|
||||||
|
else if ( primaryEntity != null ) {
|
||||||
|
return primaryEntity.getSimpleName().toString();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String addFromClauseIfNecessary(String hql, @Nullable String entityType) {
|
||||||
|
if ( entityType == null ) {
|
||||||
|
return hql;
|
||||||
|
}
|
||||||
|
else if ( isInsertUpdateDelete(hql) ) {
|
||||||
|
return hql;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final HqlLexer hqlLexer = HqlParseTreeBuilder.INSTANCE.buildHqlLexer( hql );
|
||||||
|
final List<? extends Token> allTokens = hqlLexer.getAllTokens();
|
||||||
|
for (Token token : allTokens) {
|
||||||
|
switch ( token.getType() ) {
|
||||||
|
case FROM:
|
||||||
|
return hql;
|
||||||
|
case WHERE:
|
||||||
|
case HAVING:
|
||||||
|
case GROUP:
|
||||||
|
case ORDER:
|
||||||
|
return new StringBuilder(hql)
|
||||||
|
.insert(token.getStartIndex(), "from " + entityType + " ")
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hql + " from " + entityType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private @Nullable DeclaredType resultType(
|
private @Nullable DeclaredType resultType(
|
||||||
ExecutableElement method, @Nullable TypeMirror returnType, AnnotationMirror mirror, AnnotationValue value) {
|
ExecutableElement method, @Nullable TypeMirror returnType, AnnotationMirror mirror, AnnotationValue value) {
|
||||||
if ( returnType != null && returnType.getKind() == TypeKind.DECLARED ) {
|
if ( returnType != null && returnType.getKind() == TypeKind.DECLARED ) {
|
||||||
|
|
Loading…
Reference in New Issue