HHH-17646 Optimize away real table group rendering if possible
This commit is contained in:
parent
5fd74adcbf
commit
a956fda688
|
@ -76,7 +76,7 @@ public class DB2LegacySqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderTableReferenceJoins(TableGroup tableGroup) {
|
protected void renderTableReferenceJoins(TableGroup tableGroup, int swappedJoinIndex, boolean forceLeftJoin) {
|
||||||
// When we are in a recursive CTE, we can't render joins on DB2...
|
// When we are in a recursive CTE, we can't render joins on DB2...
|
||||||
// See https://modern-sql.com/feature/with-recursive/db2/error-345-state-42836
|
// See https://modern-sql.com/feature/with-recursive/db2/error-345-state-42836
|
||||||
if ( isInRecursiveQueryPart() ) {
|
if ( isInRecursiveQueryPart() ) {
|
||||||
|
@ -103,7 +103,7 @@ public class DB2LegacySqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
super.renderTableReferenceJoins( tableGroup );
|
super.renderTableReferenceJoins( tableGroup, swappedJoinIndex, forceLeftJoin );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -340,13 +340,9 @@ public class H2LegacySqlAstTranslator<T extends JdbcOperation> extends AbstractS
|
||||||
final TableReference tableRef = tableGroup.getPrimaryTableReference();
|
final TableReference tableRef = tableGroup.getPrimaryTableReference();
|
||||||
// The H2 parser can't handle a sub-query as first element in a nested join
|
// The H2 parser can't handle a sub-query as first element in a nested join
|
||||||
// i.e. `join ( (select ...) alias join ... )`, so we have to introduce a dummy table reference
|
// i.e. `join ( (select ...) alias join ... )`, so we have to introduce a dummy table reference
|
||||||
if ( tableRef instanceof QueryPartTableReference || tableRef.getTableId().startsWith( "(select" ) ) {
|
if ( getSqlBuffer().charAt( getSqlBuffer().length() - 1 ) == '('
|
||||||
final boolean realTableGroup = tableGroup.isRealTableGroup()
|
&& ( tableRef instanceof QueryPartTableReference || tableRef.getTableId().startsWith( "(select" ) ) ) {
|
||||||
&& ( CollectionHelper.isNotEmpty( tableGroup.getTableReferenceJoins() )
|
appendSql( "dual cross join " );
|
||||||
|| hasNestedTableGroupsToRender( tableGroup.getNestedTableGroupJoins() ) );
|
|
||||||
if ( realTableGroup ) {
|
|
||||||
appendSql( "dual cross join " );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return super.renderPrimaryTableReference( tableGroup, lockMode );
|
return super.renderPrimaryTableReference( tableGroup, lockMode );
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,20 +12,14 @@ import java.util.function.Consumer;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||||
import org.hibernate.query.IllegalQueryOperationException;
|
import org.hibernate.query.IllegalQueryOperationException;
|
||||||
import org.hibernate.query.sqm.BinaryArithmeticOperator;
|
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
import org.hibernate.sql.ast.tree.MutationStatement;
|
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
|
||||||
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
|
||||||
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
|
||||||
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
||||||
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
|
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
|
||||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
|
@ -147,8 +141,7 @@ public class HSQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
||||||
protected void visitAnsiCaseSearchedExpression(
|
protected void visitAnsiCaseSearchedExpression(
|
||||||
CaseSearchedExpression expression,
|
CaseSearchedExpression expression,
|
||||||
Consumer<Expression> resultRenderer) {
|
Consumer<Expression> resultRenderer) {
|
||||||
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( expression )
|
if ( areAllResultsPlainParametersOrStringLiterals( expression ) ) {
|
||||||
|| areAllResultsPlainParametersOrLiterals( expression ) ) {
|
|
||||||
final List<CaseSearchedExpression.WhenFragment> whenFragments = expression.getWhenFragments();
|
final List<CaseSearchedExpression.WhenFragment> whenFragments = expression.getWhenFragments();
|
||||||
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
||||||
super.visitAnsiCaseSearchedExpression(
|
super.visitAnsiCaseSearchedExpression(
|
||||||
|
@ -172,8 +165,7 @@ public class HSQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
||||||
protected void visitAnsiCaseSimpleExpression(
|
protected void visitAnsiCaseSimpleExpression(
|
||||||
CaseSimpleExpression expression,
|
CaseSimpleExpression expression,
|
||||||
Consumer<Expression> resultRenderer) {
|
Consumer<Expression> resultRenderer) {
|
||||||
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( expression )
|
if ( areAllResultsPlainParametersOrStringLiterals( expression ) ) {
|
||||||
|| areAllResultsPlainParametersOrLiterals( expression ) ) {
|
|
||||||
final List<CaseSimpleExpression.WhenFragment> whenFragments = expression.getWhenFragments();
|
final List<CaseSimpleExpression.WhenFragment> whenFragments = expression.getWhenFragments();
|
||||||
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
||||||
super.visitAnsiCaseSimpleExpression(
|
super.visitAnsiCaseSimpleExpression(
|
||||||
|
@ -193,11 +185,11 @@ public class HSQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean areAllResultsPlainParametersOrLiterals(CaseSearchedExpression caseSearchedExpression) {
|
protected boolean areAllResultsPlainParametersOrStringLiterals(CaseSearchedExpression caseSearchedExpression) {
|
||||||
final List<CaseSearchedExpression.WhenFragment> whenFragments = caseSearchedExpression.getWhenFragments();
|
final List<CaseSearchedExpression.WhenFragment> whenFragments = caseSearchedExpression.getWhenFragments();
|
||||||
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
||||||
if ( isParameter( firstResult ) && getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT
|
if ( isParameter( firstResult ) && getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT
|
||||||
|| isLiteral( firstResult ) ) {
|
|| isStringLiteral( firstResult ) ) {
|
||||||
for ( int i = 1; i < whenFragments.size(); i++ ) {
|
for ( int i = 1; i < whenFragments.size(); i++ ) {
|
||||||
final Expression result = whenFragments.get( i ).getResult();
|
final Expression result = whenFragments.get( i ).getResult();
|
||||||
if ( isParameter( result ) ) {
|
if ( isParameter( result ) ) {
|
||||||
|
@ -205,7 +197,7 @@ public class HSQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( !isLiteral( result ) ) {
|
else if ( !isStringLiteral( result ) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,11 +206,11 @@ public class HSQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean areAllResultsPlainParametersOrLiterals(CaseSimpleExpression caseSimpleExpression) {
|
protected boolean areAllResultsPlainParametersOrStringLiterals(CaseSimpleExpression caseSimpleExpression) {
|
||||||
final List<CaseSimpleExpression.WhenFragment> whenFragments = caseSimpleExpression.getWhenFragments();
|
final List<CaseSimpleExpression.WhenFragment> whenFragments = caseSimpleExpression.getWhenFragments();
|
||||||
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
||||||
if ( isParameter( firstResult ) && getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT
|
if ( isParameter( firstResult ) && getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT
|
||||||
|| isLiteral( firstResult ) ) {
|
|| isStringLiteral( firstResult ) ) {
|
||||||
for ( int i = 1; i < whenFragments.size(); i++ ) {
|
for ( int i = 1; i < whenFragments.size(); i++ ) {
|
||||||
final Expression result = whenFragments.get( i ).getResult();
|
final Expression result = whenFragments.get( i ).getResult();
|
||||||
if ( isParameter( result ) ) {
|
if ( isParameter( result ) ) {
|
||||||
|
@ -226,7 +218,7 @@ public class HSQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( !isLiteral( result ) ) {
|
else if ( !isStringLiteral( result ) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,6 +227,13 @@ public class HSQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isStringLiteral( Expression expression ) {
|
||||||
|
if ( expression instanceof Literal ) {
|
||||||
|
return ( (Literal) expression ).getJdbcMapping().getJdbcType().isStringLike();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsFilterClause() {
|
public boolean supportsFilterClause() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class DB2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAst
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderTableReferenceJoins(TableGroup tableGroup) {
|
protected void renderTableReferenceJoins(TableGroup tableGroup, int swappedJoinIndex, boolean forceLeftJoin) {
|
||||||
// When we are in a recursive CTE, we can't render joins on DB2...
|
// When we are in a recursive CTE, we can't render joins on DB2...
|
||||||
// See https://modern-sql.com/feature/with-recursive/db2/error-345-state-42836
|
// See https://modern-sql.com/feature/with-recursive/db2/error-345-state-42836
|
||||||
if ( isInRecursiveQueryPart() ) {
|
if ( isInRecursiveQueryPart() ) {
|
||||||
|
@ -102,7 +102,7 @@ public class DB2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAst
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
super.renderTableReferenceJoins( tableGroup );
|
super.renderTableReferenceJoins( tableGroup, swappedJoinIndex, forceLeftJoin );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -314,13 +314,9 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends SqlAstTranslato
|
||||||
final TableReference tableRef = tableGroup.getPrimaryTableReference();
|
final TableReference tableRef = tableGroup.getPrimaryTableReference();
|
||||||
// The H2 parser can't handle a sub-query as first element in a nested join
|
// The H2 parser can't handle a sub-query as first element in a nested join
|
||||||
// i.e. `join ( (select ...) alias join ... )`, so we have to introduce a dummy table reference
|
// i.e. `join ( (select ...) alias join ... )`, so we have to introduce a dummy table reference
|
||||||
if ( tableRef instanceof QueryPartTableReference || tableRef.getTableId().startsWith( "(select" ) ) {
|
if ( getSqlBuffer().charAt( getSqlBuffer().length() - 1 ) == '('
|
||||||
final boolean realTableGroup = tableGroup.isRealTableGroup()
|
&& ( tableRef instanceof QueryPartTableReference || tableRef.getTableId().startsWith( "(select" ) ) ) {
|
||||||
&& ( isNotEmpty( tableGroup.getTableReferenceJoins() )
|
appendSql( "dual cross join " );
|
||||||
|| hasNestedTableGroupsToRender( tableGroup.getNestedTableGroupJoins() ) );
|
|
||||||
if ( realTableGroup ) {
|
|
||||||
appendSql( "dual cross join " );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return super.renderPrimaryTableReference( tableGroup, lockMode );
|
return super.renderPrimaryTableReference( tableGroup, lockMode );
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,14 +12,12 @@ import java.util.function.Consumer;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||||
import org.hibernate.query.IllegalQueryOperationException;
|
import org.hibernate.query.IllegalQueryOperationException;
|
||||||
import org.hibernate.query.sqm.BinaryArithmeticOperator;
|
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
|
||||||
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
||||||
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
|
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
@ -152,8 +150,7 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
protected void visitAnsiCaseSearchedExpression(
|
protected void visitAnsiCaseSearchedExpression(
|
||||||
CaseSearchedExpression expression,
|
CaseSearchedExpression expression,
|
||||||
Consumer<Expression> resultRenderer) {
|
Consumer<Expression> resultRenderer) {
|
||||||
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( expression )
|
if ( areAllResultsPlainParametersOrStringLiterals( expression ) ) {
|
||||||
|| areAllResultsPlainParametersOrLiterals( expression ) ) {
|
|
||||||
final List<CaseSearchedExpression.WhenFragment> whenFragments = expression.getWhenFragments();
|
final List<CaseSearchedExpression.WhenFragment> whenFragments = expression.getWhenFragments();
|
||||||
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
||||||
super.visitAnsiCaseSearchedExpression(
|
super.visitAnsiCaseSearchedExpression(
|
||||||
|
@ -177,8 +174,7 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
protected void visitAnsiCaseSimpleExpression(
|
protected void visitAnsiCaseSimpleExpression(
|
||||||
CaseSimpleExpression expression,
|
CaseSimpleExpression expression,
|
||||||
Consumer<Expression> resultRenderer) {
|
Consumer<Expression> resultRenderer) {
|
||||||
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( expression )
|
if ( areAllResultsPlainParametersOrStringLiterals( expression ) ) {
|
||||||
|| areAllResultsPlainParametersOrLiterals( expression ) ) {
|
|
||||||
final List<CaseSimpleExpression.WhenFragment> whenFragments = expression.getWhenFragments();
|
final List<CaseSimpleExpression.WhenFragment> whenFragments = expression.getWhenFragments();
|
||||||
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
||||||
super.visitAnsiCaseSimpleExpression(
|
super.visitAnsiCaseSimpleExpression(
|
||||||
|
@ -198,11 +194,11 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean areAllResultsPlainParametersOrLiterals(CaseSearchedExpression caseSearchedExpression) {
|
protected boolean areAllResultsPlainParametersOrStringLiterals(CaseSearchedExpression caseSearchedExpression) {
|
||||||
final List<CaseSearchedExpression.WhenFragment> whenFragments = caseSearchedExpression.getWhenFragments();
|
final List<CaseSearchedExpression.WhenFragment> whenFragments = caseSearchedExpression.getWhenFragments();
|
||||||
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
||||||
if ( isParameter( firstResult ) && getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT
|
if ( isParameter( firstResult ) && getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT
|
||||||
|| isLiteral( firstResult ) ) {
|
|| isStringLiteral( firstResult ) ) {
|
||||||
for ( int i = 1; i < whenFragments.size(); i++ ) {
|
for ( int i = 1; i < whenFragments.size(); i++ ) {
|
||||||
final Expression result = whenFragments.get( i ).getResult();
|
final Expression result = whenFragments.get( i ).getResult();
|
||||||
if ( isParameter( result ) ) {
|
if ( isParameter( result ) ) {
|
||||||
|
@ -210,7 +206,7 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( !isLiteral( result ) ) {
|
else if ( !isStringLiteral( result ) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,11 +215,11 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean areAllResultsPlainParametersOrLiterals(CaseSimpleExpression caseSimpleExpression) {
|
protected boolean areAllResultsPlainParametersOrStringLiterals(CaseSimpleExpression caseSimpleExpression) {
|
||||||
final List<CaseSimpleExpression.WhenFragment> whenFragments = caseSimpleExpression.getWhenFragments();
|
final List<CaseSimpleExpression.WhenFragment> whenFragments = caseSimpleExpression.getWhenFragments();
|
||||||
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
||||||
if ( isParameter( firstResult ) && getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT
|
if ( isParameter( firstResult ) && getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT
|
||||||
|| isLiteral( firstResult ) ) {
|
|| isStringLiteral( firstResult ) ) {
|
||||||
for ( int i = 1; i < whenFragments.size(); i++ ) {
|
for ( int i = 1; i < whenFragments.size(); i++ ) {
|
||||||
final Expression result = whenFragments.get( i ).getResult();
|
final Expression result = whenFragments.get( i ).getResult();
|
||||||
if ( isParameter( result ) ) {
|
if ( isParameter( result ) ) {
|
||||||
|
@ -231,7 +227,7 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( !isLiteral( result ) ) {
|
else if ( !isStringLiteral( result ) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -240,6 +236,13 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isStringLiteral( Expression expression ) {
|
||||||
|
if ( expression instanceof Literal ) {
|
||||||
|
return ( (Literal) expression ).getJdbcMapping().getJdbcType().isStringLike();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsFilterClause() {
|
public boolean supportsFilterClause() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -48,6 +48,8 @@ import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable;
|
||||||
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl;
|
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl;
|
||||||
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
|
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base implementation for composite identifier mappings
|
* Base implementation for composite identifier mappings
|
||||||
*
|
*
|
||||||
|
@ -129,9 +131,9 @@ public abstract class AbstractCompositeIdentifierMapping
|
||||||
public TableGroupJoin createTableGroupJoin(
|
public TableGroupJoin createTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType requestedJoinType,
|
@Nullable SqlAstJoinType requestedJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
boolean addsPredicate,
|
boolean addsPredicate,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
|
@ -154,11 +156,11 @@ public abstract class AbstractCompositeIdentifierMapping
|
||||||
public TableGroup createRootTableGroupJoin(
|
public TableGroup createRootTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType sqlAstJoinType,
|
@Nullable SqlAstJoinType sqlAstJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
Consumer<Predicate> predicateConsumer,
|
@Nullable Consumer<Predicate> predicateConsumer,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
|
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,6 +292,14 @@ public interface EntityMappingType
|
||||||
*/
|
*/
|
||||||
EntityIdentifierMapping getIdentifierMapping();
|
EntityIdentifierMapping getIdentifierMapping();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping details for the entity's identifier. This is shared across all
|
||||||
|
* entity mappings within an inheritance hierarchy.
|
||||||
|
*/
|
||||||
|
default EntityIdentifierMapping getIdentifierMappingForJoin() {
|
||||||
|
return getIdentifierMapping();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapping details for the entity's discriminator. This is shared across all
|
* Mapping details for the entity's discriminator. This is shared across all
|
||||||
* entity mappings within an inheritance hierarchy.
|
* entity mappings within an inheritance hierarchy.
|
||||||
|
|
|
@ -6,7 +6,9 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.metamodel.mapping.internal;
|
package org.hibernate.metamodel.mapping.internal;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchTiming;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
@ -255,6 +257,24 @@ public class CaseStatementDiscriminatorMappingImpl extends AbstractDiscriminator
|
||||||
this.entityTableGroup = entityTableGroup;
|
this.entityTableGroup = entityTableGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<TableReference> getUsedTableReferences() {
|
||||||
|
final ArrayList<TableReference> usedTableReferences = new ArrayList<>( tableDiscriminatorDetailsMap.size() );
|
||||||
|
tableDiscriminatorDetailsMap.forEach(
|
||||||
|
(tableName, tableDiscriminatorDetails) -> {
|
||||||
|
final TableReference tableReference = entityTableGroup.getTableReference(
|
||||||
|
entityTableGroup.getNavigablePath(),
|
||||||
|
tableName,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( tableReference != null ) {
|
||||||
|
usedTableReferences.add( tableReference );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return usedTableReferences;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderToSql(
|
public void renderToSql(
|
||||||
SqlAppender sqlAppender,
|
SqlAppender sqlAppender,
|
||||||
|
|
|
@ -52,6 +52,8 @@ import org.hibernate.type.AnyType;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.java.MutabilityPlan;
|
import org.hibernate.type.descriptor.java.MutabilityPlan;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singular, any-valued attribute
|
* Singular, any-valued attribute
|
||||||
*
|
*
|
||||||
|
@ -487,9 +489,9 @@ public class DiscriminatedAssociationAttributeMapping
|
||||||
public TableGroupJoin createTableGroupJoin(
|
public TableGroupJoin createTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType requestedJoinType,
|
@Nullable SqlAstJoinType requestedJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
boolean addsPredicate,
|
boolean addsPredicate,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
|
@ -512,11 +514,11 @@ public class DiscriminatedAssociationAttributeMapping
|
||||||
public TableGroup createRootTableGroupJoin(
|
public TableGroup createRootTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType sqlAstJoinType,
|
@Nullable SqlAstJoinType sqlAstJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
Consumer<Predicate> predicateConsumer,
|
@Nullable Consumer<Predicate> predicateConsumer,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
|
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,10 @@ import org.hibernate.sql.results.graph.Fetchable;
|
||||||
import org.hibernate.type.AnyType;
|
import org.hibernate.type.AnyType;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNullElse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
|
@ -343,19 +347,13 @@ public class DiscriminatedCollectionPart implements DiscriminatedAssociationMode
|
||||||
public TableGroupJoin createTableGroupJoin(
|
public TableGroupJoin createTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType requestedJoinType,
|
@Nullable SqlAstJoinType requestedJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
boolean addsPredicate,
|
boolean addsPredicate,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
final SqlAstJoinType joinType;
|
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
|
||||||
if ( requestedJoinType == null ) {
|
|
||||||
joinType = SqlAstJoinType.INNER;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
joinType = requestedJoinType;
|
|
||||||
}
|
|
||||||
final TableGroup tableGroup = createRootTableGroupJoin(
|
final TableGroup tableGroup = createRootTableGroupJoin(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
lhs,
|
lhs,
|
||||||
|
@ -374,11 +372,11 @@ public class DiscriminatedCollectionPart implements DiscriminatedAssociationMode
|
||||||
public TableGroup createRootTableGroupJoin(
|
public TableGroup createRootTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType sqlAstJoinType,
|
@Nullable SqlAstJoinType sqlAstJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
Consumer<Predicate> predicateConsumer,
|
@Nullable Consumer<Predicate> predicateConsumer,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
|
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,10 @@ import org.hibernate.sql.results.graph.embeddable.internal.AggregateEmbeddableRe
|
||||||
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl;
|
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl;
|
||||||
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
|
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNullElse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
|
@ -318,13 +322,13 @@ public class EmbeddedAttributeMapping
|
||||||
public TableGroupJoin createTableGroupJoin(
|
public TableGroupJoin createTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType requestedJoinType,
|
@Nullable SqlAstJoinType requestedJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
boolean addsPredicate,
|
boolean addsPredicate,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
final SqlAstJoinType joinType = requestedJoinType == null ? SqlAstJoinType.INNER : requestedJoinType;
|
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
|
||||||
final TableGroup tableGroup = createRootTableGroupJoin(
|
final TableGroup tableGroup = createRootTableGroupJoin(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
lhs,
|
lhs,
|
||||||
|
@ -343,11 +347,11 @@ public class EmbeddedAttributeMapping
|
||||||
public TableGroup createRootTableGroupJoin(
|
public TableGroup createRootTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType sqlAstJoinType,
|
@Nullable SqlAstJoinType sqlAstJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
Consumer<Predicate> predicateConsumer,
|
@Nullable Consumer<Predicate> predicateConsumer,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
|
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.hibernate.engine.FetchStyle;
|
import org.hibernate.engine.FetchStyle;
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchTiming;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
@ -21,7 +20,6 @@ import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
import org.hibernate.metamodel.mapping.MappingType;
|
import org.hibernate.metamodel.mapping.MappingType;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
|
||||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl;
|
import org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl;
|
||||||
|
@ -41,7 +39,6 @@ import org.hibernate.sql.ast.tree.from.PluralTableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.StandardVirtualTableGroup;
|
import org.hibernate.sql.ast.tree.from.StandardVirtualTableGroup;
|
||||||
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.TableGroupProducer;
|
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
import org.hibernate.sql.results.graph.DomainResult;
|
import org.hibernate.sql.results.graph.DomainResult;
|
||||||
|
@ -49,12 +46,15 @@ import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||||
import org.hibernate.sql.results.graph.Fetch;
|
import org.hibernate.sql.results.graph.Fetch;
|
||||||
import org.hibernate.sql.results.graph.FetchOptions;
|
import org.hibernate.sql.results.graph.FetchOptions;
|
||||||
import org.hibernate.sql.results.graph.FetchParent;
|
import org.hibernate.sql.results.graph.FetchParent;
|
||||||
import org.hibernate.sql.results.graph.Fetchable;
|
|
||||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable;
|
import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable;
|
||||||
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl;
|
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl;
|
||||||
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
|
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNullElse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
|
@ -231,19 +231,13 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
|
||||||
public TableGroupJoin createTableGroupJoin(
|
public TableGroupJoin createTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType requestedJoinType,
|
@Nullable SqlAstJoinType requestedJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
boolean addsPredicate,
|
boolean addsPredicate,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
final SqlAstJoinType joinType;
|
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
|
||||||
if ( requestedJoinType == null ) {
|
|
||||||
joinType = SqlAstJoinType.INNER;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
joinType = requestedJoinType;
|
|
||||||
}
|
|
||||||
final TableGroup tableGroup = createRootTableGroupJoin(
|
final TableGroup tableGroup = createRootTableGroupJoin(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
lhs,
|
lhs,
|
||||||
|
@ -262,11 +256,11 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
|
||||||
public TableGroup createRootTableGroupJoin(
|
public TableGroup createRootTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType sqlAstJoinType,
|
@Nullable SqlAstJoinType sqlAstJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
Consumer<Predicate> predicateConsumer,
|
@Nullable Consumer<Predicate> predicateConsumer,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
assert lhs.getModelPart() instanceof PluralAttributeMapping;
|
assert lhs.getModelPart() instanceof PluralAttributeMapping;
|
||||||
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
|
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
|
||||||
|
|
|
@ -56,6 +56,8 @@ import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
import org.hibernate.type.EntityType;
|
import org.hibernate.type.EntityType;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNullElse;
|
import static java.util.Objects.requireNonNullElse;
|
||||||
import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper.createInverseModelPart;
|
import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper.createInverseModelPart;
|
||||||
import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper.getPropertyOrder;
|
import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper.getPropertyOrder;
|
||||||
|
@ -258,14 +260,13 @@ public class ManyToManyCollectionPart extends AbstractEntityCollectionPart imple
|
||||||
public TableGroupJoin createTableGroupJoin(
|
public TableGroupJoin createTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType requestedJoinType,
|
@Nullable SqlAstJoinType requestedJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
boolean addsPredicate,
|
boolean addsPredicate,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
|
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
|
||||||
|
|
||||||
final LazyTableGroup lazyTableGroup = createRootTableGroupJoin(
|
final LazyTableGroup lazyTableGroup = createRootTableGroupJoin(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
lhs,
|
lhs,
|
||||||
|
@ -302,11 +303,11 @@ public class ManyToManyCollectionPart extends AbstractEntityCollectionPart imple
|
||||||
public LazyTableGroup createRootTableGroupJoin(
|
public LazyTableGroup createRootTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType requestedJoinType,
|
@Nullable SqlAstJoinType requestedJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
Consumer<Predicate> predicateConsumer,
|
@Nullable Consumer<Predicate> predicateConsumer,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
|
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
|
||||||
final boolean canUseInnerJoin = joinType == SqlAstJoinType.INNER || lhs.canUseInnerJoins();
|
final boolean canUseInnerJoin = joinType == SqlAstJoinType.INNER || lhs.canUseInnerJoins();
|
||||||
|
@ -393,7 +394,8 @@ public class ManyToManyCollectionPart extends AbstractEntityCollectionPart imple
|
||||||
fkTargetModelPart = resolveNamedTargetPart( mapKeyPropertyName, entityMappingType, collectionDescriptor );
|
fkTargetModelPart = resolveNamedTargetPart( mapKeyPropertyName, entityMappingType, collectionDescriptor );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
fkTargetModelPart = getAssociatedEntityMappingType().getIdentifierMapping();
|
fkTargetModelPart = getAssociatedEntityMappingType().getIdentifierMappingForJoin();
|
||||||
|
// fkTargetModelPart = getAssociatedEntityMappingType().getIdentifierMapping();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( StringHelper.isNotEmpty( bootCollectionDescriptor.getMappedByProperty() ) ) {
|
else if ( StringHelper.isNotEmpty( bootCollectionDescriptor.getMappedByProperty() ) ) {
|
||||||
|
@ -449,7 +451,8 @@ public class ManyToManyCollectionPart extends AbstractEntityCollectionPart imple
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// non-inverse @ManyToMany
|
// non-inverse @ManyToMany
|
||||||
fkTargetModelPart = getAssociatedEntityMappingType().getIdentifierMapping();
|
fkTargetModelPart = getAssociatedEntityMappingType().getIdentifierMappingForJoin();
|
||||||
|
// fkTargetModelPart = getAssociatedEntityMappingType().getIdentifierMapping();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( getNature() == Nature.ELEMENT ) {
|
if ( getNature() == Nature.ELEMENT ) {
|
||||||
|
|
|
@ -116,6 +116,8 @@ import org.hibernate.type.descriptor.java.MutabilityPlan;
|
||||||
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import static org.hibernate.internal.util.NullnessUtil.castNonNull;
|
import static org.hibernate.internal.util.NullnessUtil.castNonNull;
|
||||||
import static org.hibernate.metamodel.mapping.MappingModelCreationLogging.MAPPING_MODEL_CREATION_MESSAGE_LOGGER;
|
import static org.hibernate.metamodel.mapping.MappingModelCreationLogging.MAPPING_MODEL_CREATION_MESSAGE_LOGGER;
|
||||||
import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey;
|
import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey;
|
||||||
|
@ -775,7 +777,8 @@ public class MappingModelCreationHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isReferenceToPrimaryKey ) {
|
if ( isReferenceToPrimaryKey ) {
|
||||||
fkTargetPart = collectionDescriptor.getOwnerEntityPersister().getIdentifierMapping();
|
fkTargetPart = collectionDescriptor.getOwnerEntityPersister().getIdentifierMappingForJoin();
|
||||||
|
// fkTargetPart = collectionDescriptor.getOwnerEntityPersister().getIdentifierMapping();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
fkTargetPart = declaringType.findContainingEntityMapping().findSubPart( lhsPropertyName );
|
fkTargetPart = declaringType.findContainingEntityMapping().findSubPart( lhsPropertyName );
|
||||||
|
@ -928,7 +931,8 @@ public class MappingModelCreationHelper {
|
||||||
|
|
||||||
final ModelPart fkTarget;
|
final ModelPart fkTarget;
|
||||||
if ( bootValueMapping.isReferenceToPrimaryKey() ) {
|
if ( bootValueMapping.isReferenceToPrimaryKey() ) {
|
||||||
fkTarget = referencedEntityDescriptor.getIdentifierMapping();
|
fkTarget = referencedEntityDescriptor.getIdentifierMappingForJoin();
|
||||||
|
// fkTarget = referencedEntityDescriptor.getIdentifierMapping();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
fkTarget = referencedEntityDescriptor.findByPath( bootValueMapping.getReferencedPropertyName() );
|
fkTarget = referencedEntityDescriptor.findByPath( bootValueMapping.getReferencedPropertyName() );
|
||||||
|
@ -1677,9 +1681,9 @@ public class MappingModelCreationHelper {
|
||||||
public TableGroupJoin createTableGroupJoin(
|
public TableGroupJoin createTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType sqlAstJoinType,
|
@Nullable SqlAstJoinType sqlAstJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
boolean addsPredicate,
|
boolean addsPredicate,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
|
@ -1690,11 +1694,11 @@ public class MappingModelCreationHelper {
|
||||||
public TableGroup createRootTableGroupJoin(
|
public TableGroup createRootTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType sqlAstJoinType,
|
@Nullable SqlAstJoinType sqlAstJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
Consumer<Predicate> predicateConsumer,
|
@Nullable Consumer<Predicate> predicateConsumer,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
|
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
|
||||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNullElse;
|
import static java.util.Objects.requireNonNullElse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -154,9 +156,9 @@ public class OneToManyCollectionPart extends AbstractEntityCollectionPart implem
|
||||||
public TableGroupJoin createTableGroupJoin(
|
public TableGroupJoin createTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType requestedJoinType,
|
@Nullable SqlAstJoinType requestedJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
boolean addsPredicate,
|
boolean addsPredicate,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
|
@ -193,11 +195,11 @@ public class OneToManyCollectionPart extends AbstractEntityCollectionPart implem
|
||||||
public TableGroup createRootTableGroupJoin(
|
public TableGroup createRootTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType sqlAstJoinType,
|
@Nullable SqlAstJoinType sqlAstJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
Consumer<Predicate> predicateConsumer,
|
@Nullable Consumer<Predicate> predicateConsumer,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
return createTableGroupInternal(
|
return createTableGroupInternal(
|
||||||
true,
|
true,
|
||||||
|
|
|
@ -75,6 +75,8 @@ import org.hibernate.sql.results.graph.collection.internal.SelectEagerCollection
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import static org.hibernate.boot.model.internal.SoftDeleteHelper.createNonSoftDeletedRestriction;
|
import static org.hibernate.boot.model.internal.SoftDeleteHelper.createNonSoftDeletedRestriction;
|
||||||
import static org.hibernate.boot.model.internal.SoftDeleteHelper.resolveSoftDeleteMapping;
|
import static org.hibernate.boot.model.internal.SoftDeleteHelper.resolveSoftDeleteMapping;
|
||||||
|
|
||||||
|
@ -683,9 +685,9 @@ public class PluralAttributeMappingImpl
|
||||||
public TableGroupJoin createTableGroupJoin(
|
public TableGroupJoin createTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType requestedJoinType,
|
@Nullable SqlAstJoinType requestedJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
boolean addsPredicate,
|
boolean addsPredicate,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
|
@ -802,7 +804,7 @@ public class PluralAttributeMappingImpl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SqlAstJoinType determineSqlJoinType(TableGroup lhs, SqlAstJoinType requestedJoinType, boolean fetched) {
|
public SqlAstJoinType determineSqlJoinType(TableGroup lhs, @Nullable SqlAstJoinType requestedJoinType, boolean fetched) {
|
||||||
if ( hasSoftDelete() ) {
|
if ( hasSoftDelete() ) {
|
||||||
return SqlAstJoinType.LEFT;
|
return SqlAstJoinType.LEFT;
|
||||||
}
|
}
|
||||||
|
@ -824,11 +826,11 @@ public class PluralAttributeMappingImpl
|
||||||
public TableGroup createRootTableGroupJoin(
|
public TableGroup createRootTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType requestedJoinType,
|
@Nullable SqlAstJoinType requestedJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
Consumer<Predicate> predicateConsumer,
|
@Nullable Consumer<Predicate> predicateConsumer,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
return createRootTableGroupJoin(
|
return createRootTableGroupJoin(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
|
@ -861,8 +863,10 @@ public class PluralAttributeMappingImpl
|
||||||
if ( collectionDescriptor.isOneToMany() ) {
|
if ( collectionDescriptor.isOneToMany() ) {
|
||||||
tableGroup = createOneToManyTableGroup(
|
tableGroup = createOneToManyTableGroup(
|
||||||
lhs.canUseInnerJoins() && joinType == SqlAstJoinType.INNER,
|
lhs.canUseInnerJoins() && joinType == SqlAstJoinType.INNER,
|
||||||
|
joinType,
|
||||||
navigablePath,
|
navigablePath,
|
||||||
fetched,
|
fetched,
|
||||||
|
addsPredicate,
|
||||||
explicitSourceAlias,
|
explicitSourceAlias,
|
||||||
sqlAliasBase,
|
sqlAliasBase,
|
||||||
creationState
|
creationState
|
||||||
|
@ -896,8 +900,10 @@ public class PluralAttributeMappingImpl
|
||||||
|
|
||||||
private TableGroup createOneToManyTableGroup(
|
private TableGroup createOneToManyTableGroup(
|
||||||
boolean canUseInnerJoins,
|
boolean canUseInnerJoins,
|
||||||
|
SqlAstJoinType joinType,
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
|
boolean addsPredicate,
|
||||||
String sourceAlias,
|
String sourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
|
@ -920,6 +926,12 @@ public class PluralAttributeMappingImpl
|
||||||
elementTableGroup,
|
elementTableGroup,
|
||||||
creationState.getCreationContext().getSessionFactory()
|
creationState.getCreationContext().getSessionFactory()
|
||||||
);
|
);
|
||||||
|
// For inner joins we never need join nesting
|
||||||
|
final boolean nestedJoin = joinType != SqlAstJoinType.INNER
|
||||||
|
// For outer joins we need nesting if there might be an on-condition that refers to the element table
|
||||||
|
&& ( addsPredicate
|
||||||
|
|| isAffectedByEnabledFilters( creationState.getLoadQueryInfluencers(), creationState.applyOnlyLoadByKeyFilters() )
|
||||||
|
|| collectionDescriptor.hasWhereRestrictions() );
|
||||||
|
|
||||||
if ( indexDescriptor instanceof TableGroupJoinProducer ) {
|
if ( indexDescriptor instanceof TableGroupJoinProducer ) {
|
||||||
final TableGroupJoin tableGroupJoin = ( (TableGroupJoinProducer) indexDescriptor ).createTableGroupJoin(
|
final TableGroupJoin tableGroupJoin = ( (TableGroupJoinProducer) indexDescriptor ).createTableGroupJoin(
|
||||||
|
@ -932,7 +944,7 @@ public class PluralAttributeMappingImpl
|
||||||
false,
|
false,
|
||||||
creationState
|
creationState
|
||||||
);
|
);
|
||||||
tableGroup.registerIndexTableGroup( tableGroupJoin );
|
tableGroup.registerIndexTableGroup( tableGroupJoin, nestedJoin );
|
||||||
}
|
}
|
||||||
|
|
||||||
return tableGroup;
|
return tableGroup;
|
||||||
|
@ -1022,8 +1034,10 @@ public class PluralAttributeMappingImpl
|
||||||
if ( getCollectionDescriptor().isOneToMany() ) {
|
if ( getCollectionDescriptor().isOneToMany() ) {
|
||||||
return createOneToManyTableGroup(
|
return createOneToManyTableGroup(
|
||||||
canUseInnerJoins,
|
canUseInnerJoins,
|
||||||
|
SqlAstJoinType.INNER,
|
||||||
navigablePath,
|
navigablePath,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
explicitSourceAlias,
|
explicitSourceAlias,
|
||||||
explicitSqlAliasBase,
|
explicitSqlAliasBase,
|
||||||
creationState
|
creationState
|
||||||
|
|
|
@ -66,6 +66,7 @@ import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
|
||||||
import org.hibernate.property.access.spi.PropertyAccess;
|
import org.hibernate.property.access.spi.PropertyAccess;
|
||||||
import org.hibernate.proxy.HibernateProxy;
|
import org.hibernate.proxy.HibernateProxy;
|
||||||
import org.hibernate.proxy.LazyInitializer;
|
import org.hibernate.proxy.LazyInitializer;
|
||||||
|
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||||
import org.hibernate.spi.EntityIdentifierNavigablePath;
|
import org.hibernate.spi.EntityIdentifierNavigablePath;
|
||||||
import org.hibernate.spi.NavigablePath;
|
import org.hibernate.spi.NavigablePath;
|
||||||
import org.hibernate.spi.TreatedNavigablePath;
|
import org.hibernate.spi.TreatedNavigablePath;
|
||||||
|
@ -106,13 +107,14 @@ import org.hibernate.sql.results.graph.entity.internal.EntityFetchSelectImpl;
|
||||||
import org.hibernate.sql.results.internal.NullValueAssembler;
|
import org.hibernate.sql.results.internal.NullValueAssembler;
|
||||||
import org.hibernate.sql.results.internal.domain.CircularBiDirectionalFetchImpl;
|
import org.hibernate.sql.results.internal.domain.CircularBiDirectionalFetchImpl;
|
||||||
import org.hibernate.sql.results.internal.domain.CircularFetchImpl;
|
import org.hibernate.sql.results.internal.domain.CircularFetchImpl;
|
||||||
import org.hibernate.type.ComponentType;
|
|
||||||
import org.hibernate.type.CompositeType;
|
import org.hibernate.type.CompositeType;
|
||||||
import org.hibernate.type.EmbeddedComponentType;
|
import org.hibernate.type.EmbeddedComponentType;
|
||||||
import org.hibernate.type.EntityType;
|
import org.hibernate.type.EntityType;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import static org.hibernate.boot.model.internal.SoftDeleteHelper.createNonSoftDeletedRestriction;
|
import static org.hibernate.boot.model.internal.SoftDeleteHelper.createNonSoftDeletedRestriction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -855,6 +857,7 @@ public class ToOneAttributeMapping
|
||||||
// * the association does not force a join (`@NotFound`, nullable 1-1, ...)
|
// * the association does not force a join (`@NotFound`, nullable 1-1, ...)
|
||||||
// Otherwise we need to join to the associated entity table(s)
|
// Otherwise we need to join to the associated entity table(s)
|
||||||
final boolean forceJoin = hasNotFoundAction()
|
final boolean forceJoin = hasNotFoundAction()
|
||||||
|
|| entityMappingType.getSoftDeleteMapping() != null
|
||||||
|| ( cardinality == Cardinality.ONE_TO_ONE && isNullable() );
|
|| ( cardinality == Cardinality.ONE_TO_ONE && isNullable() );
|
||||||
this.canUseParentTableGroup = ! forceJoin
|
this.canUseParentTableGroup = ! forceJoin
|
||||||
&& sideNature == ForeignKeyDescriptor.Nature.KEY
|
&& sideNature == ForeignKeyDescriptor.Nature.KEY
|
||||||
|
@ -1978,9 +1981,9 @@ public class ToOneAttributeMapping
|
||||||
public TableGroupJoin createTableGroupJoin(
|
public TableGroupJoin createTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType requestedJoinType,
|
@Nullable SqlAstJoinType requestedJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
boolean addsPredicate,
|
boolean addsPredicate,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
|
@ -2072,7 +2075,11 @@ public class ToOneAttributeMapping
|
||||||
|
|
||||||
final TableGroupJoin join = new TableGroupJoin(
|
final TableGroupJoin join = new TableGroupJoin(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
joinType,
|
// Avoid checking for nested joins in here again, since this is already done in createRootTableGroupJoin
|
||||||
|
// and simply rely on the canUseInnerJoins flag instead for override the join type to LEFT
|
||||||
|
requestedJoinType == null && !lazyTableGroup.canUseInnerJoins()
|
||||||
|
? SqlAstJoinType.LEFT
|
||||||
|
: joinType,
|
||||||
lazyTableGroup,
|
lazyTableGroup,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
@ -2144,7 +2151,7 @@ public class ToOneAttributeMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqlAstJoinType determineSqlJoinType(TableGroup lhs, SqlAstJoinType requestedJoinType, boolean fetched) {
|
public SqlAstJoinType determineSqlJoinType(TableGroup lhs, @Nullable SqlAstJoinType requestedJoinType, boolean fetched) {
|
||||||
if ( requestedJoinType != null ) {
|
if ( requestedJoinType != null ) {
|
||||||
return requestedJoinType;
|
return requestedJoinType;
|
||||||
}
|
}
|
||||||
|
@ -2160,11 +2167,11 @@ public class ToOneAttributeMapping
|
||||||
public LazyTableGroup createRootTableGroupJoin(
|
public LazyTableGroup createRootTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType requestedJoinType,
|
@Nullable SqlAstJoinType requestedJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
Consumer<Predicate> predicateConsumer,
|
@Nullable Consumer<Predicate> predicateConsumer,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
final SqlAliasBase sqlAliasBase = SqlAliasBase.from(
|
final SqlAliasBase sqlAliasBase = SqlAliasBase.from(
|
||||||
explicitSqlAliasBase,
|
explicitSqlAliasBase,
|
||||||
|
@ -2174,18 +2181,16 @@ public class ToOneAttributeMapping
|
||||||
);
|
);
|
||||||
|
|
||||||
final SoftDeleteMapping softDeleteMapping = getAssociatedEntityMappingType().getSoftDeleteMapping();
|
final SoftDeleteMapping softDeleteMapping = getAssociatedEntityMappingType().getSoftDeleteMapping();
|
||||||
|
|
||||||
final boolean canUseInnerJoin;
|
final boolean canUseInnerJoin;
|
||||||
if ( ! lhs.canUseInnerJoins() ) {
|
final SqlAstJoinType currentlyProcessingJoinType = creationState instanceof SqmToSqlAstConverter
|
||||||
canUseInnerJoin = false;
|
? ( (SqmToSqlAstConverter) creationState ).getCurrentlyProcessingJoinType()
|
||||||
}
|
: null;
|
||||||
else if ( isNullable
|
if ( currentlyProcessingJoinType != null && currentlyProcessingJoinType != SqlAstJoinType.INNER ) {
|
||||||
|| hasNotFoundAction()
|
// Don't change the join type though, as that has implications for eager initialization of a LazyTableGroup
|
||||||
|| softDeleteMapping != null ) {
|
|
||||||
canUseInnerJoin = false;
|
canUseInnerJoin = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
canUseInnerJoin = requestedJoinType == SqlAstJoinType.INNER;
|
canUseInnerJoin = determineSqlJoinType( lhs, requestedJoinType, fetched ) == SqlAstJoinType.INNER;
|
||||||
}
|
}
|
||||||
|
|
||||||
TableGroup realParentTableGroup = lhs;
|
TableGroup realParentTableGroup = lhs;
|
||||||
|
|
|
@ -5814,6 +5814,7 @@ public abstract class AbstractEntityPersister
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModelPart getIdentifierModelPart(String name, EntityMappingType treatTargetType) {
|
private ModelPart getIdentifierModelPart(String name, EntityMappingType treatTargetType) {
|
||||||
|
final EntityIdentifierMapping identifierMapping = getIdentifierMappingForJoin();
|
||||||
if ( identifierMapping instanceof NonAggregatedIdentifierMapping ) {
|
if ( identifierMapping instanceof NonAggregatedIdentifierMapping ) {
|
||||||
NonAggregatedIdentifierMapping mapping = (NonAggregatedIdentifierMapping) identifierMapping;
|
NonAggregatedIdentifierMapping mapping = (NonAggregatedIdentifierMapping) identifierMapping;
|
||||||
final ModelPart subPart = mapping.findSubPart( name, treatTargetType );
|
final ModelPart subPart = mapping.findSubPart( name, treatTargetType );
|
||||||
|
|
|
@ -1331,6 +1331,15 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityIdentifierMapping getIdentifierMappingForJoin() {
|
||||||
|
// If the joined subclass has a physical discriminator and has subtypes
|
||||||
|
// we must use the root table identifier mapping for joining to allow table group elimination to work
|
||||||
|
return isPhysicalDiscriminator() && !getSubMappingTypes().isEmpty()
|
||||||
|
? getRootEntityDescriptor().getIdentifierMapping()
|
||||||
|
: super.getIdentifierMappingForJoin();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean applyDiscriminatorPredicate(
|
private boolean applyDiscriminatorPredicate(
|
||||||
TableReferenceJoin join,
|
TableReferenceJoin join,
|
||||||
NamedTableReference tableReference,
|
NamedTableReference tableReference,
|
||||||
|
@ -1341,9 +1350,11 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
|
||||||
final String discriminatorPredicate = getPrunedDiscriminatorPredicate(
|
final String discriminatorPredicate = getPrunedDiscriminatorPredicate(
|
||||||
entityNameUses,
|
entityNameUses,
|
||||||
metamodel,
|
metamodel,
|
||||||
tableReference.getIdentificationVariable()
|
"t"
|
||||||
|
// tableReference.getIdentificationVariable()
|
||||||
);
|
);
|
||||||
join.applyPredicate( new SqlFragmentPredicate( discriminatorPredicate ) );
|
tableReference.setPrunedTableExpression( "(select * from " + getRootTableName() + " t where " + discriminatorPredicate + ")" );
|
||||||
|
// join.applyPredicate( new SqlFragmentPredicate( discriminatorPredicate ) );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -63,6 +63,9 @@ import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
|
|
||||||
import jakarta.persistence.metamodel.Attribute;
|
import jakarta.persistence.metamodel.Attribute;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNullElse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Christian Beikov
|
* @author Christian Beikov
|
||||||
|
@ -341,13 +344,13 @@ public class AnonymousTupleEmbeddableValuedModelPart implements EmbeddableValued
|
||||||
public TableGroupJoin createTableGroupJoin(
|
public TableGroupJoin createTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType requestedJoinType,
|
@Nullable SqlAstJoinType requestedJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
boolean addsPredicate,
|
boolean addsPredicate,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
final SqlAstJoinType joinType = requestedJoinType == null ? SqlAstJoinType.INNER : requestedJoinType;
|
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
|
||||||
final TableGroup tableGroup = createRootTableGroupJoin(
|
final TableGroup tableGroup = createRootTableGroupJoin(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
lhs,
|
lhs,
|
||||||
|
@ -366,18 +369,13 @@ public class AnonymousTupleEmbeddableValuedModelPart implements EmbeddableValued
|
||||||
public TableGroup createRootTableGroupJoin(
|
public TableGroup createRootTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType sqlAstJoinType,
|
@Nullable SqlAstJoinType sqlAstJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
Consumer<Predicate> predicateConsumer,
|
@Nullable Consumer<Predicate> predicateConsumer,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
return new StandardVirtualTableGroup(
|
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
|
||||||
navigablePath,
|
|
||||||
this,
|
|
||||||
lhs,
|
|
||||||
fetched
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -70,6 +70,8 @@ import org.hibernate.sql.results.graph.DomainResult;
|
||||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNullElse;
|
import static java.util.Objects.requireNonNullElse;
|
||||||
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
|
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
|
||||||
|
|
||||||
|
@ -245,14 +247,13 @@ public class AnonymousTupleEntityValuedModelPart
|
||||||
public TableGroupJoin createTableGroupJoin(
|
public TableGroupJoin createTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType requestedJoinType,
|
@Nullable SqlAstJoinType requestedJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
boolean addsPredicate,
|
boolean addsPredicate,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
|
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
|
||||||
|
|
||||||
final LazyTableGroup lazyTableGroup = createRootTableGroupJoin(
|
final LazyTableGroup lazyTableGroup = createRootTableGroupJoin(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
lhs,
|
lhs,
|
||||||
|
@ -440,11 +441,11 @@ public class AnonymousTupleEntityValuedModelPart
|
||||||
public LazyTableGroup createRootTableGroupJoin(
|
public LazyTableGroup createRootTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType sqlAstJoinType,
|
@Nullable SqlAstJoinType sqlAstJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
Consumer<Predicate> predicateConsumer,
|
@Nullable Consumer<Predicate> predicateConsumer,
|
||||||
SqlAstCreationState creationState) {
|
SqlAstCreationState creationState) {
|
||||||
final SqlAliasBase sqlAliasBase = SqlAliasBase.from(
|
final SqlAliasBase sqlAliasBase = SqlAliasBase.from(
|
||||||
explicitSqlAliasBase,
|
explicitSqlAliasBase,
|
||||||
|
|
|
@ -41,8 +41,11 @@ import org.hibernate.metamodel.model.domain.BasicDomainType;
|
||||||
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.metamodel.model.domain.IdentifiableDomainType;
|
import org.hibernate.metamodel.model.domain.IdentifiableDomainType;
|
||||||
|
import org.hibernate.metamodel.model.domain.ManagedDomainType;
|
||||||
import org.hibernate.metamodel.model.domain.SimpleDomainType;
|
import org.hibernate.metamodel.model.domain.SimpleDomainType;
|
||||||
|
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
|
||||||
import org.hibernate.metamodel.model.domain.internal.EntitySqmPathSource;
|
import org.hibernate.metamodel.model.domain.internal.EntitySqmPathSource;
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.query.IllegalQueryOperationException;
|
import org.hibernate.query.IllegalQueryOperationException;
|
||||||
import org.hibernate.query.IllegalSelectQueryException;
|
import org.hibernate.query.IllegalSelectQueryException;
|
||||||
import org.hibernate.query.Order;
|
import org.hibernate.query.Order;
|
||||||
|
@ -63,6 +66,7 @@ import org.hibernate.query.sqm.tree.SqmDmlStatement;
|
||||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||||
import org.hibernate.query.sqm.tree.SqmStatement;
|
import org.hibernate.query.sqm.tree.SqmStatement;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
|
import org.hibernate.query.sqm.tree.domain.SqmSingularJoin;
|
||||||
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
|
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
|
import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||||
|
@ -101,6 +105,7 @@ import org.hibernate.type.internal.ConvertedBasicTypeImpl;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
import jakarta.persistence.Tuple;
|
import jakarta.persistence.Tuple;
|
||||||
|
import jakarta.persistence.metamodel.Type;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
@ -188,7 +193,7 @@ public class SqmUtil {
|
||||||
// we need to render the target side if in group/order by
|
// we need to render the target side if in group/order by
|
||||||
if ( association.getTargetKeyPropertyNames().contains( sqmPath.getReferencedPathSource().getPathName() )
|
if ( association.getTargetKeyPropertyNames().contains( sqmPath.getReferencedPathSource().getPathName() )
|
||||||
&& ( clause == Clause.GROUP || clause == Clause.ORDER
|
&& ( clause == Clause.GROUP || clause == Clause.ORDER
|
||||||
|| !isFkOptimizationAllowed( sqmPath.getLhs() )
|
|| !isFkOptimizationAllowed( sqmPath.getLhs(), association )
|
||||||
|| queryPart.getFirstQuerySpec().groupByClauseContains( sqmPath.getNavigablePath(), sqlAstCreationState )
|
|| queryPart.getFirstQuerySpec().groupByClauseContains( sqmPath.getNavigablePath(), sqlAstCreationState )
|
||||||
|| queryPart.getFirstQuerySpec().orderByClauseContains( sqmPath.getNavigablePath(), sqlAstCreationState ) ) ) {
|
|| queryPart.getFirstQuerySpec().orderByClauseContains( sqmPath.getNavigablePath(), sqlAstCreationState ) ) ) {
|
||||||
return association.getAssociatedEntityMappingType();
|
return association.getAssociatedEntityMappingType();
|
||||||
|
@ -217,17 +222,106 @@ public class SqmUtil {
|
||||||
* a join that cannot be dereferenced through the foreign key on the associated table,
|
* a join that cannot be dereferenced through the foreign key on the associated table,
|
||||||
* i.e. a join that's neither {@linkplain SqmJoinType#INNER} nor {@linkplain SqmJoinType#LEFT}
|
* i.e. a join that's neither {@linkplain SqmJoinType#INNER} nor {@linkplain SqmJoinType#LEFT}
|
||||||
* or one that has an explicit on clause predicate.
|
* or one that has an explicit on clause predicate.
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link #isFkOptimizationAllowed(SqmPath, EntityAssociationMapping)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "6.6.1")
|
||||||
public static boolean isFkOptimizationAllowed(SqmPath<?> sqmPath) {
|
public static boolean isFkOptimizationAllowed(SqmPath<?> sqmPath) {
|
||||||
if ( sqmPath instanceof SqmJoin<?, ?> sqmJoin ) {
|
if ( sqmPath instanceof SqmJoin<?, ?> ) {
|
||||||
return switch ( sqmJoin.getSqmJoinType() ) {
|
final SqmJoin<?, ?> sqmJoin = (SqmJoin<?, ?>) sqmPath;
|
||||||
case INNER, LEFT -> sqmJoin.getJoinPredicate() == null;
|
switch ( sqmJoin.getSqmJoinType() ) {
|
||||||
default -> false;
|
case LEFT:
|
||||||
};
|
final EntityAssociationMapping associationMapping = resolveAssociationMapping( sqmJoin );
|
||||||
|
if ( associationMapping != null && isFiltered( associationMapping ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// FallThrough intended
|
||||||
|
case INNER:
|
||||||
|
return sqmJoin.getJoinPredicate() == null;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility that returns {@code false} when the provided {@link SqmPath sqmPath} is
|
||||||
|
* a join that cannot be dereferenced through the foreign key on the associated table,
|
||||||
|
* i.e. a join that's neither {@linkplain SqmJoinType#INNER} nor {@linkplain SqmJoinType#LEFT}
|
||||||
|
* or one that has an explicit on clause predicate.
|
||||||
|
*/
|
||||||
|
public static boolean isFkOptimizationAllowed(SqmPath<?> sqmPath, EntityAssociationMapping associationMapping) {
|
||||||
|
if ( sqmPath instanceof SqmJoin<?, ?> ) {
|
||||||
|
final SqmJoin<?, ?> sqmJoin = (SqmJoin<?, ?>) sqmPath;
|
||||||
|
switch ( sqmJoin.getSqmJoinType() ) {
|
||||||
|
case LEFT:
|
||||||
|
if ( isFiltered( associationMapping ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// FallThrough intended
|
||||||
|
case INNER:
|
||||||
|
return sqmJoin.getJoinPredicate() == null;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isFiltered(EntityAssociationMapping associationMapping) {
|
||||||
|
final EntityMappingType entityMappingType = associationMapping.getAssociatedEntityMappingType();
|
||||||
|
return !associationMapping.isFkOptimizationAllowed()
|
||||||
|
// When the identifier mappings are different we have a joined subclass entity
|
||||||
|
// which will filter rows based on a discriminator predicate
|
||||||
|
|| entityMappingType.getIdentifierMappingForJoin() != entityMappingType.getIdentifierMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @Nullable EntityAssociationMapping resolveAssociationMapping(SqmJoin<?, ?> sqmJoin) {
|
||||||
|
if ( sqmJoin instanceof SqmSingularJoin<?, ?> ) {
|
||||||
|
final SqmSingularJoin<?, ?> singularJoin = (SqmSingularJoin<?, ?>) sqmJoin;
|
||||||
|
if ( singularJoin.getAttribute().getSqmPathType() instanceof EntityDomainType<?> ) {
|
||||||
|
return resolveAssociationMapping( singularJoin );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @Nullable EntityAssociationMapping resolveAssociationMapping(SqmSingularJoin<?, ?> sqmJoin) {
|
||||||
|
SingularPersistentAttribute<?, ?> attribute = sqmJoin.getAttribute();
|
||||||
|
ManagedDomainType<?> declaringType = attribute.getDeclaringType();
|
||||||
|
if ( declaringType.getPersistenceType() != Type.PersistenceType.ENTITY ) {
|
||||||
|
final StringBuilder pathBuilder = new StringBuilder();
|
||||||
|
do {
|
||||||
|
if ( pathBuilder.length() > 0 ) {
|
||||||
|
pathBuilder.insert(0, '.');
|
||||||
|
}
|
||||||
|
pathBuilder.insert( 0, attribute.getName() );
|
||||||
|
final SqmFrom<?, ?> lhs = sqmJoin.getLhs();
|
||||||
|
if ( !(lhs instanceof SqmSingularJoin<?, ?> ) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
sqmJoin = (SqmSingularJoin<?, ?>) lhs;
|
||||||
|
attribute = sqmJoin.getAttribute();
|
||||||
|
declaringType = attribute.getDeclaringType();
|
||||||
|
} while (declaringType.getPersistenceType() != Type.PersistenceType.ENTITY );
|
||||||
|
pathBuilder.insert(0, '.');
|
||||||
|
pathBuilder.insert( 0, attribute.getName() );
|
||||||
|
final EntityPersister entityDescriptor = sqmJoin.nodeBuilder()
|
||||||
|
.getSessionFactory()
|
||||||
|
.getMappingMetamodel()
|
||||||
|
.getEntityDescriptor( ( (EntityDomainType<?>) declaringType ).getHibernateEntityName() );
|
||||||
|
return (EntityAssociationMapping) entityDescriptor.findByPath( pathBuilder.toString() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final EntityPersister entityDescriptor = sqmJoin.nodeBuilder()
|
||||||
|
.getSessionFactory()
|
||||||
|
.getMappingMetamodel()
|
||||||
|
.getEntityDescriptor( ( (EntityDomainType<?>) declaringType ).getHibernateEntityName() );
|
||||||
|
return (EntityAssociationMapping) entityDescriptor.findAttributeMapping( attribute.getName() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static List<NavigablePath> getWhereClauseNavigablePaths(SqmQuerySpec<?> querySpec) {
|
public static List<NavigablePath> getWhereClauseNavigablePaths(SqmQuerySpec<?> querySpec) {
|
||||||
final SqmWhereClause where = querySpec.getWhereClause();
|
final SqmWhereClause where = querySpec.getWhereClause();
|
||||||
if ( where == null || where.getPredicate() == null ) {
|
if ( where == null || where.getPredicate() == null ) {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.sqm.sql;
|
package org.hibernate.query.sqm.sql;
|
||||||
|
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
|
@ -28,7 +29,6 @@ import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.Internal;
|
import org.hibernate.Internal;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
|
@ -3922,7 +3922,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
// Implicit joins in the ON clause need to be added as nested table group joins
|
// Implicit joins in the ON clause need to be added as nested table group joins
|
||||||
final boolean nested = currentClauseStack.getCurrent() == Clause.FROM;
|
final boolean nested = currentlyProcessingJoin != null;
|
||||||
if ( nested ) {
|
if ( nested ) {
|
||||||
parentTableGroup.addNestedTableGroupJoin( tableGroupJoin );
|
parentTableGroup.addNestedTableGroupJoin( tableGroupJoin );
|
||||||
}
|
}
|
||||||
|
@ -3996,6 +3996,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable SqlAstJoinType getCurrentlyProcessingJoinType() {
|
||||||
|
return currentlyProcessingJoin == null
|
||||||
|
? null
|
||||||
|
: currentlyProcessingJoin.getSqmJoinType().getCorrespondingSqlJoinType();
|
||||||
|
}
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// SqmPath handling
|
// SqmPath handling
|
||||||
// - Note that SqmFrom references defined in the FROM-clause are already
|
// - Note that SqmFrom references defined in the FROM-clause are already
|
||||||
|
@ -4125,8 +4132,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
// When the inferred mapping is null, we try to resolve to the FK by default, which is fine because
|
// When the inferred mapping is null, we try to resolve to the FK by default, which is fine because
|
||||||
// expansion to all target columns for select and group by clauses is handled in EntityValuedPathInterpretation
|
// expansion to all target columns for select and group by clauses is handled in EntityValuedPathInterpretation
|
||||||
if ( entityValuedModelPart instanceof EntityAssociationMapping
|
if ( entityValuedModelPart instanceof EntityAssociationMapping
|
||||||
&& ( (EntityAssociationMapping) entityValuedModelPart ).isFkOptimizationAllowed()
|
&& isFkOptimizationAllowed( path, (EntityAssociationMapping) entityValuedModelPart ) ) {
|
||||||
&& isFkOptimizationAllowed( path ) ) {
|
|
||||||
// If the table group uses an association mapping that is not a one-to-many,
|
// If the table group uses an association mapping that is not a one-to-many,
|
||||||
// we make use of the FK model part - unless the path is a non-optimizable join,
|
// we make use of the FK model part - unless the path is a non-optimizable join,
|
||||||
// for which we should always use the target's identifier to preserve semantics
|
// for which we should always use the target's identifier to preserve semantics
|
||||||
|
@ -8401,7 +8407,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
final TableGroupJoinProducer joinProducer = (TableGroupJoinProducer) fetchable;
|
final TableGroupJoinProducer joinProducer = (TableGroupJoinProducer) fetchable;
|
||||||
final TableGroup compatibleTableGroup = lhs.findCompatibleJoinedGroup(
|
final TableGroup compatibleTableGroup = lhs.findCompatibleJoinedGroup(
|
||||||
joinProducer,
|
joinProducer,
|
||||||
joinProducer.determineSqlJoinType( lhs, null, true )
|
joinProducer.getDefaultSqlAstJoinType( lhs )
|
||||||
);
|
);
|
||||||
final SqmQueryPart<?> queryPart = getCurrentSqmQueryPart();
|
final SqmQueryPart<?> queryPart = getCurrentSqmQueryPart();
|
||||||
if ( compatibleTableGroup == null
|
if ( compatibleTableGroup == null
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||||
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
|
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
|
||||||
import org.hibernate.query.sqm.tree.select.SqmQueryPart;
|
import org.hibernate.query.sqm.tree.select.SqmQueryPart;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||||
|
@ -31,6 +32,8 @@ import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.QueryTransformer;
|
import org.hibernate.sql.ast.tree.expression.QueryTransformer;
|
||||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
|
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -101,6 +104,11 @@ public class FakeSqmToSqlAstConverter extends BaseSemanticQueryWalker implements
|
||||||
public void registerQueryTransformer(QueryTransformer transformer) {
|
public void registerQueryTransformer(QueryTransformer transformer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable SqlAstJoinType getCurrentlyProcessingJoinType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isInTypeInference() {
|
public boolean isInTypeInference() {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -18,6 +18,7 @@ import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||||
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
|
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
|
||||||
import org.hibernate.query.sqm.tree.select.SqmQueryPart;
|
import org.hibernate.query.sqm.tree.select.SqmQueryPart;
|
||||||
|
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
@ -38,6 +39,12 @@ public interface SqmToSqlAstConverter extends SemanticQueryWalker<Object>, SqlAs
|
||||||
|
|
||||||
void registerQueryTransformer(QueryTransformer transformer);
|
void registerQueryTransformer(QueryTransformer transformer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link SqlAstJoinType} of the currently processing join if there is one, or {@code null}.
|
||||||
|
* This is used to determine the join type for implicit joins happening in the {@code ON} clause.
|
||||||
|
*/
|
||||||
|
@Nullable SqlAstJoinType getCurrentlyProcessingJoinType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the state of the translation is currently in type inference mode.
|
* Returns whether the state of the translation is currently in type inference mode.
|
||||||
* This is useful to avoid type inference based on other incomplete inference information.
|
* This is useful to avoid type inference based on other incomplete inference information.
|
||||||
|
|
|
@ -0,0 +1,282 @@
|
||||||
|
/*
|
||||||
|
* 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.internal;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
|
import org.hibernate.metamodel.mapping.internal.CaseStatementDiscriminatorMappingImpl;
|
||||||
|
import org.hibernate.persister.internal.SqlFragmentPredicate;
|
||||||
|
import org.hibernate.sql.ast.spi.AbstractSqlAstWalker;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.AggregateColumnWriteExpression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SelfRenderingSqlFragmentExpression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
|
||||||
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.FilterPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.Junction;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
|
import org.hibernate.sql.model.ast.ColumnWriteFragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple walker that checks if a predicate contains qualifiers.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class TableGroupHelper extends AbstractSqlAstWalker {
|
||||||
|
|
||||||
|
public static final int REAL_TABLE_GROUP_REQUIRED = Integer.MAX_VALUE;
|
||||||
|
public static final int NO_TABLE_GROUP_REQUIRED = -1;
|
||||||
|
|
||||||
|
private final String primaryQualifier;
|
||||||
|
private final Map<String, Integer> qualifiers;
|
||||||
|
private final String[] qualifierFragments;
|
||||||
|
private Integer usedTableReferenceJoinIndex;
|
||||||
|
|
||||||
|
private TableGroupHelper(String primaryQualifier, Map<String, Integer> qualifiers) {
|
||||||
|
this.primaryQualifier = primaryQualifier;
|
||||||
|
this.qualifiers = qualifiers;
|
||||||
|
final String[] qualifierFragments = new String[qualifiers.size()];
|
||||||
|
for ( Map.Entry<String, Integer> entry : qualifiers.entrySet() ) {
|
||||||
|
qualifierFragments[entry.getValue()] = entry.getKey() + ".";
|
||||||
|
}
|
||||||
|
this.qualifierFragments = qualifierFragments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index of a table reference join which can be swapped with the primary table reference
|
||||||
|
* to avoid rendering a real nested table group.
|
||||||
|
* {@link #REAL_TABLE_GROUP_REQUIRED} is returned if swapping is not possible.
|
||||||
|
* {@code #NO_TABLE_GROUP_REQUIRED} is returned if no swapping is necessary.
|
||||||
|
*/
|
||||||
|
public static int findReferenceJoinForPredicateSwap(TableGroup tableGroup, Predicate predicate) {
|
||||||
|
if ( predicate != null && !tableGroup.getTableReferenceJoins().isEmpty() ) {
|
||||||
|
final TableReference primaryTableReference = tableGroup.getPrimaryTableReference();
|
||||||
|
final HashMap<String, Integer> qualifiers = CollectionHelper.mapOfSize( tableGroup.getTableReferenceJoins().size() );
|
||||||
|
final List<TableReferenceJoin> tableReferenceJoins = tableGroup.getTableReferenceJoins();
|
||||||
|
for ( int i = 0; i < tableReferenceJoins.size(); i++ ) {
|
||||||
|
final TableReferenceJoin tableReferenceJoin = tableReferenceJoins.get( i );
|
||||||
|
if ( !tableGroup.canUseInnerJoins() ) {
|
||||||
|
if ( !isSimplePredicate( tableGroup, i ) ){//|| isSimpleOrOuterJoin( tableReferenceJoin ) ) {
|
||||||
|
// Can't do avoid the real table group rendering in this case if it's not inner joined,
|
||||||
|
// because doing so might change the meaning of the SQL. Consider this example:
|
||||||
|
// `from tbl1 t1 left join (tbl2 t2 join tbl3 t3 on t2.id=t3.id and ...) on t1.fk=t2.id`
|
||||||
|
//
|
||||||
|
// To avoid the nested table group rendering, the join on `tbl3` has to switch to left join
|
||||||
|
// `from tbl1 t1 left join tbl2 t2 on t1.fk=t2.id left join tbl3 t3 on t2.id=t3.id and ...`
|
||||||
|
// The additional predicate in the `tbl3` join can make `t3` null even though `t2` is non-null
|
||||||
|
return REAL_TABLE_GROUP_REQUIRED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qualifiers.put( tableReferenceJoin.getJoinedTableReference().getIdentificationVariable(), i );
|
||||||
|
}
|
||||||
|
final TableGroupHelper qualifierCollector = new TableGroupHelper(
|
||||||
|
primaryTableReference.getIdentificationVariable(),
|
||||||
|
qualifiers
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
predicate.accept( qualifierCollector );
|
||||||
|
if ( qualifierCollector.usedTableReferenceJoinIndex == null ) {
|
||||||
|
return NO_TABLE_GROUP_REQUIRED;
|
||||||
|
}
|
||||||
|
if ( qualifierCollector.usedTableReferenceJoinIndex != NO_TABLE_GROUP_REQUIRED
|
||||||
|
&& !tableGroup.canUseInnerJoins() && !isSimpleTableReference( primaryTableReference ) ) {
|
||||||
|
// Can't reorder table reference join with primary table reference if the primary table reference
|
||||||
|
// might filter out elements, since that affects result count with outer joins
|
||||||
|
return REAL_TABLE_GROUP_REQUIRED;
|
||||||
|
}
|
||||||
|
return qualifierCollector.usedTableReferenceJoinIndex;
|
||||||
|
}
|
||||||
|
catch (MultipleUsesFoundException ex) {
|
||||||
|
return REAL_TABLE_GROUP_REQUIRED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NO_TABLE_GROUP_REQUIRED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isSimpleTableReference(TableReference tableReference) {
|
||||||
|
return tableReference instanceof NamedTableReference && !tableReference.getTableId().startsWith( "(select" );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the table reference join at the given index uses a simple equality join predicate.
|
||||||
|
* Predicates that contain anything but comparisons of the primary table reference with table reference join columns
|
||||||
|
* are non-simple.
|
||||||
|
*/
|
||||||
|
private static boolean isSimplePredicate(TableGroup tableGroup, int index) {
|
||||||
|
final TableReference primaryTableReference = tableGroup.getPrimaryTableReference();
|
||||||
|
final TableReferenceJoin tableReferenceJoin = tableGroup.getTableReferenceJoins().get( index );
|
||||||
|
final NamedTableReference joinedTableReference = tableReferenceJoin.getJoinedTableReference();
|
||||||
|
final Predicate predicate = tableReferenceJoin.getPredicate();
|
||||||
|
if ( predicate instanceof Junction ) {
|
||||||
|
final Junction junction = (Junction) predicate;
|
||||||
|
if ( junction.getNature() == Junction.Nature.CONJUNCTION ) {
|
||||||
|
for ( Predicate subPredicate : junction.getPredicates() ) {
|
||||||
|
if ( !isComparison( subPredicate, primaryTableReference, joinedTableReference ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return isComparison( predicate, primaryTableReference, joinedTableReference );
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isComparison(Predicate predicate, TableReference table1, TableReference table2) {
|
||||||
|
if ( predicate instanceof ComparisonPredicate ) {
|
||||||
|
final ComparisonPredicate comparisonPredicate = (ComparisonPredicate) predicate;
|
||||||
|
final Expression lhs = comparisonPredicate.getLeftHandExpression();
|
||||||
|
final Expression rhs = comparisonPredicate.getRightHandExpression();
|
||||||
|
final SqlTuple lhsTuple;
|
||||||
|
if ( lhs instanceof SqlTupleContainer && ( lhsTuple = ( (SqlTupleContainer) lhs ).getSqlTuple() ) != null ) {
|
||||||
|
final SqlTuple rhsTuple = ( (SqlTupleContainer) rhs ).getSqlTuple();
|
||||||
|
final List<? extends Expression> lhsExpressions = lhsTuple.getExpressions();
|
||||||
|
final List<? extends Expression> rhsExpressions = rhsTuple.getExpressions();
|
||||||
|
for ( int i = 0; i < lhsExpressions.size(); i++ ) {
|
||||||
|
final ColumnReference lhsColumn = lhsExpressions.get( i ).getColumnReference();
|
||||||
|
final ColumnReference rhsColumn = rhsExpressions.get( i ).getColumnReference();
|
||||||
|
if ( !isComparison( table1, table2, lhsColumn, rhsColumn ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return isComparison( table1, table2, lhs.getColumnReference(), rhs.getColumnReference() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isComparison(
|
||||||
|
TableReference table1,
|
||||||
|
TableReference table2,
|
||||||
|
ColumnReference column1,
|
||||||
|
ColumnReference column2) {
|
||||||
|
if ( column1 != null && column2 != null ) {
|
||||||
|
final String column1Qualifier = column1.getQualifier();
|
||||||
|
final String column2Qualifier = column2.getQualifier();
|
||||||
|
final String table1Qualifier = table1.getIdentificationVariable();
|
||||||
|
final String table2Qualifier = table2.getIdentificationVariable();
|
||||||
|
return column1Qualifier.equals( table1Qualifier ) && column2Qualifier.equals( table2Qualifier )
|
||||||
|
|| column1Qualifier.equals( table2Qualifier ) && column2Qualifier.equals( table1Qualifier );
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MultipleUsesFoundException extends RuntimeException {
|
||||||
|
public MultipleUsesFoundException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Throwable fillInStackTrace() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkQualifier(String qualifier) {
|
||||||
|
if ( primaryQualifier.equals( qualifier ) ) {
|
||||||
|
if ( usedTableReferenceJoinIndex != null && usedTableReferenceJoinIndex != NO_TABLE_GROUP_REQUIRED ) {
|
||||||
|
throw new MultipleUsesFoundException();
|
||||||
|
}
|
||||||
|
usedTableReferenceJoinIndex = NO_TABLE_GROUP_REQUIRED;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final Integer index = qualifiers.get( qualifier );
|
||||||
|
if ( index != null ) {
|
||||||
|
if ( usedTableReferenceJoinIndex != null && usedTableReferenceJoinIndex.intValue() != index ) {
|
||||||
|
throw new MultipleUsesFoundException();
|
||||||
|
}
|
||||||
|
usedTableReferenceJoinIndex = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkSql(String sql) {
|
||||||
|
if ( sql.contains( primaryQualifier + "." ) ) {
|
||||||
|
if ( usedTableReferenceJoinIndex != null && usedTableReferenceJoinIndex != NO_TABLE_GROUP_REQUIRED ) {
|
||||||
|
throw new MultipleUsesFoundException();
|
||||||
|
}
|
||||||
|
usedTableReferenceJoinIndex = NO_TABLE_GROUP_REQUIRED;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for ( int i = 0; i < qualifierFragments.length; i++ ) {
|
||||||
|
if ( sql.contains( qualifierFragments[i] ) ) {
|
||||||
|
if ( usedTableReferenceJoinIndex != null && usedTableReferenceJoinIndex != i ) {
|
||||||
|
throw new MultipleUsesFoundException();
|
||||||
|
}
|
||||||
|
usedTableReferenceJoinIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitSelfRenderingExpression(SelfRenderingExpression expression) {
|
||||||
|
if ( expression instanceof SelfRenderingSqlFragmentExpression ) {
|
||||||
|
checkSql( ( (SelfRenderingSqlFragmentExpression) expression ).getExpression() );
|
||||||
|
}
|
||||||
|
else if ( expression instanceof CaseStatementDiscriminatorMappingImpl.CaseStatementDiscriminatorExpression ) {
|
||||||
|
for ( TableReference usedTableReference : ( (CaseStatementDiscriminatorMappingImpl.CaseStatementDiscriminatorExpression) expression ).getUsedTableReferences() ) {
|
||||||
|
usedTableReference.accept( this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.visitSelfRenderingExpression( expression );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitNamedTableReference(NamedTableReference tableReference) {
|
||||||
|
checkQualifier( tableReference.getIdentificationVariable() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitColumnReference(ColumnReference columnReference) {
|
||||||
|
checkQualifier( columnReference.getQualifier() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitAggregateColumnWriteExpression(AggregateColumnWriteExpression aggregateColumnWriteExpression) {
|
||||||
|
checkQualifier( aggregateColumnWriteExpression.getAggregateColumnReference().getQualifier() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitFilterPredicate(FilterPredicate filterPredicate) {
|
||||||
|
for ( FilterPredicate.FilterFragmentPredicate fragment : filterPredicate.getFragments() ) {
|
||||||
|
visitFilterFragmentPredicate( fragment );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitFilterFragmentPredicate(FilterPredicate.FilterFragmentPredicate fragmentPredicate) {
|
||||||
|
checkSql( fragmentPredicate.getSqlFragment() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitSqlFragmentPredicate(SqlFragmentPredicate predicate) {
|
||||||
|
checkSql( predicate.getSqlFragment() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitColumnWriteFragment(ColumnWriteFragment columnWriteFragment) {
|
||||||
|
checkSql( columnWriteFragment.getFragment() );
|
||||||
|
}
|
||||||
|
}
|
|
@ -88,6 +88,7 @@ import org.hibernate.sql.ast.SqlAstJoinType;
|
||||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.SqlTreeCreationException;
|
import org.hibernate.sql.ast.SqlTreeCreationException;
|
||||||
|
import org.hibernate.sql.ast.internal.TableGroupHelper;
|
||||||
import org.hibernate.sql.ast.internal.ParameterMarkerStrategyStandard;
|
import org.hibernate.sql.ast.internal.ParameterMarkerStrategyStandard;
|
||||||
import org.hibernate.sql.ast.tree.AbstractUpdateOrDeleteStatement;
|
import org.hibernate.sql.ast.tree.AbstractUpdateOrDeleteStatement;
|
||||||
import org.hibernate.sql.ast.tree.MutationStatement;
|
import org.hibernate.sql.ast.tree.MutationStatement;
|
||||||
|
@ -6034,10 +6035,60 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void renderTableGroup(TableGroup tableGroup, Predicate predicate, List<TableGroupJoin> tableGroupJoinCollector) {
|
protected void renderTableGroup(TableGroup tableGroup, Predicate predicate, List<TableGroupJoin> tableGroupJoinCollector) {
|
||||||
// Without reference joins or nested join groups, even a real table group does not need parenthesis
|
final boolean realTableGroup;
|
||||||
final boolean realTableGroup = tableGroup.isRealTableGroup()
|
int swappedJoinIndex = -1;
|
||||||
&& ( CollectionHelper.isNotEmpty( tableGroup.getTableReferenceJoins() )
|
boolean forceLeftJoin = false;
|
||||||
|| hasNestedTableGroupsToRender( tableGroup.getNestedTableGroupJoins() ) );
|
if ( tableGroup.isRealTableGroup() ) {
|
||||||
|
if ( hasNestedTableGroupsToRender( tableGroup.getNestedTableGroupJoins() ) ) {
|
||||||
|
// If there are nested table groups, we need to render a real table group
|
||||||
|
realTableGroup = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Determine the reference join indexes of the table reference used in the predicate
|
||||||
|
final int referenceJoinIndexForPredicateSwap = TableGroupHelper.findReferenceJoinForPredicateSwap(
|
||||||
|
tableGroup,
|
||||||
|
predicate
|
||||||
|
);
|
||||||
|
if ( referenceJoinIndexForPredicateSwap == TableGroupHelper.REAL_TABLE_GROUP_REQUIRED ) {
|
||||||
|
// Means that real table group rendering is necessary
|
||||||
|
realTableGroup = true;
|
||||||
|
}
|
||||||
|
else if ( referenceJoinIndexForPredicateSwap == TableGroupHelper.NO_TABLE_GROUP_REQUIRED ) {
|
||||||
|
// Means that no swap is necessary to avoid the table group rendering
|
||||||
|
realTableGroup = false;
|
||||||
|
forceLeftJoin = !tableGroup.canUseInnerJoins();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Means that real table group rendering can be avoided if the primary table reference is swapped
|
||||||
|
// with the table reference join at the given index
|
||||||
|
realTableGroup = false;
|
||||||
|
forceLeftJoin = !tableGroup.canUseInnerJoins();
|
||||||
|
swappedJoinIndex = referenceJoinIndexForPredicateSwap;
|
||||||
|
|
||||||
|
// Render the table reference of the table reference join first
|
||||||
|
final TableReferenceJoin tableReferenceJoin = tableGroup.getTableReferenceJoins().get( swappedJoinIndex );
|
||||||
|
renderNamedTableReference( tableReferenceJoin.getJoinedTableReference(), LockMode.NONE );
|
||||||
|
// along with the predicate for the table group
|
||||||
|
if ( predicate != null ) {
|
||||||
|
appendSql( " on " );
|
||||||
|
predicate.accept( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then render the join syntax and fall through to rendering the primary table reference
|
||||||
|
appendSql( WHITESPACE );
|
||||||
|
if ( tableGroup.canUseInnerJoins() ) {
|
||||||
|
appendSql( tableReferenceJoin.getJoinType().getText() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
append( "left " );
|
||||||
|
}
|
||||||
|
appendSql( "join " );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
realTableGroup = false;
|
||||||
|
}
|
||||||
if ( realTableGroup ) {
|
if ( realTableGroup ) {
|
||||||
appendSql( OPEN_PARENTHESIS );
|
appendSql( OPEN_PARENTHESIS );
|
||||||
}
|
}
|
||||||
|
@ -6065,7 +6116,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
||||||
tableGroupJoins = null;
|
tableGroupJoins = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( predicate != null ) {
|
// Predicate was already rendered when swappedJoinIndex is not equal to -1
|
||||||
|
if ( predicate != null && swappedJoinIndex == -1 ) {
|
||||||
appendSql( " on " );
|
appendSql( " on " );
|
||||||
predicate.accept( this );
|
predicate.accept( this );
|
||||||
}
|
}
|
||||||
|
@ -6083,7 +6135,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !realTableGroup ) {
|
if ( !realTableGroup ) {
|
||||||
renderTableReferenceJoins( tableGroup );
|
renderTableReferenceJoins( tableGroup, swappedJoinIndex, forceLeftJoin );
|
||||||
processNestedTableGroupJoins( tableGroup, tableGroupJoinCollector );
|
processNestedTableGroupJoins( tableGroup, tableGroupJoinCollector );
|
||||||
}
|
}
|
||||||
if ( tableGroupJoinCollector != null ) {
|
if ( tableGroupJoinCollector != null ) {
|
||||||
|
@ -6362,21 +6414,43 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void renderTableReferenceJoins(TableGroup tableGroup) {
|
protected void renderTableReferenceJoins(TableGroup tableGroup) {
|
||||||
|
renderTableReferenceJoins( tableGroup, -1, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderTableReferenceJoins(TableGroup tableGroup, int swappedJoinIndex, boolean forceLeftJoin) {
|
||||||
final List<TableReferenceJoin> joins = tableGroup.getTableReferenceJoins();
|
final List<TableReferenceJoin> joins = tableGroup.getTableReferenceJoins();
|
||||||
if ( joins == null || joins.isEmpty() ) {
|
if ( joins == null || joins.isEmpty() ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( TableReferenceJoin tableJoin : joins ) {
|
if ( swappedJoinIndex != -1 ) {
|
||||||
appendSql( WHITESPACE );
|
// Finish the join against the primary table reference after the swap
|
||||||
appendSql( tableJoin.getJoinType().getText() );
|
final TableReferenceJoin swappedJoin = joins.get( swappedJoinIndex );
|
||||||
appendSql( "join " );
|
if ( swappedJoin.getPredicate() != null && !swappedJoin.getPredicate().isEmpty() ) {
|
||||||
|
|
||||||
renderNamedTableReference( tableJoin.getJoinedTableReference(), LockMode.NONE );
|
|
||||||
|
|
||||||
if ( tableJoin.getPredicate() != null && !tableJoin.getPredicate().isEmpty() ) {
|
|
||||||
appendSql( " on " );
|
appendSql( " on " );
|
||||||
tableJoin.getPredicate().accept( this );
|
swappedJoin.getPredicate().accept( this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( int i = 0; i < joins.size(); i++ ) {
|
||||||
|
// Skip the swapped join since it was already rendered
|
||||||
|
if ( swappedJoinIndex != i ) {
|
||||||
|
final TableReferenceJoin tableJoin = joins.get( i );
|
||||||
|
appendSql( WHITESPACE );
|
||||||
|
if ( forceLeftJoin ) {
|
||||||
|
append( "left " );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appendSql( tableJoin.getJoinType().getText() );
|
||||||
|
}
|
||||||
|
appendSql( "join " );
|
||||||
|
|
||||||
|
renderNamedTableReference( tableJoin.getJoinedTableReference(), LockMode.NONE );
|
||||||
|
|
||||||
|
if ( tableJoin.getPredicate() != null && !tableJoin.getPredicate().isEmpty() ) {
|
||||||
|
appendSql( " on " );
|
||||||
|
tableJoin.getPredicate().accept( this );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,9 +63,18 @@ public class OneToManyTableGroup extends AbstractColumnReferenceQualifier implem
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerIndexTableGroup(TableGroupJoin indexTableGroupJoin) {
|
public void registerIndexTableGroup(TableGroupJoin indexTableGroupJoin) {
|
||||||
|
registerIndexTableGroup( indexTableGroupJoin, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerIndexTableGroup(TableGroupJoin indexTableGroupJoin, boolean nested) {
|
||||||
assert this.indexTableGroup == null;
|
assert this.indexTableGroup == null;
|
||||||
this.indexTableGroup = indexTableGroupJoin.getJoinedGroup();
|
this.indexTableGroup = indexTableGroupJoin.getJoinedGroup();
|
||||||
addNestedTableGroupJoin( indexTableGroupJoin );
|
if ( nested ) {
|
||||||
|
addNestedTableGroupJoin( indexTableGroupJoin );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
addTableGroupJoin( indexTableGroupJoin );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -12,10 +12,11 @@ import org.hibernate.spi.NavigablePath;
|
||||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
|
||||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
|
@ -41,9 +42,9 @@ public interface TableGroupJoinProducer extends TableGroupProducer {
|
||||||
TableGroupJoin createTableGroupJoin(
|
TableGroupJoin createTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType sqlAstJoinType,
|
@Nullable SqlAstJoinType sqlAstJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
boolean addsPredicate,
|
boolean addsPredicate,
|
||||||
SqlAstCreationState creationState);
|
SqlAstCreationState creationState);
|
||||||
|
@ -70,14 +71,14 @@ public interface TableGroupJoinProducer extends TableGroupProducer {
|
||||||
TableGroup createRootTableGroupJoin(
|
TableGroup createRootTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
@Nullable String explicitSourceAlias,
|
||||||
SqlAliasBase explicitSqlAliasBase,
|
@Nullable SqlAliasBase explicitSqlAliasBase,
|
||||||
SqlAstJoinType sqlAstJoinType,
|
@Nullable SqlAstJoinType sqlAstJoinType,
|
||||||
boolean fetched,
|
boolean fetched,
|
||||||
Consumer<Predicate> predicateConsumer,
|
@Nullable Consumer<Predicate> predicateConsumer,
|
||||||
SqlAstCreationState creationState);
|
SqlAstCreationState creationState);
|
||||||
|
|
||||||
default SqlAstJoinType determineSqlJoinType(TableGroup lhs, SqlAstJoinType requestedJoinType, boolean fetched) {
|
default SqlAstJoinType determineSqlJoinType(TableGroup lhs, @Nullable SqlAstJoinType requestedJoinType, boolean fetched) {
|
||||||
if ( requestedJoinType != null ) {
|
if ( requestedJoinType != null ) {
|
||||||
return requestedJoinType;
|
return requestedJoinType;
|
||||||
}
|
}
|
||||||
|
|
|
@ -458,7 +458,10 @@ public final class DateTimeUtils {
|
||||||
/**
|
/**
|
||||||
* Do the same conversion that databases do when they encounter a timestamp with a higher precision
|
* Do the same conversion that databases do when they encounter a timestamp with a higher precision
|
||||||
* than what is supported by a column, which is to round the excess fractions.
|
* than what is supported by a column, which is to round the excess fractions.
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link #adjustToDefaultPrecision(Temporal, Dialect)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "6.6.1")
|
||||||
public static <T extends Temporal> T roundToDefaultPrecision(T temporal, Dialect d) {
|
public static <T extends Temporal> T roundToDefaultPrecision(T temporal, Dialect d) {
|
||||||
final int defaultTimestampPrecision = d.getDefaultTimestampPrecision();
|
final int defaultTimestampPrecision = d.getDefaultTimestampPrecision();
|
||||||
if ( defaultTimestampPrecision >= 9 || !temporal.isSupported( ChronoField.NANO_OF_SECOND ) ) {
|
if ( defaultTimestampPrecision >= 9 || !temporal.isSupported( ChronoField.NANO_OF_SECOND ) ) {
|
||||||
|
@ -472,6 +475,9 @@ public final class DateTimeUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends Temporal> T roundToSecondPrecision(T temporal, int precision) {
|
public static <T extends Temporal> T roundToSecondPrecision(T temporal, int precision) {
|
||||||
|
if ( precision >= 9 || !temporal.isSupported( ChronoField.NANO_OF_SECOND ) ) {
|
||||||
|
return temporal;
|
||||||
|
}
|
||||||
if ( precision == 0 ) {
|
if ( precision == 0 ) {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
return temporal.get( ChronoField.NANO_OF_SECOND ) >= 500_000_000L
|
return temporal.get( ChronoField.NANO_OF_SECOND ) >= 500_000_000L
|
||||||
|
|
|
@ -5,10 +5,8 @@ import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
//@Disabled("test produces broken SQL and issue needs to be fixed")
|
|
||||||
@TestForIssue(jiraKey = "HHH-15933")
|
@TestForIssue(jiraKey = "HHH-15933")
|
||||||
@SessionFactory
|
@SessionFactory
|
||||||
@DomainModel(annotatedClasses = { Split.class, Reference.class })
|
@DomainModel(annotatedClasses = { Split.class, Reference.class })
|
||||||
|
|
|
@ -57,6 +57,33 @@ public class AttributeJoinWithJoinedInheritanceTest {
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Jira( "https://hibernate.atlassian.net/browse/HHH-17646" )
|
||||||
|
public void testLeftJoinSelectFk(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( s -> {
|
||||||
|
final ChildEntityA childEntityA = new SubChildEntityA1( 11 );
|
||||||
|
s.persist( childEntityA );
|
||||||
|
final ChildEntityB childEntityB = new ChildEntityB( 21 );
|
||||||
|
s.persist( childEntityB );
|
||||||
|
s.persist( new RootOne( 1, childEntityA ) );
|
||||||
|
s.persist( new RootOne( 2, null ) );
|
||||||
|
} );
|
||||||
|
scope.inTransaction( s -> {
|
||||||
|
// simulate association with ChildEntityB
|
||||||
|
s.createNativeMutationQuery( "update root_one set child_id = 21 where id = 2" ).executeUpdate();
|
||||||
|
} );
|
||||||
|
scope.inTransaction( s -> {
|
||||||
|
final List<Integer> resultList = s.createQuery(
|
||||||
|
"select ce.id " +
|
||||||
|
"from RootOne r left join r.child ce ",
|
||||||
|
Integer.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, resultList.size() );
|
||||||
|
assertEquals( 11, resultList.get( 0 ) );
|
||||||
|
assertNull( resultList.get( 1 ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLeftJoin(SessionFactoryScope scope) {
|
public void testLeftJoin(SessionFactoryScope scope) {
|
||||||
scope.inTransaction( s -> {
|
scope.inTransaction( s -> {
|
||||||
|
|
|
@ -0,0 +1,225 @@
|
||||||
|
/*
|
||||||
|
* 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.orm.test.inheritance.join;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.Jira;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.DiscriminatorColumn;
|
||||||
|
import jakarta.persistence.DiscriminatorValue;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Inheritance;
|
||||||
|
import jakarta.persistence.InheritanceType;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Tuple;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
|
||||||
|
@DomainModel( annotatedClasses = {
|
||||||
|
AttributeJoinWithNaturalJoinedInheritanceTest.BaseClass.class,
|
||||||
|
AttributeJoinWithNaturalJoinedInheritanceTest.ChildEntityA.class,
|
||||||
|
AttributeJoinWithNaturalJoinedInheritanceTest.SubChildEntityA1.class,
|
||||||
|
AttributeJoinWithNaturalJoinedInheritanceTest.SubChildEntityA2.class,
|
||||||
|
AttributeJoinWithNaturalJoinedInheritanceTest.ChildEntityB.class,
|
||||||
|
AttributeJoinWithNaturalJoinedInheritanceTest.RootOne.class
|
||||||
|
} )
|
||||||
|
@SessionFactory
|
||||||
|
@Jira( "https://hibernate.atlassian.net/browse/HHH-17646" )
|
||||||
|
public class AttributeJoinWithNaturalJoinedInheritanceTest {
|
||||||
|
@AfterEach
|
||||||
|
public void cleanup(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( s -> {
|
||||||
|
s.createMutationQuery( "delete from RootOne" ).executeUpdate();
|
||||||
|
s.createMutationQuery( "delete from SubChildEntityA1" ).executeUpdate();
|
||||||
|
s.createMutationQuery( "delete from SubChildEntityA2" ).executeUpdate();
|
||||||
|
s.createMutationQuery( "delete from BaseClass" ).executeUpdate();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLeftJoinWithDiscriminatorFiltering(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( s -> {
|
||||||
|
final ChildEntityA childEntityA1 = new SubChildEntityA1( 11 );
|
||||||
|
s.persist( childEntityA1 );
|
||||||
|
final ChildEntityA childEntityA2 = new SubChildEntityA2( 21 );
|
||||||
|
s.persist( childEntityA2 );
|
||||||
|
s.persist( new RootOne( 1, childEntityA1 ) );
|
||||||
|
s.persist( new RootOne( 2, childEntityA2 ) );
|
||||||
|
} );
|
||||||
|
scope.inTransaction( s -> {
|
||||||
|
final List<Tuple> resultList = s.createQuery(
|
||||||
|
"select r, ce, ce.uk " +
|
||||||
|
"from RootOne r left join treat(r.child as SubChildEntityA1) ce " +
|
||||||
|
"order by r.id",
|
||||||
|
Tuple.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, resultList.size() );
|
||||||
|
assertResult( resultList.get( 0 ), 1, 11, 11, "child_a_1", SubChildEntityA1.class, 11 );
|
||||||
|
assertResult( resultList.get( 1 ), 2, 21, null, null, null, null );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends ChildEntityA> void assertResult(
|
||||||
|
Tuple result,
|
||||||
|
Integer rootId,
|
||||||
|
Integer rootChildId,
|
||||||
|
Integer childId,
|
||||||
|
String discValue,
|
||||||
|
Class<T> subClass,
|
||||||
|
Integer uk) {
|
||||||
|
if ( rootId != null ) {
|
||||||
|
final RootOne root = result.get( 0, RootOne.class );
|
||||||
|
assertEquals( rootId, root.getId() );
|
||||||
|
assertEquals( rootChildId, root.getChildId() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assertNull( result.get( 0 ) );
|
||||||
|
}
|
||||||
|
if ( subClass != null ) {
|
||||||
|
assertInstanceOf( subClass, result.get( 1 ) );
|
||||||
|
final ChildEntityA sub1 = result.get( 1, subClass );
|
||||||
|
assertEquals( childId, sub1.getId() );
|
||||||
|
assertEquals( discValue, sub1.getDiscCol() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assertNull( result.get( 1 ) );
|
||||||
|
}
|
||||||
|
if ( uk != null ) {
|
||||||
|
assertEquals( uk, result.get( 2 ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assertNull( result.get( 2 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE: We define a {@link DiscriminatorColumn} to allow multiple subclasses
|
||||||
|
* to share the same table name. This will need additional care when pruning
|
||||||
|
* the table expression, since we'll have to add the discriminator condition
|
||||||
|
* before joining with the subclass tables
|
||||||
|
*/
|
||||||
|
@Entity( name = "BaseClass" )
|
||||||
|
@Inheritance( strategy = InheritanceType.JOINED )
|
||||||
|
@DiscriminatorColumn( name = "disc_col" )
|
||||||
|
public static class BaseClass {
|
||||||
|
@Id
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Column( name = "disc_col", insertable = false, updatable = false )
|
||||||
|
private String discCol;
|
||||||
|
|
||||||
|
public BaseClass() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseClass(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDiscCol() {
|
||||||
|
return discCol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "ChildEntityA" )
|
||||||
|
@Table( name = "child_entity" )
|
||||||
|
public static abstract class ChildEntityA extends BaseClass {
|
||||||
|
@Column(unique = true)
|
||||||
|
private Integer uk;
|
||||||
|
|
||||||
|
public ChildEntityA() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChildEntityA(Integer id) {
|
||||||
|
super( id );
|
||||||
|
this.uk = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getUk() {
|
||||||
|
return uk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "SubChildEntityA1" )
|
||||||
|
@DiscriminatorValue( "child_a_1" )
|
||||||
|
public static class SubChildEntityA1 extends ChildEntityA {
|
||||||
|
public SubChildEntityA1() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubChildEntityA1(Integer id) {
|
||||||
|
super( id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "SubChildEntityA2" )
|
||||||
|
@DiscriminatorValue( "child_a_2" )
|
||||||
|
public static class SubChildEntityA2 extends ChildEntityA {
|
||||||
|
public SubChildEntityA2() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubChildEntityA2(Integer id) {
|
||||||
|
super( id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "ChildEntityB" )
|
||||||
|
@Table( name = "child_entity" )
|
||||||
|
public static class ChildEntityB extends BaseClass {
|
||||||
|
|
||||||
|
public ChildEntityB() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChildEntityB(Integer id) {
|
||||||
|
super( id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "RootOne" )
|
||||||
|
@Table( name = "root_one" )
|
||||||
|
public static class RootOne {
|
||||||
|
@Id
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Column( name = "child_id", insertable = false, updatable = false )
|
||||||
|
private Integer childId;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn( name = "child_id", referencedColumnName = "uk")
|
||||||
|
private ChildEntityA child;
|
||||||
|
|
||||||
|
public RootOne() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public RootOne(Integer id, ChildEntityA child) {
|
||||||
|
this.id = id;
|
||||||
|
this.child = child;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getChildId() {
|
||||||
|
return childId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,215 @@
|
||||||
|
/*
|
||||||
|
* 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.orm.test.inheritance.join;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.SQLRestriction;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.Jira;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.DiscriminatorColumn;
|
||||||
|
import jakarta.persistence.DiscriminatorValue;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Inheritance;
|
||||||
|
import jakarta.persistence.InheritanceType;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Tuple;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
|
||||||
|
@DomainModel( annotatedClasses = {
|
||||||
|
AttributeJoinWithRestrictedJoinedInheritanceTest.BaseClass.class,
|
||||||
|
AttributeJoinWithRestrictedJoinedInheritanceTest.ChildEntityA.class,
|
||||||
|
AttributeJoinWithRestrictedJoinedInheritanceTest.SubChildEntityA1.class,
|
||||||
|
AttributeJoinWithRestrictedJoinedInheritanceTest.SubChildEntityA2.class,
|
||||||
|
AttributeJoinWithRestrictedJoinedInheritanceTest.ChildEntityB.class,
|
||||||
|
AttributeJoinWithRestrictedJoinedInheritanceTest.RootOne.class
|
||||||
|
} )
|
||||||
|
@SessionFactory
|
||||||
|
@Jira( "https://hibernate.atlassian.net/browse/HHH-17646" )
|
||||||
|
public class AttributeJoinWithRestrictedJoinedInheritanceTest {
|
||||||
|
@AfterEach
|
||||||
|
public void cleanup(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( s -> {
|
||||||
|
s.createMutationQuery( "delete from RootOne" ).executeUpdate();
|
||||||
|
s.createMutationQuery( "delete from SubChildEntityA1" ).executeUpdate();
|
||||||
|
s.createMutationQuery( "delete from SubChildEntityA2" ).executeUpdate();
|
||||||
|
s.createMutationQuery( "delete from BaseClass" ).executeUpdate();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLeftJoinWithDiscriminatorFiltering(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( s -> {
|
||||||
|
final ChildEntityA childEntityA1 = new SubChildEntityA1( 11 );
|
||||||
|
s.persist( childEntityA1 );
|
||||||
|
final ChildEntityA childEntityA2 = new SubChildEntityA2( 21 );
|
||||||
|
s.persist( childEntityA2 );
|
||||||
|
s.persist( new RootOne( 1, childEntityA1 ) );
|
||||||
|
s.persist( new RootOne( 2, childEntityA2 ) );
|
||||||
|
} );
|
||||||
|
scope.inTransaction( s -> {
|
||||||
|
final List<Tuple> resultList = s.createQuery(
|
||||||
|
"select r, ce " +
|
||||||
|
"from RootOne r left join r.child ce " +
|
||||||
|
"order by r.id",
|
||||||
|
Tuple.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, resultList.size() );
|
||||||
|
assertResult( resultList.get( 0 ), 1, 11, 11, "child_a_1", SubChildEntityA1.class );
|
||||||
|
assertResult( resultList.get( 1 ), 2, 21, null, null, null );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends ChildEntityA> void assertResult(
|
||||||
|
Tuple result,
|
||||||
|
Integer rootId,
|
||||||
|
Integer rootChildId,
|
||||||
|
Integer childId,
|
||||||
|
String discValue,
|
||||||
|
Class<T> subClass) {
|
||||||
|
if ( rootId != null ) {
|
||||||
|
final RootOne root = result.get( 0, RootOne.class );
|
||||||
|
assertEquals( rootId, root.getId() );
|
||||||
|
assertEquals( rootChildId, root.getChildId() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assertNull( result.get( 0 ) );
|
||||||
|
}
|
||||||
|
if ( subClass != null ) {
|
||||||
|
assertInstanceOf( subClass, result.get( 1 ) );
|
||||||
|
final ChildEntityA sub1 = result.get( 1, subClass );
|
||||||
|
assertEquals( childId, sub1.getId() );
|
||||||
|
assertEquals( discValue, sub1.getDiscCol() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assertNull( result.get( 1 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE: We define a {@link DiscriminatorColumn} to allow multiple subclasses
|
||||||
|
* to share the same table name. This will need additional care when pruning
|
||||||
|
* the table expression, since we'll have to add the discriminator condition
|
||||||
|
* before joining with the subclass tables
|
||||||
|
*/
|
||||||
|
@Entity( name = "BaseClass" )
|
||||||
|
@Inheritance( strategy = InheritanceType.JOINED )
|
||||||
|
@DiscriminatorColumn( name = "disc_col" )
|
||||||
|
@SQLRestriction( "ident < 20" )
|
||||||
|
public static class BaseClass {
|
||||||
|
@Id
|
||||||
|
@Column(name = "ident")
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Column( name = "disc_col", insertable = false, updatable = false )
|
||||||
|
private String discCol;
|
||||||
|
|
||||||
|
public BaseClass() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseClass(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDiscCol() {
|
||||||
|
return discCol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "ChildEntityA" )
|
||||||
|
@Table( name = "child_entity" )
|
||||||
|
public static abstract class ChildEntityA extends BaseClass {
|
||||||
|
|
||||||
|
public ChildEntityA() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChildEntityA(Integer id) {
|
||||||
|
super( id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "SubChildEntityA1" )
|
||||||
|
@DiscriminatorValue( "child_a_1" )
|
||||||
|
public static class SubChildEntityA1 extends ChildEntityA {
|
||||||
|
public SubChildEntityA1() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubChildEntityA1(Integer id) {
|
||||||
|
super( id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "SubChildEntityA2" )
|
||||||
|
@DiscriminatorValue( "child_a_2" )
|
||||||
|
public static class SubChildEntityA2 extends ChildEntityA {
|
||||||
|
public SubChildEntityA2() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubChildEntityA2(Integer id) {
|
||||||
|
super( id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "ChildEntityB" )
|
||||||
|
@Table( name = "child_entity" )
|
||||||
|
public static class ChildEntityB extends BaseClass {
|
||||||
|
|
||||||
|
public ChildEntityB() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChildEntityB(Integer id) {
|
||||||
|
super( id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "RootOne" )
|
||||||
|
@Table( name = "root_one" )
|
||||||
|
public static class RootOne {
|
||||||
|
@Id
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Column( name = "child_id", insertable = false, updatable = false )
|
||||||
|
private Integer childId;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn( name = "child_id")
|
||||||
|
private ChildEntityA child;
|
||||||
|
|
||||||
|
public RootOne() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public RootOne(Integer id, ChildEntityA child) {
|
||||||
|
this.id = id;
|
||||||
|
this.child = child;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getChildId() {
|
||||||
|
return childId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,6 @@ import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
||||||
import org.hibernate.testing.orm.junit.Jpa;
|
import org.hibernate.testing.orm.junit.Jpa;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
@ -411,7 +410,6 @@ public class OuterJoinTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Disabled("Hibernate doesn't support implicit joins")
|
|
||||||
public void testJoinOrderWithRightNormalJoinWithInnerImplicitJoins(EntityManagerFactoryScope scope) {
|
public void testJoinOrderWithRightNormalJoinWithInnerImplicitJoins(EntityManagerFactoryScope scope) {
|
||||||
scope.inTransaction( em -> {
|
scope.inTransaction( em -> {
|
||||||
List<Tuple> resultList = em.createQuery(
|
List<Tuple> resultList = em.createQuery(
|
||||||
|
|
|
@ -35,7 +35,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
)
|
)
|
||||||
public class LocalTimeTest {
|
public class LocalTimeTest {
|
||||||
|
|
||||||
private static final LocalTime LOCAL_TIME = DateTimeUtils.roundToDefaultPrecision(
|
private static final LocalTime LOCAL_TIME = DateTimeUtils.adjustToDefaultPrecision(
|
||||||
LocalTime.now(),
|
LocalTime.now(),
|
||||||
DialectContext.getDialect()
|
DialectContext.getDialect()
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,7 +8,6 @@ package org.hibernate.orm.test.jpa.ql;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.hibernate.testing.TestForIssue;
|
|
||||||
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;
|
||||||
import org.hibernate.testing.orm.junit.JiraKey;
|
import org.hibernate.testing.orm.junit.JiraKey;
|
||||||
|
@ -21,19 +20,28 @@ import jakarta.persistence.CollectionTable;
|
||||||
import jakarta.persistence.ElementCollection;
|
import jakarta.persistence.ElementCollection;
|
||||||
import jakarta.persistence.Embeddable;
|
import jakarta.persistence.Embeddable;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Inheritance;
|
||||||
|
import jakarta.persistence.InheritanceType;
|
||||||
import jakarta.persistence.JoinTable;
|
import jakarta.persistence.JoinTable;
|
||||||
|
import jakarta.persistence.ManyToMany;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.OneToMany;
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
@TestForIssue(jiraKey = "HHH-16691")
|
|
||||||
@DomainModel(
|
@DomainModel(
|
||||||
annotatedClasses = {
|
annotatedClasses = {
|
||||||
JoinTableOptimizationTest.Document.class, JoinTableOptimizationTest.Person.class
|
JoinTableOptimizationTest.Document.class,
|
||||||
|
JoinTableOptimizationTest.Person.class,
|
||||||
|
JoinTableOptimizationTest.File.class,
|
||||||
|
JoinTableOptimizationTest.Picture.class
|
||||||
})
|
})
|
||||||
@SessionFactory(useCollectingStatementInspector = true)
|
@SessionFactory(useCollectingStatementInspector = true)
|
||||||
public class JoinTableOptimizationTest {
|
public class JoinTableOptimizationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@JiraKey("HHH-16691")
|
||||||
public void testOnlyCollectionTableJoined(SessionFactoryScope scope) {
|
public void testOnlyCollectionTableJoined(SessionFactoryScope scope) {
|
||||||
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
statementInspector.clear();
|
statementInspector.clear();
|
||||||
|
@ -48,6 +56,7 @@ public class JoinTableOptimizationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@JiraKey("HHH-16691")
|
||||||
public void testInnerJoin(SessionFactoryScope scope) {
|
public void testInnerJoin(SessionFactoryScope scope) {
|
||||||
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
statementInspector.clear();
|
statementInspector.clear();
|
||||||
|
@ -68,6 +77,7 @@ public class JoinTableOptimizationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@JiraKey("HHH-16691")
|
||||||
public void testLeftJoin(SessionFactoryScope scope) {
|
public void testLeftJoin(SessionFactoryScope scope) {
|
||||||
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
statementInspector.clear();
|
statementInspector.clear();
|
||||||
|
@ -88,6 +98,7 @@ public class JoinTableOptimizationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@JiraKey("HHH-16691")
|
||||||
public void testInnerJoinCustomOnClause(SessionFactoryScope scope) {
|
public void testInnerJoinCustomOnClause(SessionFactoryScope scope) {
|
||||||
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
statementInspector.clear();
|
statementInspector.clear();
|
||||||
|
@ -108,6 +119,7 @@ public class JoinTableOptimizationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@JiraKey("HHH-16691")
|
||||||
public void testLeftJoinCustomOnClause(SessionFactoryScope scope) {
|
public void testLeftJoinCustomOnClause(SessionFactoryScope scope) {
|
||||||
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
statementInspector.clear();
|
statementInspector.clear();
|
||||||
|
@ -147,6 +159,199 @@ public class JoinTableOptimizationTest {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@JiraKey("HHH-17646")
|
||||||
|
public void testLeftJoinFetch(SessionFactoryScope scope) {
|
||||||
|
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
|
statementInspector.clear();
|
||||||
|
scope.inTransaction(
|
||||||
|
s -> {
|
||||||
|
s.createQuery( "select d from Document d left join fetch d.people" ).list();
|
||||||
|
statementInspector.assertExecutedCount( 1 );
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"select d1_0.id,d1_0.name,p1_0.Document_id,p1_1.id,p1_1.name " +
|
||||||
|
"from Document d1_0 " +
|
||||||
|
"left join people p1_0 on d1_0.id=p1_0.Document_id " +
|
||||||
|
"left join Person p1_1 on p1_1.id=p1_0.people_id",
|
||||||
|
statementInspector.getSqlQueries().get( 0 ),
|
||||||
|
"Nested join was not optimized away"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@JiraKey("HHH-17646")
|
||||||
|
public void testJoinTablePolymorphicJoined(SessionFactoryScope scope) {
|
||||||
|
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
|
statementInspector.clear();
|
||||||
|
scope.inTransaction(
|
||||||
|
s -> {
|
||||||
|
s.createQuery( "select f from Document d join d.manyFiles f" ).list();
|
||||||
|
statementInspector.assertExecutedCount( 1 );
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"select mf1_1.id,case when mf1_2.id is not null then 1 when mf1_1.id is not null then 0 end,mf1_1.document_id,mf1_1.name,mf1_2.extension " +
|
||||||
|
"from Document d1_0 " +
|
||||||
|
"join many_files mf1_0 on d1_0.id=mf1_0.Document_id " +
|
||||||
|
"join file_tbl mf1_1 on mf1_1.id=mf1_0.manyFiles_id " +
|
||||||
|
"left join Picture mf1_2 on mf1_1.id=mf1_2.id",
|
||||||
|
statementInspector.getSqlQueries().get( 0 ),
|
||||||
|
"Nested join was not optimized away"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@JiraKey("HHH-17646")
|
||||||
|
public void testJoinTablePolymorphicLeftJoined(SessionFactoryScope scope) {
|
||||||
|
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
|
statementInspector.clear();
|
||||||
|
scope.inTransaction(
|
||||||
|
s -> {
|
||||||
|
s.createQuery( "select f from Document d left join d.manyFiles f" ).list();
|
||||||
|
statementInspector.assertExecutedCount( 1 );
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"select mf1_1.id,case when mf1_2.id is not null then 1 when mf1_1.id is not null then 0 end,mf1_1.document_id,mf1_1.name,mf1_2.extension " +
|
||||||
|
"from Document d1_0 " +
|
||||||
|
"left join many_files mf1_0 on d1_0.id=mf1_0.Document_id " +
|
||||||
|
"left join file_tbl mf1_1 on mf1_1.id=mf1_0.manyFiles_id " +
|
||||||
|
"left join Picture mf1_2 on mf1_1.id=mf1_2.id",
|
||||||
|
statementInspector.getSqlQueries().get( 0 ),
|
||||||
|
"Nested join was not optimized away"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@JiraKey("HHH-17646")
|
||||||
|
public void testJoinPolymorphicJoined(SessionFactoryScope scope) {
|
||||||
|
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
|
statementInspector.clear();
|
||||||
|
scope.inTransaction(
|
||||||
|
s -> {
|
||||||
|
s.createQuery( "select f from Document d join d.files f" ).list();
|
||||||
|
statementInspector.assertExecutedCount( 1 );
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"select f1_0.id,case when f1_1.id is not null then 1 when f1_0.id is not null then 0 end,d1_0.id,d1_0.name,f1_0.name,f1_1.extension " +
|
||||||
|
"from Document d1_0 " +
|
||||||
|
"join file_tbl f1_0 on d1_0.id=f1_0.document_id " +
|
||||||
|
"left join Picture f1_1 on f1_0.id=f1_1.id",
|
||||||
|
statementInspector.getSqlQueries().get( 0 ),
|
||||||
|
"Nested join was not optimized away"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@JiraKey("HHH-17646")
|
||||||
|
public void testJoinPolymorphicLeftJoined(SessionFactoryScope scope) {
|
||||||
|
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
|
statementInspector.clear();
|
||||||
|
scope.inTransaction(
|
||||||
|
s -> {
|
||||||
|
s.createQuery( "select f from Document d left join d.files f" ).list();
|
||||||
|
statementInspector.assertExecutedCount( 1 );
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"select f1_0.id,case when f1_1.id is not null then 1 when f1_0.id is not null then 0 end,d1_0.id,d1_0.name,f1_0.name,f1_1.extension " +
|
||||||
|
"from Document d1_0 " +
|
||||||
|
"left join file_tbl f1_0 on d1_0.id=f1_0.document_id " +
|
||||||
|
"left join Picture f1_1 on f1_0.id=f1_1.id",
|
||||||
|
statementInspector.getSqlQueries().get( 0 ),
|
||||||
|
"Nested join was not optimized away"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@JiraKey("HHH-17646")
|
||||||
|
public void testJoinTablePolymorphicSubtypeJoined(SessionFactoryScope scope) {
|
||||||
|
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
|
statementInspector.clear();
|
||||||
|
scope.inTransaction(
|
||||||
|
s -> {
|
||||||
|
s.createQuery( "select f from Document d join d.manyPictures f" ).list();
|
||||||
|
statementInspector.assertExecutedCount( 1 );
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"select mp1_1.id,mp1_2.document_id,mp1_2.name,mp1_1.extension " +
|
||||||
|
"from Document d1_0 " +
|
||||||
|
"join many_pictures mp1_0 on d1_0.id=mp1_0.Document_id " +
|
||||||
|
"join Picture mp1_1 on mp1_1.id=mp1_0.manyPictures_id " +
|
||||||
|
"join file_tbl mp1_2 on mp1_1.id=mp1_2.id",
|
||||||
|
statementInspector.getSqlQueries().get( 0 ),
|
||||||
|
"Nested join was not optimized away"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@JiraKey("HHH-17646")
|
||||||
|
public void testJoinTablePolymorphicSubtypeLeftJoined(SessionFactoryScope scope) {
|
||||||
|
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
|
statementInspector.clear();
|
||||||
|
scope.inTransaction(
|
||||||
|
s -> {
|
||||||
|
s.createQuery( "select f from Document d left join d.manyPictures f" ).list();
|
||||||
|
statementInspector.assertExecutedCount( 1 );
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"select mp1_1.id,mp1_2.document_id,mp1_2.name,mp1_1.extension " +
|
||||||
|
"from Document d1_0 " +
|
||||||
|
"left join many_pictures mp1_0 on d1_0.id=mp1_0.Document_id " +
|
||||||
|
"left join Picture mp1_1 on mp1_1.id=mp1_0.manyPictures_id " +
|
||||||
|
"left join file_tbl mp1_2 on mp1_1.id=mp1_2.id",
|
||||||
|
statementInspector.getSqlQueries().get( 0 ),
|
||||||
|
"Nested join was not optimized away"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@JiraKey("HHH-17646")
|
||||||
|
public void testJoinPolymorphicSubtypeJoined(SessionFactoryScope scope) {
|
||||||
|
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
|
statementInspector.clear();
|
||||||
|
scope.inTransaction(
|
||||||
|
s -> {
|
||||||
|
s.createQuery( "select f from Document d join d.pictures f" ).list();
|
||||||
|
statementInspector.assertExecutedCount( 1 );
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"select p1_0.id,p1_1.document_id,p1_1.name,p1_0.extension " +
|
||||||
|
"from Document d1_0 " +
|
||||||
|
"join file_tbl p1_1 on d1_0.id=p1_1.document_id " +
|
||||||
|
"join Picture p1_0 on p1_0.id=p1_1.id",
|
||||||
|
statementInspector.getSqlQueries().get( 0 ),
|
||||||
|
"Nested join was not optimized away"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@JiraKey("HHH-17646")
|
||||||
|
public void testJoinPolymorphicSubtypeLeftJoined(SessionFactoryScope scope) {
|
||||||
|
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
|
statementInspector.clear();
|
||||||
|
scope.inTransaction(
|
||||||
|
s -> {
|
||||||
|
s.createQuery( "select f from Document d left join d.pictures f" ).list();
|
||||||
|
statementInspector.assertExecutedCount( 1 );
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"select p1_0.id,p1_1.document_id,p1_1.name,p1_0.extension " +
|
||||||
|
"from Document d1_0 " +
|
||||||
|
"left join file_tbl p1_1 on d1_0.id=p1_1.document_id " +
|
||||||
|
"left join Picture p1_0 on p1_0.id=p1_1.id",
|
||||||
|
statementInspector.getSqlQueries().get( 0 ),
|
||||||
|
"Nested join was not optimized away"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Entity(name = "Document")
|
@Entity(name = "Document")
|
||||||
public static class Document {
|
public static class Document {
|
||||||
@Id
|
@Id
|
||||||
|
@ -158,6 +363,16 @@ public class JoinTableOptimizationTest {
|
||||||
@ElementCollection
|
@ElementCollection
|
||||||
@CollectionTable(name = "document_pages")
|
@CollectionTable(name = "document_pages")
|
||||||
Set<Page> pages;
|
Set<Page> pages;
|
||||||
|
@OneToMany(mappedBy = "document")
|
||||||
|
Set<File> files;
|
||||||
|
@ManyToMany
|
||||||
|
@JoinTable(name = "many_files")
|
||||||
|
Set<File> manyFiles;
|
||||||
|
@OneToMany(mappedBy = "document")
|
||||||
|
Set<Picture> pictures;
|
||||||
|
@ManyToMany
|
||||||
|
@JoinTable(name = "many_pictures")
|
||||||
|
Set<Picture> manyPictures;
|
||||||
}
|
}
|
||||||
@Entity(name = "Person")
|
@Entity(name = "Person")
|
||||||
public static class Person {
|
public static class Person {
|
||||||
|
@ -165,6 +380,20 @@ public class JoinTableOptimizationTest {
|
||||||
Long id;
|
Long id;
|
||||||
String name;
|
String name;
|
||||||
}
|
}
|
||||||
|
@Entity(name = "File")
|
||||||
|
@Table(name = "file_tbl")
|
||||||
|
@Inheritance(strategy = InheritanceType.JOINED)
|
||||||
|
public static class File {
|
||||||
|
@Id
|
||||||
|
Long id;
|
||||||
|
String name;
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
Document document;
|
||||||
|
}
|
||||||
|
@Entity(name = "Picture")
|
||||||
|
public static class Picture extends File {
|
||||||
|
String extension;
|
||||||
|
}
|
||||||
@Embeddable
|
@Embeddable
|
||||||
public static class Page {
|
public static class Page {
|
||||||
String text;
|
String text;
|
||||||
|
|
|
@ -44,7 +44,7 @@ import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.roundToDefaultPrecision;
|
import static org.hibernate.type.descriptor.DateTimeUtils.adjustToDefaultPrecision;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for "direct" JDBC handling of {@linkplain java.time Java Time} types.
|
* Tests for "direct" JDBC handling of {@linkplain java.time Java Time} types.
|
||||||
|
@ -84,7 +84,7 @@ public class GlobalJavaTimeJdbcTypeTests {
|
||||||
@Test
|
@Test
|
||||||
void testInstant(SessionFactoryScope scope) {
|
void testInstant(SessionFactoryScope scope) {
|
||||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||||
final Instant start = roundToDefaultPrecision( Instant.EPOCH, dialect );
|
final Instant start = adjustToDefaultPrecision( Instant.EPOCH, dialect );
|
||||||
|
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
|
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
|
||||||
|
@ -114,7 +114,7 @@ public class GlobalJavaTimeJdbcTypeTests {
|
||||||
@Test
|
@Test
|
||||||
void testLocalDateTime(SessionFactoryScope scope) {
|
void testLocalDateTime(SessionFactoryScope scope) {
|
||||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||||
final LocalDateTime start = roundToDefaultPrecision( LocalDateTime.now(), dialect );
|
final LocalDateTime start = adjustToDefaultPrecision( LocalDateTime.now(), dialect );
|
||||||
|
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
|
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
|
||||||
|
@ -144,7 +144,7 @@ public class GlobalJavaTimeJdbcTypeTests {
|
||||||
@Test
|
@Test
|
||||||
void testLocalDate(SessionFactoryScope scope) {
|
void testLocalDate(SessionFactoryScope scope) {
|
||||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||||
final LocalDate startTime = roundToDefaultPrecision( LocalDate.now(), dialect );
|
final LocalDate startTime = adjustToDefaultPrecision( LocalDate.now(), dialect );
|
||||||
|
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
|
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
|
||||||
|
@ -177,7 +177,7 @@ public class GlobalJavaTimeJdbcTypeTests {
|
||||||
@SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Altibase drivers truncate fractional seconds from the LocalTime")
|
@SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Altibase drivers truncate fractional seconds from the LocalTime")
|
||||||
void testLocalTime(SessionFactoryScope scope) {
|
void testLocalTime(SessionFactoryScope scope) {
|
||||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||||
final LocalTime startTime = roundToDefaultPrecision( LocalTime.now(), dialect );
|
final LocalTime startTime = adjustToDefaultPrecision( LocalTime.now(), dialect );
|
||||||
|
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
|
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
|
||||||
|
|
|
@ -45,7 +45,7 @@ import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.roundToDefaultPrecision;
|
import static org.hibernate.type.descriptor.DateTimeUtils.adjustToDefaultPrecision;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for "direct" JDBC handling of {@linkplain java.time Java Time} types.
|
* Tests for "direct" JDBC handling of {@linkplain java.time Java Time} types.
|
||||||
|
@ -85,7 +85,7 @@ public class JavaTimeJdbcTypeTests {
|
||||||
@Test
|
@Test
|
||||||
void testInstant(SessionFactoryScope scope) {
|
void testInstant(SessionFactoryScope scope) {
|
||||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||||
final Instant start = roundToDefaultPrecision( Instant.EPOCH, dialect );
|
final Instant start = adjustToDefaultPrecision( Instant.EPOCH, dialect );
|
||||||
|
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
|
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
|
||||||
|
@ -115,7 +115,7 @@ public class JavaTimeJdbcTypeTests {
|
||||||
@Test
|
@Test
|
||||||
void testLocalDateTime(SessionFactoryScope scope) {
|
void testLocalDateTime(SessionFactoryScope scope) {
|
||||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||||
final LocalDateTime start = roundToDefaultPrecision( LocalDateTime.now(), dialect );
|
final LocalDateTime start = adjustToDefaultPrecision( LocalDateTime.now(), dialect );
|
||||||
|
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
|
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
|
||||||
|
@ -145,7 +145,7 @@ public class JavaTimeJdbcTypeTests {
|
||||||
@Test
|
@Test
|
||||||
void testLocalDate(SessionFactoryScope scope) {
|
void testLocalDate(SessionFactoryScope scope) {
|
||||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||||
final LocalDate startTime = roundToDefaultPrecision( LocalDate.now(), dialect );
|
final LocalDate startTime = adjustToDefaultPrecision( LocalDate.now(), dialect );
|
||||||
|
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
|
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
|
||||||
|
@ -178,7 +178,7 @@ public class JavaTimeJdbcTypeTests {
|
||||||
@SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Altibase drivers truncate fractional seconds from the LocalTime")
|
@SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Altibase drivers truncate fractional seconds from the LocalTime")
|
||||||
void testLocalTime(SessionFactoryScope scope) {
|
void testLocalTime(SessionFactoryScope scope) {
|
||||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||||
final LocalTime startTime = roundToDefaultPrecision( LocalTime.now(), dialect );
|
final LocalTime startTime = adjustToDefaultPrecision( LocalTime.now(), dialect );
|
||||||
|
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
|
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
|
||||||
|
|
|
@ -111,6 +111,23 @@ public class ToOneTests {
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void fkAccessTest(SessionFactoryScope scope) {
|
||||||
|
final SQLStatementInspector sqlInspector = scope.getCollectingStatementInspector();
|
||||||
|
sqlInspector.clear();
|
||||||
|
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
final Integer issue2Reporter = session.createQuery( "select i.reporter.id from Issue i where i.id = 2", Integer.class ).getSingleResultOrNull();
|
||||||
|
assertThat( issue2Reporter ).isNull();
|
||||||
|
|
||||||
|
assertThat( sqlInspector.getSqlQueries() ).hasSize( 1 );
|
||||||
|
assertThat( sqlInspector.getSqlQueries().get( 0 ) ).contains( " join " );
|
||||||
|
assertThat( sqlInspector.getSqlQueries().get( 0 ) ).contains( ".reporter_fk" );
|
||||||
|
assertThat( sqlInspector.getSqlQueries().get( 0 ) ).containsAnyOf( ".active='Y'", ".active=N'Y'" );
|
||||||
|
assertThat( sqlInspector.getSqlQueries().get( 0 ) ).containsOnlyOnce( "active" );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
@Entity(name="Issue")
|
@Entity(name="Issue")
|
||||||
@Table(name="issues")
|
@Table(name="issues")
|
||||||
public static class Issue {
|
public static class Issue {
|
||||||
|
|
|
@ -86,10 +86,10 @@ public class UTCNormalizedInstantTest {
|
||||||
});
|
});
|
||||||
scope.inSession( s-> {
|
scope.inSession( s-> {
|
||||||
final Zoned z = s.find(Zoned.class, id);
|
final Zoned z = s.find(Zoned.class, id);
|
||||||
Instant expected = DateTimeUtils.adjustToDefaultPrecision( z.utcInstant, dialect );
|
Instant expected = DateTimeUtils.adjustToDefaultPrecision( z.utcInstant, dialect );
|
||||||
Instant actual = DateTimeUtils.adjustToDefaultPrecision( instant, dialect );
|
Instant actual = DateTimeUtils.adjustToDefaultPrecision( instant, dialect );
|
||||||
assertEquals(
|
assertEquals(
|
||||||
expected,
|
expected,
|
||||||
actual
|
actual
|
||||||
);
|
);
|
||||||
expected = DateTimeUtils.adjustToDefaultPrecision( z.localInstant, dialect );
|
expected = DateTimeUtils.adjustToDefaultPrecision( z.localInstant, dialect );
|
||||||
|
|
Loading…
Reference in New Issue