HHH-18066 Support de-referencing function invocation with dot and bracket syntax
This commit is contained in:
parent
940c898ecf
commit
2932933c43
|
@ -430,12 +430,17 @@ pathContinuation
|
||||||
* * VALUE( path )
|
* * VALUE( path )
|
||||||
* * KEY( path )
|
* * KEY( path )
|
||||||
* * path[ selector ]
|
* * path[ selector ]
|
||||||
|
* * ARRAY_GET( embeddableArrayPath, index ).path
|
||||||
|
* * COALESCE( array1, array2 )[ selector ].path
|
||||||
*/
|
*/
|
||||||
syntacticDomainPath
|
syntacticDomainPath
|
||||||
: treatedNavigablePath
|
: treatedNavigablePath
|
||||||
| collectionValueNavigablePath
|
| collectionValueNavigablePath
|
||||||
| mapKeyNavigablePath
|
| mapKeyNavigablePath
|
||||||
| simplePath indexedPathAccessFragment
|
| simplePath indexedPathAccessFragment
|
||||||
|
| toOneFkReference
|
||||||
|
| function pathContinuation
|
||||||
|
| function indexedPathAccessFragment pathContinuation?
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -732,7 +737,6 @@ primaryExpression
|
||||||
| entityIdReference # EntityIdExpression
|
| entityIdReference # EntityIdExpression
|
||||||
| entityVersionReference # EntityVersionExpression
|
| entityVersionReference # EntityVersionExpression
|
||||||
| entityNaturalIdReference # EntityNaturalIdExpression
|
| entityNaturalIdReference # EntityNaturalIdExpression
|
||||||
| toOneFkReference # ToOneFkExpression
|
|
||||||
| syntacticDomainPath pathContinuation? # SyntacticPathExpression
|
| syntacticDomainPath pathContinuation? # SyntacticPathExpression
|
||||||
| function # FunctionExpression
|
| function # FunctionExpression
|
||||||
| generalPathFragment # GeneralPathExpression
|
| generalPathFragment # GeneralPathExpression
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
||||||
import org.hibernate.metamodel.model.domain.DomainType;
|
import org.hibernate.metamodel.model.domain.DomainType;
|
||||||
import org.hibernate.query.ReturnableType;
|
import org.hibernate.query.ReturnableType;
|
||||||
import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver;
|
import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver;
|
||||||
|
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||||
import org.hibernate.query.sqm.tree.SqmTypedNode;
|
import org.hibernate.query.sqm.tree.SqmTypedNode;
|
||||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
import org.hibernate.type.BasicType;
|
import org.hibernate.type.BasicType;
|
||||||
|
@ -25,6 +26,8 @@ import org.hibernate.type.descriptor.jdbc.DelegatingJdbcTypeIndicators;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link FunctionReturnTypeResolver} that resolves a JSON encoded array type based on the arguments,
|
* A {@link FunctionReturnTypeResolver} that resolves a JSON encoded array type based on the arguments,
|
||||||
* which are supposed to be of the element type. The inferred type and implied type have precedence though.
|
* which are supposed to be of the element type. The inferred type and implied type have precedence though.
|
||||||
|
@ -39,16 +42,24 @@ public class JsonArrayViaElementArgumentReturnTypeResolver implements FunctionRe
|
||||||
@Override
|
@Override
|
||||||
public ReturnableType<?> resolveFunctionReturnType(
|
public ReturnableType<?> resolveFunctionReturnType(
|
||||||
ReturnableType<?> impliedType,
|
ReturnableType<?> impliedType,
|
||||||
Supplier<MappingModelExpressible<?>> inferredTypeSupplier,
|
@Nullable SqmToSqlAstConverter converter,
|
||||||
List<? extends SqmTypedNode<?>> arguments,
|
List<? extends SqmTypedNode<?>> arguments,
|
||||||
TypeConfiguration typeConfiguration) {
|
TypeConfiguration typeConfiguration) {
|
||||||
final MappingModelExpressible<?> inferredType = inferredTypeSupplier.get();
|
if ( converter != null ) {
|
||||||
if ( inferredType != null ) {
|
if ( converter.isInTypeInference() ) {
|
||||||
if ( inferredType instanceof ReturnableType<?> ) {
|
// Don't default to a Json array when in type inference mode.
|
||||||
return (ReturnableType<?>) inferredType;
|
// Comparing e.g. `array() = (select array_agg() ...)` will trigger this resolver
|
||||||
|
// while inferring the type for `array()`, which we want to avoid.
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
else if ( inferredType instanceof BasicValuedMapping ) {
|
final MappingModelExpressible<?> inferredType = converter.resolveFunctionImpliedReturnType();
|
||||||
return (ReturnableType<?>) ( (BasicValuedMapping) inferredType ).getJdbcMapping();
|
if ( inferredType != null ) {
|
||||||
|
if ( inferredType instanceof ReturnableType<?> ) {
|
||||||
|
return (ReturnableType<?>) inferredType;
|
||||||
|
}
|
||||||
|
else if ( inferredType instanceof BasicValuedMapping ) {
|
||||||
|
return (ReturnableType<?>) ( (BasicValuedMapping) inferredType ).getJdbcMapping();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( impliedType != null ) {
|
if ( impliedType != null ) {
|
||||||
|
|
|
@ -33,6 +33,8 @@ import org.hibernate.type.descriptor.java.EnumJavaType;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
|
|
||||||
import jakarta.persistence.criteria.Expression;
|
import jakarta.persistence.criteria.Expression;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,7 +43,7 @@ import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
||||||
public class FullyQualifiedReflectivePathTerminal
|
public class FullyQualifiedReflectivePathTerminal
|
||||||
extends FullyQualifiedReflectivePath
|
extends FullyQualifiedReflectivePath
|
||||||
implements SqmExpression {
|
implements SqmExpression {
|
||||||
private final SqmExpressible expressibleType;
|
private final @Nullable SqmExpressible expressibleType;
|
||||||
private final SqmCreationState creationState;
|
private final SqmCreationState creationState;
|
||||||
|
|
||||||
private final Function<SemanticQueryWalker,?> handler;
|
private final Function<SemanticQueryWalker,?> handler;
|
||||||
|
@ -136,7 +138,7 @@ public class FullyQualifiedReflectivePathTerminal
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmExpressible getNodeType() {
|
public @Nullable SqmExpressible getNodeType() {
|
||||||
return expressibleType;
|
return expressibleType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,12 +149,12 @@ public class FullyQualifiedReflectivePathTerminal
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JavaType getJavaTypeDescriptor() {
|
public JavaType getJavaTypeDescriptor() {
|
||||||
return expressibleType.getExpressibleJavaType();
|
return expressibleType == null ? null : expressibleType.getExpressibleJavaType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyInferableType(SqmExpressible type) {
|
public void applyInferableType(@Nullable SqmExpressible type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1869,7 +1869,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
part = (SemanticPathPart) ctx.pathContinuation().accept( this );
|
part = (SemanticPathPart) ctx.pathContinuation().simplePath().accept( this );
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
dotIdentifierConsumerStack.pop();
|
dotIdentifierConsumerStack.pop();
|
||||||
|
@ -2965,11 +2965,11 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
throw new FunctionArgumentException( "Argument '" + sqmPath.getNavigablePath()
|
throw new FunctionArgumentException( "Argument '" + sqmPath.getNavigablePath()
|
||||||
+ "' of 'naturalid()' function does not resolve to an entity type" );
|
+ "' of 'naturalid()' function does not resolve to an entity type" );
|
||||||
}
|
}
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
public Object visitToOneFkExpression(HqlParser.ToOneFkExpressionContext ctx) {
|
// public Object visitToOneFkExpression(HqlParser.ToOneFkExpressionContext ctx) {
|
||||||
return visitToOneFkReference( (HqlParser.ToOneFkReferenceContext) ctx.getChild( 0 ) );
|
// return visitToOneFkReference( (HqlParser.ToOneFkReferenceContext) ctx.getChild( 0 ) );
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmFkExpression<?> visitToOneFkReference(HqlParser.ToOneFkReferenceContext ctx) {
|
public SqmFkExpression<?> visitToOneFkReference(HqlParser.ToOneFkReferenceContext ctx) {
|
||||||
|
@ -2990,7 +2990,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SqmFkExpression<>( (SqmEntityValuedSimplePath<?>) sqmPath, creationContext.getNodeBuilder() );
|
return new SqmFkExpression<>( (SqmEntityValuedSimplePath<?>) sqmPath );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -5160,25 +5160,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
final HqlParser.SyntacticDomainPathContext syntacticDomainPath = ctx.syntacticDomainPath();
|
final HqlParser.SyntacticDomainPathContext syntacticDomainPath = ctx.syntacticDomainPath();
|
||||||
final HqlParser.GeneralPathFragmentContext generalPathFragment = ctx.generalPathFragment();
|
final HqlParser.GeneralPathFragmentContext generalPathFragment = ctx.generalPathFragment();
|
||||||
if ( syntacticDomainPath != null ) {
|
if ( syntacticDomainPath != null ) {
|
||||||
final SemanticPathPart syntacticNavigablePathResult =
|
return visitPathContinuation( visitSyntacticDomainPath( syntacticDomainPath ), ctx.pathContinuation() );
|
||||||
visitSyntacticDomainPath(syntacticDomainPath);
|
|
||||||
final HqlParser.PathContinuationContext pathContinuation = ctx.pathContinuation();
|
|
||||||
if ( pathContinuation != null ) {
|
|
||||||
dotIdentifierConsumerStack.push(
|
|
||||||
new BasicDotIdentifierConsumer( syntacticNavigablePathResult, this ) {
|
|
||||||
@Override
|
|
||||||
protected void reset() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
return (SemanticPathPart) pathContinuation.accept( this );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
dotIdentifierConsumerStack.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return syntacticNavigablePathResult;
|
|
||||||
}
|
}
|
||||||
else if (generalPathFragment != null) {
|
else if (generalPathFragment != null) {
|
||||||
return (SemanticPathPart) generalPathFragment.accept(this);
|
return (SemanticPathPart) generalPathFragment.accept(this);
|
||||||
|
@ -5190,7 +5172,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SemanticPathPart visitGeneralPathFragment(HqlParser.GeneralPathFragmentContext ctx) {
|
public SemanticPathPart visitGeneralPathFragment(HqlParser.GeneralPathFragmentContext ctx) {
|
||||||
return visitIndexedPathAccessFragment( ctx.simplePath(), ctx.indexedPathAccessFragment() );
|
return visitIndexedPathAccessFragment( visitSimplePath( ctx.simplePath() ), ctx.indexedPathAccessFragment() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -5204,8 +5186,20 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
else if ( ctx.mapKeyNavigablePath() != null ) {
|
else if ( ctx.mapKeyNavigablePath() != null ) {
|
||||||
return visitMapKeyNavigablePath( ctx.mapKeyNavigablePath() );
|
return visitMapKeyNavigablePath( ctx.mapKeyNavigablePath() );
|
||||||
}
|
}
|
||||||
|
else if ( ctx.toOneFkReference() != null ) {
|
||||||
|
return visitToOneFkReference( ctx.toOneFkReference() );
|
||||||
|
}
|
||||||
|
else if ( ctx.function() != null ) {
|
||||||
|
return visitPathContinuation(
|
||||||
|
visitIndexedPathAccessFragment(
|
||||||
|
(SemanticPathPart) visitFunction( ctx.function() ),
|
||||||
|
ctx.indexedPathAccessFragment()
|
||||||
|
),
|
||||||
|
ctx.pathContinuation()
|
||||||
|
);
|
||||||
|
}
|
||||||
else if ( ctx.simplePath() != null && ctx.indexedPathAccessFragment() != null ) {
|
else if ( ctx.simplePath() != null && ctx.indexedPathAccessFragment() != null ) {
|
||||||
return visitIndexedPathAccessFragment( ctx.simplePath(), ctx.indexedPathAccessFragment() );
|
return visitIndexedPathAccessFragment( visitSimplePath( ctx.simplePath() ), ctx.indexedPathAccessFragment() );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new ParsingException( "Illegal domain path '" + ctx.getText() + "'" );
|
throw new ParsingException( "Illegal domain path '" + ctx.getText() + "'" );
|
||||||
|
@ -5213,10 +5207,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
}
|
}
|
||||||
|
|
||||||
private SemanticPathPart visitIndexedPathAccessFragment(
|
private SemanticPathPart visitIndexedPathAccessFragment(
|
||||||
HqlParser.SimplePathContext ctx,
|
SemanticPathPart pathPart,
|
||||||
HqlParser.IndexedPathAccessFragmentContext idxCtx) {
|
HqlParser.IndexedPathAccessFragmentContext idxCtx) {
|
||||||
final SemanticPathPart pathPart = visitSimplePath( ctx );
|
|
||||||
|
|
||||||
if ( idxCtx == null ) {
|
if ( idxCtx == null ) {
|
||||||
return pathPart;
|
return pathPart;
|
||||||
}
|
}
|
||||||
|
@ -5243,6 +5235,27 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
return indexedPath;
|
return indexedPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SemanticPathPart visitPathContinuation(
|
||||||
|
SemanticPathPart pathPart,
|
||||||
|
HqlParser.PathContinuationContext pathContinuation) {
|
||||||
|
if ( pathContinuation == null ) {
|
||||||
|
return pathPart;
|
||||||
|
}
|
||||||
|
dotIdentifierConsumerStack.push(
|
||||||
|
new BasicDotIdentifierConsumer( pathPart, this ) {
|
||||||
|
@Override
|
||||||
|
protected void reset() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
return (SemanticPathPart) pathContinuation.simplePath().accept( this );
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
dotIdentifierConsumerStack.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SemanticPathPart visitIndexedPathAccessFragment(HqlParser.IndexedPathAccessFragmentContext idxCtx) {
|
public SemanticPathPart visitIndexedPathAccessFragment(HqlParser.IndexedPathAccessFragmentContext idxCtx) {
|
||||||
throw new UnsupportedOperationException( "Should be handled by #visitIndexedPathAccessFragment" );
|
throw new UnsupportedOperationException( "Should be handled by #visitIndexedPathAccessFragment" );
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.hibernate.query.sqm.tree.domain.SqmDerivedRoot;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmFkExpression;
|
import org.hibernate.query.sqm.tree.domain.SqmFkExpression;
|
||||||
|
import org.hibernate.query.sqm.tree.domain.SqmFunctionPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmListJoin;
|
import org.hibernate.query.sqm.tree.domain.SqmListJoin;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
||||||
|
@ -245,6 +246,8 @@ public interface SemanticQueryWalker<T> {
|
||||||
|
|
||||||
T visitIndexAggregateFunction(SqmIndexAggregateFunction<?> path);
|
T visitIndexAggregateFunction(SqmIndexAggregateFunction<?> path);
|
||||||
|
|
||||||
|
T visitFunctionPath(SqmFunctionPath<?> functionPath);
|
||||||
|
|
||||||
T visitTreatedPath(SqmTreatedPath<?, ?> sqmTreatedPath);
|
T visitTreatedPath(SqmTreatedPath<?, ?> sqmTreatedPath);
|
||||||
|
|
||||||
T visitCorrelation(SqmCorrelation<?, ?> correlation);
|
T visitCorrelation(SqmCorrelation<?, ?> correlation);
|
||||||
|
@ -411,5 +414,4 @@ public interface SemanticQueryWalker<T> {
|
||||||
T visitMapEntryFunction(SqmMapEntryReference<?, ?> function);
|
T visitMapEntryFunction(SqmMapEntryReference<?, ?> function);
|
||||||
|
|
||||||
T visitFullyQualifiedClass(Class<?> namedClass);
|
T visitFullyQualifiedClass(Class<?> namedClass);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.hibernate.query.sqm.tree.domain.SqmDerivedRoot;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmFkExpression;
|
import org.hibernate.query.sqm.tree.domain.SqmFkExpression;
|
||||||
|
import org.hibernate.query.sqm.tree.domain.SqmFunctionPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
|
import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
|
||||||
|
@ -1080,6 +1081,11 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitFunctionPath(SqmFunctionPath<?> functionPath) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitLiteral(SqmLiteral literal) {
|
public Object visitLiteral(SqmLiteral literal) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.hibernate.query.sqm.tree.domain.SqmDerivedRoot;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmFkExpression;
|
import org.hibernate.query.sqm.tree.domain.SqmFkExpression;
|
||||||
|
import org.hibernate.query.sqm.tree.domain.SqmFunctionPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
|
import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
|
||||||
|
@ -488,6 +489,12 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker<Obj
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitFunctionPath(SqmFunctionPath<?> functionPath) {
|
||||||
|
visitFunction( functionPath.getFunction() );
|
||||||
|
return functionPath;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitCorrelation(SqmCorrelation<?, ?> correlation) {
|
public Object visitCorrelation(SqmCorrelation<?, ?> correlation) {
|
||||||
return correlation;
|
return correlation;
|
||||||
|
|
|
@ -121,6 +121,7 @@ import org.hibernate.query.sqm.UnaryArithmeticOperator;
|
||||||
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||||
import org.hibernate.query.sqm.function.SelfRenderingAggregateFunctionSqlAstExpression;
|
import org.hibernate.query.sqm.function.SelfRenderingAggregateFunctionSqlAstExpression;
|
||||||
import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression;
|
import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression;
|
||||||
|
import org.hibernate.query.sqm.function.SelfRenderingSqmFunction;
|
||||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||||
import org.hibernate.query.sqm.internal.SqmMappingModelHelper;
|
import org.hibernate.query.sqm.internal.SqmMappingModelHelper;
|
||||||
import org.hibernate.query.sqm.mutation.internal.SqmInsertStrategyHelper;
|
import org.hibernate.query.sqm.mutation.internal.SqmInsertStrategyHelper;
|
||||||
|
@ -164,6 +165,7 @@ import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmFkExpression;
|
import org.hibernate.query.sqm.tree.domain.SqmFkExpression;
|
||||||
|
import org.hibernate.query.sqm.tree.domain.SqmFunctionPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmIndexAggregateFunction;
|
import org.hibernate.query.sqm.tree.domain.SqmIndexAggregateFunction;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
||||||
|
@ -326,11 +328,14 @@ import org.hibernate.sql.ast.tree.expression.UnaryOperation;
|
||||||
import org.hibernate.sql.ast.tree.expression.UnparsedNumericLiteral;
|
import org.hibernate.sql.ast.tree.expression.UnparsedNumericLiteral;
|
||||||
import org.hibernate.sql.ast.tree.from.CorrelatedPluralTableGroup;
|
import org.hibernate.sql.ast.tree.from.CorrelatedPluralTableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.CorrelatedTableGroup;
|
import org.hibernate.sql.ast.tree.from.CorrelatedTableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.EmbeddableFunctionTableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.FromClause;
|
import org.hibernate.sql.ast.tree.from.FromClause;
|
||||||
|
import org.hibernate.sql.ast.tree.from.FunctionTableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.PluralTableGroup;
|
import org.hibernate.sql.ast.tree.from.PluralTableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.QueryPartTableGroup;
|
import org.hibernate.sql.ast.tree.from.QueryPartTableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.StandardTableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
|
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
|
||||||
|
@ -391,6 +396,7 @@ import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
|
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.java.JavaTypeHelper;
|
import org.hibernate.type.descriptor.java.JavaTypeHelper;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||||
|
@ -3687,6 +3693,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
parentPath = sqmPath.getLhs();
|
parentPath = sqmPath.getLhs();
|
||||||
}
|
}
|
||||||
if ( parentPath == null ) {
|
if ( parentPath == null ) {
|
||||||
|
if ( sqmPath instanceof SqmFunctionPath<?> ) {
|
||||||
|
return visitFunctionPath( (SqmFunctionPath<?>) sqmPath );
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final TableGroup parentTableGroup = getActualTableGroup(
|
final TableGroup parentTableGroup = getActualTableGroup(
|
||||||
|
@ -4527,6 +4536,25 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
return createMinOrMaxIndexOrElement( path, true, path.getFunctionName() );
|
return createMinOrMaxIndexOrElement( path, true, path.getFunctionName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TableGroup visitFunctionPath(SqmFunctionPath<?> functionPath) {
|
||||||
|
final NavigablePath navigablePath = functionPath.getNavigablePath();
|
||||||
|
TableGroup tableGroup = getFromClauseAccess().findTableGroup( navigablePath );
|
||||||
|
if ( tableGroup == null ) {
|
||||||
|
final Expression functionExpression = (Expression) functionPath.getFunction().accept( this );
|
||||||
|
final EmbeddableMappingType embeddableMappingType = ( (AggregateJdbcType) functionExpression.getExpressionType()
|
||||||
|
.getSingleJdbcMapping()
|
||||||
|
.getJdbcType() ).getEmbeddableMappingType();
|
||||||
|
tableGroup = new EmbeddableFunctionTableGroup(
|
||||||
|
navigablePath,
|
||||||
|
embeddableMappingType,
|
||||||
|
functionExpression
|
||||||
|
);
|
||||||
|
getFromClauseAccess().registerTableGroup( navigablePath, tableGroup );
|
||||||
|
}
|
||||||
|
return tableGroup;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Expression visitCorrelation(SqmCorrelation<?, ?> correlation) {
|
public Expression visitCorrelation(SqmCorrelation<?, ?> correlation) {
|
||||||
final TableGroup resolved = getFromClauseAccess().findTableGroup( correlation.getNavigablePath() );
|
final TableGroup resolved = getFromClauseAccess().findTableGroup( correlation.getNavigablePath() );
|
||||||
|
@ -5371,7 +5399,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MappingModelExpressible<?> resolveFunctionImpliedReturnType() {
|
public MappingModelExpressible<?> resolveFunctionImpliedReturnType() {
|
||||||
if ( inImpliedResultTypeInference || functionImpliedResultTypeAccess == null ) {
|
if ( inImpliedResultTypeInference || inTypeInference || functionImpliedResultTypeAccess == null ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
inImpliedResultTypeInference = true;
|
inImpliedResultTypeInference = true;
|
||||||
|
|
|
@ -6,9 +6,15 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.sqm.tree.domain;
|
package org.hibernate.query.sqm.tree.domain;
|
||||||
|
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
import org.hibernate.metamodel.model.domain.DomainType;
|
import org.hibernate.metamodel.model.domain.DomainType;
|
||||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
|
import org.hibernate.query.hql.spi.SqmPathRegistry;
|
||||||
|
import org.hibernate.query.spi.QueryEngine;
|
||||||
import org.hibernate.query.sqm.UnknownPathException;
|
import org.hibernate.query.sqm.UnknownPathException;
|
||||||
|
import org.hibernate.query.sqm.function.SelfRenderingSqmFunction;
|
||||||
|
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||||
|
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||||
import org.hibernate.spi.NavigablePath;
|
import org.hibernate.spi.NavigablePath;
|
||||||
import org.hibernate.query.PathException;
|
import org.hibernate.query.PathException;
|
||||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||||
|
@ -17,9 +23,12 @@ import org.hibernate.query.sqm.SemanticQueryWalker;
|
||||||
import org.hibernate.query.sqm.SqmExpressible;
|
import org.hibernate.query.sqm.SqmExpressible;
|
||||||
import org.hibernate.query.sqm.SqmPathSource;
|
import org.hibernate.query.sqm.SqmPathSource;
|
||||||
import org.hibernate.query.sqm.tree.SqmCopyContext;
|
import org.hibernate.query.sqm.tree.SqmCopyContext;
|
||||||
|
import org.hibernate.type.BasicPluralType;
|
||||||
import org.hibernate.type.descriptor.java.BasicJavaType;
|
import org.hibernate.type.descriptor.java.BasicJavaType;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
|
@ -86,6 +95,37 @@ public class SqmBasicValuedSimplePath<T>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmPath<?> resolveIndexedAccess(
|
||||||
|
SqmExpression<?> selector,
|
||||||
|
boolean isTerminal,
|
||||||
|
SqmCreationState creationState) {
|
||||||
|
final SqmPathRegistry pathRegistry = creationState.getCurrentProcessingState().getPathRegistry();
|
||||||
|
final String alias = selector.toHqlString();
|
||||||
|
final NavigablePath navigablePath = getNavigablePath().getParent().append(
|
||||||
|
CollectionPart.Nature.ELEMENT.getName(),
|
||||||
|
alias
|
||||||
|
);
|
||||||
|
final SqmFrom<?, ?> indexedPath = pathRegistry.findFromByPath( navigablePath );
|
||||||
|
if ( indexedPath != null ) {
|
||||||
|
return indexedPath;
|
||||||
|
}
|
||||||
|
if ( !( getNodeType().getSqmPathType() instanceof BasicPluralType<?, ?> ) ) {
|
||||||
|
throw new UnsupportedOperationException( "Index access is only supported for basic plural types." );
|
||||||
|
}
|
||||||
|
final QueryEngine queryEngine = creationState.getCreationContext().getQueryEngine();
|
||||||
|
final SelfRenderingSqmFunction<?> result = queryEngine.getSqmFunctionRegistry()
|
||||||
|
.findFunctionDescriptor( "array_get" )
|
||||||
|
.generateSqmExpression(
|
||||||
|
asList( this, selector ),
|
||||||
|
null,
|
||||||
|
queryEngine
|
||||||
|
);
|
||||||
|
final SqmFunctionPath<Object> path = new SqmFunctionPath<>( result );
|
||||||
|
pathRegistry.register( path );
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// SqmPath
|
// SqmPath
|
||||||
|
|
|
@ -6,13 +6,19 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.sqm.tree.domain;
|
package org.hibernate.query.sqm.tree.domain;
|
||||||
|
|
||||||
|
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||||
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
import org.hibernate.metamodel.model.domain.IdentifiableDomainType;
|
import org.hibernate.metamodel.model.domain.IdentifiableDomainType;
|
||||||
|
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||||
import org.hibernate.query.sqm.NodeBuilder;
|
import org.hibernate.query.sqm.NodeBuilder;
|
||||||
import org.hibernate.query.sqm.SemanticQueryWalker;
|
import org.hibernate.query.sqm.SemanticQueryWalker;
|
||||||
import org.hibernate.query.sqm.SqmExpressible;
|
import org.hibernate.query.sqm.SqmExpressible;
|
||||||
|
import org.hibernate.query.sqm.SqmPathSource;
|
||||||
|
import org.hibernate.query.sqm.produce.function.FunctionArgumentException;
|
||||||
import org.hibernate.query.sqm.tree.SqmCopyContext;
|
import org.hibernate.query.sqm.tree.SqmCopyContext;
|
||||||
import org.hibernate.query.sqm.tree.expression.AbstractSqmExpression;
|
import org.hibernate.query.sqm.tree.expression.AbstractSqmExpression;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||||
|
import org.hibernate.spi.NavigablePath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to the key-side (as opposed to the target-side) of the
|
* Reference to the key-side (as opposed to the target-side) of the
|
||||||
|
@ -20,13 +26,30 @@ import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class SqmFkExpression<T> extends AbstractSqmExpression<T> {
|
public class SqmFkExpression<T> extends AbstractSqmPath<T> {
|
||||||
private final SqmEntityValuedSimplePath<?> toOnePath;
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #SqmFkExpression(SqmEntityValuedSimplePath)} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
|
public SqmFkExpression(SqmEntityValuedSimplePath<?> toOnePath, NodeBuilder criteriaBuilder) {
|
||||||
|
this( toOnePath );
|
||||||
|
}
|
||||||
|
|
||||||
|
public SqmFkExpression(SqmEntityValuedSimplePath<?> toOnePath) {
|
||||||
|
this( toOnePath.getNavigablePath().append( ForeignKeyDescriptor.PART_NAME ), toOnePath );
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public SqmFkExpression(SqmEntityValuedSimplePath<?> toOnePath, NodeBuilder criteriaBuilder) {
|
private SqmFkExpression(
|
||||||
super( (SqmExpressible<? super T>) pathDomainType( toOnePath ).getIdType(), criteriaBuilder );
|
NavigablePath navigablePath,
|
||||||
this.toOnePath = toOnePath;
|
SqmEntityValuedSimplePath<?> toOnePath) {
|
||||||
|
super(
|
||||||
|
navigablePath,
|
||||||
|
(SqmPathSource<T>) pathDomainType( toOnePath ).getIdentifierDescriptor(),
|
||||||
|
toOnePath,
|
||||||
|
toOnePath.nodeBuilder()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IdentifiableDomainType<?> pathDomainType(SqmEntityValuedSimplePath<?> toOnePath) {
|
private static IdentifiableDomainType<?> pathDomainType(SqmEntityValuedSimplePath<?> toOnePath) {
|
||||||
|
@ -34,7 +57,7 @@ public class SqmFkExpression<T> extends AbstractSqmExpression<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public SqmEntityValuedSimplePath<?> getToOnePath() {
|
public SqmEntityValuedSimplePath<?> getToOnePath() {
|
||||||
return toOnePath;
|
return (SqmEntityValuedSimplePath<?>) getLhs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -45,20 +68,37 @@ public class SqmFkExpression<T> extends AbstractSqmExpression<T> {
|
||||||
@Override
|
@Override
|
||||||
public void appendHqlString(StringBuilder sb) {
|
public void appendHqlString(StringBuilder sb) {
|
||||||
sb.append( "fk(" );
|
sb.append( "fk(" );
|
||||||
toOnePath.appendHqlString( sb );
|
getLhs().appendHqlString( sb );
|
||||||
sb.append( ')' );
|
sb.append( ')' );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmExpression<T> copy(SqmCopyContext context) {
|
public SqmFkExpression<T> copy(SqmCopyContext context) {
|
||||||
final SqmFkExpression<T> existing = context.getCopy( this );
|
final SqmFkExpression<T> existing = context.getCopy( this );
|
||||||
if ( existing != null ) {
|
if ( existing != null ) {
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
final SqmEntityValuedSimplePath<?> lhsCopy = (SqmEntityValuedSimplePath<?>) getLhs().copy( context );
|
||||||
return context.registerCopy(
|
return context.registerCopy(
|
||||||
this,
|
this,
|
||||||
new SqmFkExpression<T>( toOnePath.copy( context ), nodeBuilder() )
|
new SqmFkExpression<T>( getNavigablePathCopy( lhsCopy ), lhsCopy )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <S extends T> SqmPath<S> treatAs(Class<S> treatJavaType) {
|
||||||
|
throw new FunctionArgumentException( "Fk paths cannot be TREAT-ed" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <S extends T> SqmPath<S> treatAs(EntityDomainType<S> treatTarget) {
|
||||||
|
throw new FunctionArgumentException( "Fk paths cannot be TREAT-ed" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmPath<?> resolvePathPart(String name, boolean isTerminal, SqmCreationState creationState) {
|
||||||
|
final SqmPath<?> sqmPath = get( name );
|
||||||
|
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
|
||||||
|
return sqmPath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* 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.query.sqm.tree.domain;
|
||||||
|
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
|
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
|
||||||
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
|
import org.hibernate.metamodel.model.domain.ListPersistentAttribute;
|
||||||
|
import org.hibernate.metamodel.model.domain.MapPersistentAttribute;
|
||||||
|
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
|
||||||
|
import org.hibernate.metamodel.model.domain.internal.EmbeddedSqmPathSource;
|
||||||
|
import org.hibernate.query.NotIndexedCollectionException;
|
||||||
|
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||||
|
import org.hibernate.query.hql.spi.SqmPathRegistry;
|
||||||
|
import org.hibernate.query.spi.QueryEngine;
|
||||||
|
import org.hibernate.query.sqm.SemanticQueryWalker;
|
||||||
|
import org.hibernate.query.sqm.SqmExpressible;
|
||||||
|
import org.hibernate.query.sqm.SqmPathSource;
|
||||||
|
import org.hibernate.query.sqm.function.SelfRenderingSqmFunction;
|
||||||
|
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
|
||||||
|
import org.hibernate.query.sqm.produce.function.FunctionArgumentException;
|
||||||
|
import org.hibernate.query.sqm.tree.SqmCopyContext;
|
||||||
|
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||||
|
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||||
|
import org.hibernate.query.sqm.tree.expression.SqmFunction;
|
||||||
|
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||||
|
import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin;
|
||||||
|
import org.hibernate.spi.NavigablePath;
|
||||||
|
import org.hibernate.type.BasicPluralType;
|
||||||
|
|
||||||
|
import jakarta.persistence.metamodel.Bindable;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
|
||||||
|
public class SqmFunctionPath<T> extends AbstractSqmPath<T> {
|
||||||
|
private final SqmFunction<?> function;
|
||||||
|
|
||||||
|
public SqmFunctionPath(SqmFunction<?> function) {
|
||||||
|
this( new NavigablePath( function.toHqlString() ), function );
|
||||||
|
}
|
||||||
|
|
||||||
|
public SqmFunctionPath(NavigablePath navigablePath, SqmFunction<?> function) {
|
||||||
|
super(
|
||||||
|
navigablePath,
|
||||||
|
determinePathSource( navigablePath, function ),
|
||||||
|
null,
|
||||||
|
function.nodeBuilder()
|
||||||
|
);
|
||||||
|
this.function = function;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <X> SqmPathSource<X> determinePathSource(NavigablePath navigablePath, SqmFunction<?> function) {
|
||||||
|
//noinspection unchecked
|
||||||
|
final SqmExpressible<X> nodeType = (SqmExpressible<X>) function.getNodeType();
|
||||||
|
final EmbeddableDomainType<X> embeddableDomainType = function.nodeBuilder()
|
||||||
|
.getJpaMetamodel()
|
||||||
|
.embeddable( nodeType.getBindableJavaType() );
|
||||||
|
return new EmbeddedSqmPathSource<>(
|
||||||
|
navigablePath.getFullPath(),
|
||||||
|
null,
|
||||||
|
embeddableDomainType,
|
||||||
|
Bindable.BindableType.SINGULAR_ATTRIBUTE,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SqmFunction<?> getFunction() {
|
||||||
|
return function;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmFunctionPath<T> copy(SqmCopyContext context) {
|
||||||
|
final SqmFunctionPath<T> existing = context.getCopy( this );
|
||||||
|
if ( existing != null ) {
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
final SqmFunctionPath<T> path = context.registerCopy(
|
||||||
|
this,
|
||||||
|
new SqmFunctionPath<>( getNavigablePath(), (SqmFunction<?>) function.copy( context ) )
|
||||||
|
);
|
||||||
|
copyTo( path, context );
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmPath<?> resolvePathPart(
|
||||||
|
String name,
|
||||||
|
boolean isTerminal,
|
||||||
|
SqmCreationState creationState) {
|
||||||
|
final SqmPath<?> sqmPath = get( name );
|
||||||
|
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
|
||||||
|
return sqmPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmPath<?> resolveIndexedAccess(
|
||||||
|
SqmExpression<?> selector,
|
||||||
|
boolean isTerminal,
|
||||||
|
SqmCreationState creationState) {
|
||||||
|
final SqmPathRegistry pathRegistry = creationState.getCurrentProcessingState().getPathRegistry();
|
||||||
|
final String alias = selector.toHqlString();
|
||||||
|
final NavigablePath navigablePath = getNavigablePath().getParent().append(
|
||||||
|
CollectionPart.Nature.ELEMENT.getName(),
|
||||||
|
alias
|
||||||
|
);
|
||||||
|
final SqmFrom<?, ?> indexedPath = pathRegistry.findFromByPath( navigablePath );
|
||||||
|
if ( indexedPath != null ) {
|
||||||
|
return indexedPath;
|
||||||
|
}
|
||||||
|
if ( !( getNodeType().getSqmPathType() instanceof BasicPluralType<?, ?> ) ) {
|
||||||
|
throw new UnsupportedOperationException( "Index access is only supported for basic plural types." );
|
||||||
|
}
|
||||||
|
final QueryEngine queryEngine = creationState.getCreationContext().getQueryEngine();
|
||||||
|
final SelfRenderingSqmFunction<?> result = queryEngine.getSqmFunctionRegistry()
|
||||||
|
.findFunctionDescriptor( "array_get" )
|
||||||
|
.generateSqmExpression(
|
||||||
|
asList( function, selector ),
|
||||||
|
null,
|
||||||
|
queryEngine
|
||||||
|
);
|
||||||
|
final SqmFunctionPath<Object> path = new SqmFunctionPath<>( result );
|
||||||
|
pathRegistry.register( path );
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> X accept(SemanticQueryWalker<X> walker) {
|
||||||
|
return walker.visitFunctionPath( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendHqlString(StringBuilder sb) {
|
||||||
|
function.appendHqlString( sb );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <S extends T> SqmPath<S> treatAs(Class<S> treatJavaType) {
|
||||||
|
throw new FunctionArgumentException( "Embeddable paths cannot be TREAT-ed" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <S extends T> SqmPath<S> treatAs(EntityDomainType<S> treatTarget) {
|
||||||
|
throw new FunctionArgumentException( "Embeddable paths cannot be TREAT-ed" );
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,9 +17,12 @@ import org.hibernate.query.sqm.SqmExpressible;
|
||||||
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
|
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
|
||||||
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||||
import org.hibernate.query.sqm.tree.SqmTypedNode;
|
import org.hibernate.query.sqm.tree.SqmTypedNode;
|
||||||
|
import org.hibernate.query.sqm.tree.domain.SqmFunctionPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A SQM function
|
* A SQM function
|
||||||
*
|
*
|
||||||
|
@ -37,7 +40,7 @@ public abstract class SqmFunction<T> extends AbstractSqmExpression<T>
|
||||||
public SqmFunction(
|
public SqmFunction(
|
||||||
String functionName,
|
String functionName,
|
||||||
SqmFunctionDescriptor functionDescriptor,
|
SqmFunctionDescriptor functionDescriptor,
|
||||||
SqmExpressible<T> type,
|
@Nullable SqmExpressible<T> type,
|
||||||
List<? extends SqmTypedNode<?>> arguments,
|
List<? extends SqmTypedNode<?>> arguments,
|
||||||
NodeBuilder criteriaBuilder) {
|
NodeBuilder criteriaBuilder) {
|
||||||
super( type, criteriaBuilder );
|
super( type, criteriaBuilder );
|
||||||
|
@ -175,13 +178,22 @@ public abstract class SqmFunction<T> extends AbstractSqmExpression<T>
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// SemanticPathPart
|
// SemanticPathPart
|
||||||
|
|
||||||
|
private SqmFunctionPath<T> functionPath;
|
||||||
|
|
||||||
|
private SqmFunctionPath<T> getFunctionPath() {
|
||||||
|
SqmFunctionPath<T> path = functionPath;
|
||||||
|
if ( path == null ) {
|
||||||
|
path = functionPath = new SqmFunctionPath<T>( this );
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SemanticPathPart resolvePathPart(
|
public SemanticPathPart resolvePathPart(
|
||||||
String name,
|
String name,
|
||||||
boolean isTerminal,
|
boolean isTerminal,
|
||||||
SqmCreationState creationState) {
|
SqmCreationState creationState) {
|
||||||
throw new UnsupportedOperationException();
|
return getFunctionPath().resolvePathPart( name, isTerminal, creationState );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -189,6 +201,6 @@ public abstract class SqmFunction<T> extends AbstractSqmExpression<T>
|
||||||
SqmExpression<?> selector,
|
SqmExpression<?> selector,
|
||||||
boolean isTerminal,
|
boolean isTerminal,
|
||||||
SqmCreationState creationState) {
|
SqmCreationState creationState) {
|
||||||
throw new UnsupportedOperationException();
|
return getFunctionPath().resolveIndexedAccess( selector, isTerminal, creationState );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* 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.sql.ast.tree.from;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||||
|
import org.hibernate.spi.NavigablePath;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A table group for functions that produce embeddable typed results.
|
||||||
|
*/
|
||||||
|
public class EmbeddableFunctionTableGroup extends AbstractTableGroup {
|
||||||
|
|
||||||
|
private final EmbeddableFunctionTableReference tableReference;
|
||||||
|
|
||||||
|
public EmbeddableFunctionTableGroup(
|
||||||
|
NavigablePath navigablePath,
|
||||||
|
EmbeddableMappingType embeddableMappingType,
|
||||||
|
Expression expression) {
|
||||||
|
super(
|
||||||
|
false,
|
||||||
|
navigablePath,
|
||||||
|
embeddableMappingType,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
this.tableReference = new EmbeddableFunctionTableReference( navigablePath, embeddableMappingType, expression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getGroupAlias() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyAffectedTableNames(Consumer<String> nameCollector) {
|
||||||
|
tableReference.applyAffectedTableNames( nameCollector );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TableReference getPrimaryTableReference() {
|
||||||
|
return tableReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<TableReferenceJoin> getTableReferenceJoins() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* 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.sql.ast.tree.from;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||||
|
import org.hibernate.spi.NavigablePath;
|
||||||
|
import org.hibernate.sql.ast.SqlAstWalker;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
|
||||||
|
import static org.hibernate.internal.util.StringHelper.isEmpty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A table reference for functions that produce embeddable typed results.
|
||||||
|
*/
|
||||||
|
public class EmbeddableFunctionTableReference extends AbstractTableReference {
|
||||||
|
|
||||||
|
private final String tableExpression;
|
||||||
|
private final Expression expression;
|
||||||
|
|
||||||
|
public EmbeddableFunctionTableReference(
|
||||||
|
NavigablePath navigablePath,
|
||||||
|
EmbeddableMappingType embeddableMappingType,
|
||||||
|
Expression expression) {
|
||||||
|
super( navigablePath.getFullPath(), false );
|
||||||
|
this.tableExpression = embeddableMappingType.getAggregateMapping().getContainingTableExpression();
|
||||||
|
this.expression = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expression getExpression() {
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(SqlAstWalker sqlTreeWalker) {
|
||||||
|
expression.accept( sqlTreeWalker );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getAffectedTableNames() {
|
||||||
|
return Collections.singletonList( tableExpression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAffectedTableName(String requestedName) {
|
||||||
|
return isEmpty( requestedName ) || tableExpression.equals( requestedName );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyAffectedTableNames(Consumer<String> nameCollector) {
|
||||||
|
nameCollector.accept( tableExpression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTableId() {
|
||||||
|
return tableExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visitAffectedTableNames(Function<String, Boolean> nameCollector) {
|
||||||
|
return nameCollector.apply( tableExpression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TableReference resolveTableReference(
|
||||||
|
NavigablePath navigablePath,
|
||||||
|
String tableExpression) {
|
||||||
|
if ( this.tableExpression.equals( tableExpression ) ) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UnknownTableReferenceException(
|
||||||
|
tableExpression,
|
||||||
|
String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"Unable to determine TableReference (`%s`) for `%s`",
|
||||||
|
tableExpression,
|
||||||
|
navigablePath
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TableReference getTableReference(
|
||||||
|
NavigablePath navigablePath,
|
||||||
|
String tableExpression,
|
||||||
|
boolean resolve) {
|
||||||
|
return this.tableExpression.equals( tableExpression ) ? this : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return identificationVariable;
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import java.util.List;
|
||||||
import org.hibernate.annotations.NotFound;
|
import org.hibernate.annotations.NotFound;
|
||||||
import org.hibernate.annotations.NotFoundAction;
|
import org.hibernate.annotations.NotFoundAction;
|
||||||
import org.hibernate.query.SyntaxException;
|
import org.hibernate.query.SyntaxException;
|
||||||
|
import org.hibernate.query.sqm.UnknownPathException;
|
||||||
|
|
||||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||||
import org.hibernate.testing.orm.junit.DomainModel;
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
@ -22,10 +23,13 @@ import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.EmbeddedId;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.FetchType;
|
import jakarta.persistence.FetchType;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.OneToOne;
|
import jakarta.persistence.OneToOne;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -35,13 +39,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
@DomainModel( annotatedClasses = { FkRefTests.Coin.class, FkRefTests.Currency.class } )
|
@DomainModel( annotatedClasses = { FkRefTests.Coin.class, FkRefTests.Currency.class, FkRefTests.Exchange.class } )
|
||||||
@SessionFactory( useCollectingStatementInspector = true )
|
@SessionFactory( useCollectingStatementInspector = true )
|
||||||
|
@JiraKey( "HHH-15099" )
|
||||||
|
@JiraKey( "HHH-15106" )
|
||||||
public class FkRefTests {
|
public class FkRefTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@JiraKey( "HHH-15099" )
|
|
||||||
@JiraKey( "HHH-15106" )
|
|
||||||
public void testSimplePredicateUse(SessionFactoryScope scope) {
|
public void testSimplePredicateUse(SessionFactoryScope scope) {
|
||||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
statementInspector.clear();
|
statementInspector.clear();
|
||||||
|
@ -88,8 +92,6 @@ public class FkRefTests {
|
||||||
* a join to the association table and use the fk-target column
|
* a join to the association table and use the fk-target column
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@JiraKey( "HHH-15099" )
|
|
||||||
@JiraKey( "HHH-15106" )
|
|
||||||
public void testNullnessPredicateUseBaseline(SessionFactoryScope scope) {
|
public void testNullnessPredicateUseBaseline(SessionFactoryScope scope) {
|
||||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
statementInspector.clear();
|
statementInspector.clear();
|
||||||
|
@ -109,8 +111,6 @@ public class FkRefTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@JiraKey( "HHH-15099" )
|
|
||||||
@JiraKey( "HHH-15106" )
|
|
||||||
public void testNullnessPredicateUse1(SessionFactoryScope scope) {
|
public void testNullnessPredicateUse1(SessionFactoryScope scope) {
|
||||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
statementInspector.clear();
|
statementInspector.clear();
|
||||||
|
@ -151,8 +151,6 @@ public class FkRefTests {
|
||||||
* the currency does not need to be fetched. So it works there
|
* the currency does not need to be fetched. So it works there
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@JiraKey( "HHH-15099" )
|
|
||||||
@JiraKey( "HHH-15106" )
|
|
||||||
public void testNullnessPredicateUse2(SessionFactoryScope scope) {
|
public void testNullnessPredicateUse2(SessionFactoryScope scope) {
|
||||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
statementInspector.clear();
|
statementInspector.clear();
|
||||||
|
@ -185,33 +183,52 @@ public class FkRefTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@JiraKey( "HHH-15099" )
|
public void testFkRefDereferenceInvalid(SessionFactoryScope scope) {
|
||||||
@JiraKey( "HHH-15106" )
|
|
||||||
public void testFkRefDereferenceNotAllowed(SessionFactoryScope scope) {
|
|
||||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
statementInspector.clear();
|
statementInspector.clear();
|
||||||
|
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
try {
|
try {
|
||||||
final String hql = "select c from Coin c where fk(c.currency).something";
|
final String hql = "select c from Coin c where fk(c.currency).something is not null";
|
||||||
final List<Coin> coins = session.createQuery( hql, Coin.class ).getResultList();
|
session.createQuery( hql, Coin.class ).getResultList();
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException expected) {
|
catch (IllegalArgumentException expected) {
|
||||||
assertThat( expected.getCause() ).isInstanceOf( SyntaxException.class );
|
assertThat( expected.getCause() ).isInstanceOf( UnknownPathException.class );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
try {
|
try {
|
||||||
final String hql = "select c from Coin c where fk(currency).something";
|
final String hql = "select c from Coin c where fk(currency).something is not null";
|
||||||
final List<Coin> coins = session.createQuery( hql, Coin.class ).getResultList();
|
session.createQuery( hql, Coin.class ).getResultList();
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException expected) {
|
catch (IllegalArgumentException expected) {
|
||||||
assertThat( expected.getCause() ).isInstanceOf( SyntaxException.class );
|
assertThat( expected.getCause() ).isInstanceOf( UnknownPathException.class );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFkRefDereference(SessionFactoryScope scope) {
|
||||||
|
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
|
statementInspector.clear();
|
||||||
|
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
final String hql = "select c from Coin c where fk(c.exchange).name is not null";
|
||||||
|
session.createQuery( hql, Coin.class ).getResultList();
|
||||||
|
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||||
|
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " join " );
|
||||||
|
} );
|
||||||
|
statementInspector.clear();
|
||||||
|
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
final String hql = "select c from Coin c where fk(exchange).name is not null";
|
||||||
|
session.createQuery( hql, Coin.class ).getResultList();
|
||||||
|
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||||
|
assertThat( statementInspector.getSqlQueries().get( 0 ) ).doesNotContain( " join " );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void prepareTestData(SessionFactoryScope scope) {
|
public void prepareTestData(SessionFactoryScope scope) {
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
|
@ -247,6 +264,7 @@ public class FkRefTests {
|
||||||
private Integer id;
|
private Integer id;
|
||||||
private String name;
|
private String name;
|
||||||
private Currency currency;
|
private Currency currency;
|
||||||
|
private Exchange exchange;
|
||||||
|
|
||||||
public Coin() {
|
public Coin() {
|
||||||
}
|
}
|
||||||
|
@ -284,6 +302,18 @@ public class FkRefTests {
|
||||||
public void setCurrency(Currency currency) {
|
public void setCurrency(Currency currency) {
|
||||||
this.currency = currency;
|
this.currency = currency;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@NotFound(action = NotFoundAction.IGNORE)
|
||||||
|
@JoinColumn(name = "exchange_country", referencedColumnName = "country_iso")
|
||||||
|
@JoinColumn(name = "exchange_name", referencedColumnName = "name")
|
||||||
|
public Exchange getExchange() {
|
||||||
|
return exchange;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExchange(Exchange exchange) {
|
||||||
|
this.exchange = exchange;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity(name = "Currency")
|
@Entity(name = "Currency")
|
||||||
|
@ -316,4 +346,23 @@ public class FkRefTests {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Exchange")
|
||||||
|
public static class Exchange implements Serializable {
|
||||||
|
@EmbeddedId
|
||||||
|
private ExchangeId id;
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
public Exchange() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ExchangeId implements Serializable {
|
||||||
|
private String name;
|
||||||
|
@Column(name = "country_iso")
|
||||||
|
private String countryIso;
|
||||||
|
|
||||||
|
public ExchangeId() {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue