support optional 'from' in @HQL query method generation
(as desired by Stef)
This commit is contained in:
parent
8794f86ad2
commit
6c435b02c9
|
@ -54,6 +54,7 @@ import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
|
|||
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
|
||||
import org.hibernate.metamodel.model.domain.internal.AnyDiscriminatorSqmPath;
|
||||
import org.hibernate.metamodel.model.domain.internal.EntitySqmPathSource;
|
||||
import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor;
|
||||
import org.hibernate.query.NullPrecedence;
|
||||
import org.hibernate.query.ParameterLabelException;
|
||||
import org.hibernate.query.PathException;
|
||||
|
@ -310,6 +311,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
}
|
||||
|
||||
private final Class<R> expectedResultType;
|
||||
private final String expectedResultEntity;
|
||||
private final SqmCreationOptions creationOptions;
|
||||
private final SqmCreationContext creationContext;
|
||||
|
||||
|
@ -336,7 +338,23 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
Class<R> expectedResultType,
|
||||
SqmCreationOptions creationOptions,
|
||||
SqmCreationContext creationContext) {
|
||||
this( expectedResultType, null, creationOptions, creationContext );
|
||||
}
|
||||
|
||||
public SemanticQueryBuilder(
|
||||
String expectedResultEntity,
|
||||
SqmCreationOptions creationOptions,
|
||||
SqmCreationContext creationContext) {
|
||||
this( null, expectedResultEntity, creationOptions, creationContext );
|
||||
}
|
||||
|
||||
private SemanticQueryBuilder(
|
||||
Class<R> expectedResultType,
|
||||
String expectedResultEntity,
|
||||
SqmCreationOptions creationOptions,
|
||||
SqmCreationContext creationContext) {
|
||||
this.expectedResultType = expectedResultType;
|
||||
this.expectedResultEntity = expectedResultEntity;
|
||||
this.creationOptions = creationOptions;
|
||||
this.creationContext = creationContext;
|
||||
this.dotIdentifierConsumerStack = new StandardStack<>(
|
||||
|
@ -1153,8 +1171,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
}
|
||||
|
||||
private SqmFromClause buildInferredFromClause(HqlParser.SelectClauseContext selectClauseContext) {
|
||||
if ( selectClauseContext != null ) {
|
||||
// when there's an explicit 'select', we never infer the 'from'
|
||||
if ( selectClauseContext != null || processingStateStack.depth() > 1 ) {
|
||||
// when there's an explicit 'select', or in a subquery, we never infer the 'from'
|
||||
return new SqmFromClause();
|
||||
}
|
||||
else {
|
||||
|
@ -1166,13 +1184,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
}
|
||||
|
||||
final SqmFromClause fromClause = new SqmFromClause();
|
||||
if ( expectedResultType != null && processingStateStack.depth() <= 1 ) {
|
||||
final EntityDomainType<R> entityDescriptor =
|
||||
creationContext.getJpaMetamodel().findEntityType( expectedResultType );
|
||||
if ( entityDescriptor == null ) {
|
||||
throw new SemanticException( "Query has no 'from' clause, and the result type '"
|
||||
+ expectedResultType.getName() + "' is not an entity type" );
|
||||
}
|
||||
final EntityDomainType<R> entityDescriptor = getResultEntity();
|
||||
if ( entityDescriptor != null ) {
|
||||
final SqmRoot<R> sqmRoot =
|
||||
new SqmRoot<>( entityDescriptor, null, false, creationContext.getNodeBuilder() );
|
||||
processingStateStack.getCurrent().getPathRegistry().register( sqmRoot );
|
||||
|
@ -1182,6 +1195,29 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
}
|
||||
}
|
||||
|
||||
private EntityDomainType<R> getResultEntity() {
|
||||
final JpaMetamodelImplementor jpaMetamodel = creationContext.getJpaMetamodel();
|
||||
if ( expectedResultEntity != null ) {
|
||||
final EntityDomainType<R> entityDescriptor = jpaMetamodel.entity( expectedResultEntity );
|
||||
if ( entityDescriptor == null ) {
|
||||
throw new SemanticException("Query has no 'from' clause, and the result type '"
|
||||
+ expectedResultEntity + "' is not an entity type");
|
||||
}
|
||||
return entityDescriptor;
|
||||
}
|
||||
else if ( expectedResultType != null ) {
|
||||
final EntityDomainType<R> entityDescriptor = jpaMetamodel.findEntityType( expectedResultType );
|
||||
if ( entityDescriptor == null ) {
|
||||
throw new SemanticException("Query has no 'from' clause, and the result type '"
|
||||
+ expectedResultType.getSimpleName() + "' is not an entity type");
|
||||
}
|
||||
return entityDescriptor;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected SqmSelectClause buildInferredSelectClause(SqmFromClause fromClause) {
|
||||
if ( creationOptions.useStrictJpaCompliance() ) {
|
||||
throw new StrictJpaComplianceViolation(
|
||||
|
|
|
@ -94,6 +94,7 @@ public abstract class AnnotationMeta implements Metamodel {
|
|||
final SqmStatement<?> statement =
|
||||
Validation.validate(
|
||||
hql,
|
||||
null,
|
||||
true,
|
||||
// If we are in the scope of @CheckHQL, semantic errors in the
|
||||
// query result in compilation errors. Otherwise, they only
|
||||
|
|
|
@ -965,6 +965,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
final SqmStatement<?> statement =
|
||||
Validation.validate(
|
||||
hql,
|
||||
returnType,
|
||||
true,
|
||||
new ErrorHandler( context, method, mirror, value, hql),
|
||||
ProcessorSessionFactory.create( context.getProcessingEnvironment() )
|
||||
|
|
|
@ -515,13 +515,13 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static boolean isMappedClass(TypeElement type) {
|
||||
static boolean isMappedClass(TypeElement type) {
|
||||
return hasAnnotation(type, "Entity")
|
||||
|| hasAnnotation(type, "Embeddable")
|
||||
|| hasAnnotation(type, "MappedSuperclass");
|
||||
}
|
||||
|
||||
private static boolean isEntity(TypeElement member) {
|
||||
static boolean isEntity(TypeElement member) {
|
||||
return member.getKind() == ElementKind.CLASS
|
||||
// && member.getAnnotation(entityAnnotation)!=null;
|
||||
&& hasAnnotation(member, "Entity");
|
||||
|
@ -668,7 +668,7 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory {
|
|||
}
|
||||
}
|
||||
|
||||
private static String getEntityName(TypeElement type) {
|
||||
static String getEntityName(TypeElement type) {
|
||||
if ( type == null ) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,14 @@ import org.hibernate.query.sqm.TerminalPathException;
|
|||
import org.hibernate.query.sqm.tree.SqmStatement;
|
||||
import org.hibernate.type.descriptor.java.spi.JdbcTypeRecommendationException;
|
||||
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
import static org.hibernate.jpamodelgen.validation.ProcessorSessionFactory.getEntityName;
|
||||
import static org.hibernate.jpamodelgen.validation.ProcessorSessionFactory.isEntity;
|
||||
|
||||
|
||||
/**
|
||||
* The entry point for HQL validation.
|
||||
|
@ -42,14 +50,16 @@ public class Validation {
|
|||
|
||||
public static @Nullable SqmStatement<?> validate(
|
||||
String hql,
|
||||
@Nullable TypeMirror returnType,
|
||||
boolean checkTyping,
|
||||
Handler handler,
|
||||
SessionFactoryImplementor factory) {
|
||||
return validate( hql, checkTyping, handler, factory, 0 );
|
||||
return validate( hql, returnType, checkTyping, handler, factory, 0 );
|
||||
}
|
||||
|
||||
public static @Nullable SqmStatement<?> validate(
|
||||
String hql,
|
||||
@Nullable TypeMirror returnType,
|
||||
boolean checkTyping,
|
||||
Handler handler,
|
||||
SessionFactoryImplementor factory,
|
||||
|
@ -57,7 +67,7 @@ public class Validation {
|
|||
try {
|
||||
final HqlParser.StatementContext statementContext = parseAndCheckSyntax( hql, handler );
|
||||
if ( checkTyping && handler.getErrorCount() == 0 ) {
|
||||
return checkTyping( hql, handler, factory, errorOffset, statementContext );
|
||||
return checkTyping( hql, returnType, handler, factory, errorOffset, statementContext );
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
@ -68,13 +78,13 @@ public class Validation {
|
|||
|
||||
private static @Nullable SqmStatement<?> checkTyping(
|
||||
String hql,
|
||||
@Nullable TypeMirror returnType,
|
||||
Handler handler,
|
||||
SessionFactoryImplementor factory,
|
||||
int errorOffset,
|
||||
HqlParser.StatementContext statementContext) {
|
||||
try {
|
||||
return new SemanticQueryBuilder<>( Object[].class, () -> false, factory )
|
||||
.visitStatement( statementContext );
|
||||
return createSemanticQueryBuilder( returnType, factory ).visitStatement( statementContext );
|
||||
}
|
||||
catch ( JdbcTypeRecommendationException ignored ) {
|
||||
// just squash these for now
|
||||
|
@ -89,6 +99,18 @@ public class Validation {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static SemanticQueryBuilder<?> createSemanticQueryBuilder(
|
||||
@Nullable TypeMirror returnType, SessionFactoryImplementor factory) {
|
||||
if ( returnType != null && returnType.getKind() == TypeKind.DECLARED ) {
|
||||
final DeclaredType declaredType = (DeclaredType) returnType;
|
||||
final TypeElement typeElement = (TypeElement) declaredType.asElement();
|
||||
if ( isEntity( typeElement ) ) {
|
||||
return new SemanticQueryBuilder<>( getEntityName( typeElement ), () -> false, factory );
|
||||
}
|
||||
}
|
||||
return new SemanticQueryBuilder<>( Object[].class, () -> false, factory );
|
||||
}
|
||||
|
||||
private static HqlParser.StatementContext parseAndCheckSyntax(String hql, Handler handler) {
|
||||
final HqlLexer hqlLexer = HqlParseTreeBuilder.INSTANCE.buildHqlLexer( hql );
|
||||
final HqlParser hqlParser = HqlParseTreeBuilder.INSTANCE.buildHqlParser( hql, hqlLexer );
|
||||
|
|
|
@ -42,6 +42,9 @@ public interface Dao {
|
|||
@Find
|
||||
SelectionQuery<Book> createBooksSelectionQuery(String title);
|
||||
|
||||
@HQL("where title like ?1")
|
||||
List<Book> findBooksByTitle(String title);
|
||||
|
||||
@HQL("from Book where title like ?1")
|
||||
TypedQuery<Book> findByTitle(String title);
|
||||
|
||||
|
|
Loading…
Reference in New Issue