HHH-17646 Optimize away real table group rendering if possible

This commit is contained in:
Christian Beikov 2024-01-30 17:47:32 +01:00
parent 9b1eb785e5
commit 3002fc31c0
48 changed files with 1513 additions and 258 deletions

View File

@ -76,7 +76,7 @@ protected boolean supportsWithClauseInSubquery() {
}
@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...
// See https://modern-sql.com/feature/with-recursive/db2/error-345-state-42836
if ( isInRecursiveQueryPart() ) {
@ -103,7 +103,7 @@ protected void renderTableReferenceJoins(TableGroup tableGroup) {
}
}
else {
super.renderTableReferenceJoins( tableGroup );
super.renderTableReferenceJoins( tableGroup, swappedJoinIndex, forceLeftJoin );
}
}

View File

@ -340,13 +340,9 @@ protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lo
final TableReference tableRef = tableGroup.getPrimaryTableReference();
// 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
if ( tableRef instanceof QueryPartTableReference || tableRef.getTableId().startsWith( "(select" ) ) {
final boolean realTableGroup = tableGroup.isRealTableGroup()
&& ( CollectionHelper.isNotEmpty( tableGroup.getTableReferenceJoins() )
|| hasNestedTableGroupsToRender( tableGroup.getNestedTableGroupJoins() ) );
if ( realTableGroup ) {
appendSql( "dual cross join " );
}
if ( getSqlBuffer().charAt( getSqlBuffer().length() - 1 ) == '('
&& ( tableRef instanceof QueryPartTableReference || tableRef.getTableId().startsWith( "(select" ) ) ) {
appendSql( "dual cross join " );
}
return super.renderPrimaryTableReference( tableGroup, lockMode );
}

View File

@ -12,20 +12,14 @@
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.sqm.BinaryArithmeticOperator;
import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
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.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.CaseSimpleExpression;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.SqlTuple;
@ -147,8 +141,7 @@ protected void visitRecursivePath(Expression recursivePath, int sizeEstimate) {
protected void visitAnsiCaseSearchedExpression(
CaseSearchedExpression expression,
Consumer<Expression> resultRenderer) {
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( expression )
|| areAllResultsPlainParametersOrLiterals( expression ) ) {
if ( areAllResultsPlainParametersOrStringLiterals( expression ) ) {
final List<CaseSearchedExpression.WhenFragment> whenFragments = expression.getWhenFragments();
final Expression firstResult = whenFragments.get( 0 ).getResult();
super.visitAnsiCaseSearchedExpression(
@ -172,8 +165,7 @@ protected void visitAnsiCaseSearchedExpression(
protected void visitAnsiCaseSimpleExpression(
CaseSimpleExpression expression,
Consumer<Expression> resultRenderer) {
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( expression )
|| areAllResultsPlainParametersOrLiterals( expression ) ) {
if ( areAllResultsPlainParametersOrStringLiterals( expression ) ) {
final List<CaseSimpleExpression.WhenFragment> whenFragments = expression.getWhenFragments();
final Expression firstResult = whenFragments.get( 0 ).getResult();
super.visitAnsiCaseSimpleExpression(
@ -193,11 +185,11 @@ protected void visitAnsiCaseSimpleExpression(
}
}
protected boolean areAllResultsPlainParametersOrLiterals(CaseSearchedExpression caseSearchedExpression) {
protected boolean areAllResultsPlainParametersOrStringLiterals(CaseSearchedExpression caseSearchedExpression) {
final List<CaseSearchedExpression.WhenFragment> whenFragments = caseSearchedExpression.getWhenFragments();
final Expression firstResult = whenFragments.get( 0 ).getResult();
if ( isParameter( firstResult ) && getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT
|| isLiteral( firstResult ) ) {
|| isStringLiteral( firstResult ) ) {
for ( int i = 1; i < whenFragments.size(); i++ ) {
final Expression result = whenFragments.get( i ).getResult();
if ( isParameter( result ) ) {
@ -205,7 +197,7 @@ protected boolean areAllResultsPlainParametersOrLiterals(CaseSearchedExpression
return false;
}
}
else if ( !isLiteral( result ) ) {
else if ( !isStringLiteral( result ) ) {
return false;
}
}
@ -214,11 +206,11 @@ else if ( !isLiteral( result ) ) {
return false;
}
protected boolean areAllResultsPlainParametersOrLiterals(CaseSimpleExpression caseSimpleExpression) {
protected boolean areAllResultsPlainParametersOrStringLiterals(CaseSimpleExpression caseSimpleExpression) {
final List<CaseSimpleExpression.WhenFragment> whenFragments = caseSimpleExpression.getWhenFragments();
final Expression firstResult = whenFragments.get( 0 ).getResult();
if ( isParameter( firstResult ) && getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT
|| isLiteral( firstResult ) ) {
|| isStringLiteral( firstResult ) ) {
for ( int i = 1; i < whenFragments.size(); i++ ) {
final Expression result = whenFragments.get( i ).getResult();
if ( isParameter( result ) ) {
@ -226,7 +218,7 @@ protected boolean areAllResultsPlainParametersOrLiterals(CaseSimpleExpression ca
return false;
}
}
else if ( !isLiteral( result ) ) {
else if ( !isStringLiteral( result ) ) {
return false;
}
}
@ -235,6 +227,13 @@ else if ( !isLiteral( result ) ) {
return false;
}
private boolean isStringLiteral( Expression expression ) {
if ( expression instanceof Literal ) {
return ( (Literal) expression ).getJdbcMapping().getJdbcType().isStringLike();
}
return false;
}
@Override
public boolean supportsFilterClause() {
return true;

View File

@ -75,7 +75,7 @@ protected boolean supportsWithClauseInSubquery() {
}
@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...
// See https://modern-sql.com/feature/with-recursive/db2/error-345-state-42836
if ( isInRecursiveQueryPart() ) {
@ -102,7 +102,7 @@ protected void renderTableReferenceJoins(TableGroup tableGroup) {
}
}
else {
super.renderTableReferenceJoins( tableGroup );
super.renderTableReferenceJoins( tableGroup, swappedJoinIndex, forceLeftJoin );
}
}

View File

@ -314,13 +314,9 @@ protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lo
final TableReference tableRef = tableGroup.getPrimaryTableReference();
// 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
if ( tableRef instanceof QueryPartTableReference || tableRef.getTableId().startsWith( "(select" ) ) {
final boolean realTableGroup = tableGroup.isRealTableGroup()
&& ( isNotEmpty( tableGroup.getTableReferenceJoins() )
|| hasNestedTableGroupsToRender( tableGroup.getNestedTableGroupJoins() ) );
if ( realTableGroup ) {
appendSql( "dual cross join " );
}
if ( getSqlBuffer().charAt( getSqlBuffer().length() - 1 ) == '('
&& ( tableRef instanceof QueryPartTableReference || tableRef.getTableId().startsWith( "(select" ) ) ) {
appendSql( "dual cross join " );
}
return super.renderPrimaryTableReference( tableGroup, lockMode );
}

View File

@ -12,14 +12,12 @@
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.sqm.BinaryArithmeticOperator;
import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlSelection;
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.CaseSimpleExpression;
import org.hibernate.sql.ast.tree.expression.Expression;
@ -152,8 +150,7 @@ protected void visitRecursivePath(Expression recursivePath, int sizeEstimate) {
protected void visitAnsiCaseSearchedExpression(
CaseSearchedExpression expression,
Consumer<Expression> resultRenderer) {
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( expression )
|| areAllResultsPlainParametersOrLiterals( expression ) ) {
if ( areAllResultsPlainParametersOrStringLiterals( expression ) ) {
final List<CaseSearchedExpression.WhenFragment> whenFragments = expression.getWhenFragments();
final Expression firstResult = whenFragments.get( 0 ).getResult();
super.visitAnsiCaseSearchedExpression(
@ -177,8 +174,7 @@ protected void visitAnsiCaseSearchedExpression(
protected void visitAnsiCaseSimpleExpression(
CaseSimpleExpression expression,
Consumer<Expression> resultRenderer) {
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( expression )
|| areAllResultsPlainParametersOrLiterals( expression ) ) {
if ( areAllResultsPlainParametersOrStringLiterals( expression ) ) {
final List<CaseSimpleExpression.WhenFragment> whenFragments = expression.getWhenFragments();
final Expression firstResult = whenFragments.get( 0 ).getResult();
super.visitAnsiCaseSimpleExpression(
@ -198,11 +194,11 @@ protected void visitAnsiCaseSimpleExpression(
}
}
protected boolean areAllResultsPlainParametersOrLiterals(CaseSearchedExpression caseSearchedExpression) {
protected boolean areAllResultsPlainParametersOrStringLiterals(CaseSearchedExpression caseSearchedExpression) {
final List<CaseSearchedExpression.WhenFragment> whenFragments = caseSearchedExpression.getWhenFragments();
final Expression firstResult = whenFragments.get( 0 ).getResult();
if ( isParameter( firstResult ) && getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT
|| isLiteral( firstResult ) ) {
|| isStringLiteral( firstResult ) ) {
for ( int i = 1; i < whenFragments.size(); i++ ) {
final Expression result = whenFragments.get( i ).getResult();
if ( isParameter( result ) ) {
@ -210,7 +206,7 @@ protected boolean areAllResultsPlainParametersOrLiterals(CaseSearchedExpression
return false;
}
}
else if ( !isLiteral( result ) ) {
else if ( !isStringLiteral( result ) ) {
return false;
}
}
@ -219,11 +215,11 @@ else if ( !isLiteral( result ) ) {
return false;
}
protected boolean areAllResultsPlainParametersOrLiterals(CaseSimpleExpression caseSimpleExpression) {
protected boolean areAllResultsPlainParametersOrStringLiterals(CaseSimpleExpression caseSimpleExpression) {
final List<CaseSimpleExpression.WhenFragment> whenFragments = caseSimpleExpression.getWhenFragments();
final Expression firstResult = whenFragments.get( 0 ).getResult();
if ( isParameter( firstResult ) && getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT
|| isLiteral( firstResult ) ) {
|| isStringLiteral( firstResult ) ) {
for ( int i = 1; i < whenFragments.size(); i++ ) {
final Expression result = whenFragments.get( i ).getResult();
if ( isParameter( result ) ) {
@ -231,7 +227,7 @@ protected boolean areAllResultsPlainParametersOrLiterals(CaseSimpleExpression ca
return false;
}
}
else if ( !isLiteral( result ) ) {
else if ( !isStringLiteral( result ) ) {
return false;
}
}
@ -240,6 +236,13 @@ else if ( !isLiteral( result ) ) {
return false;
}
private boolean isStringLiteral( Expression expression ) {
if ( expression instanceof Literal ) {
return ( (Literal) expression ).getJdbcMapping().getJdbcType().isStringLike();
}
return false;
}
@Override
public boolean supportsFilterClause() {
return true;

View File

@ -48,6 +48,8 @@
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl;
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Base implementation for composite identifier mappings
*
@ -129,9 +131,9 @@ public Fetch generateFetch(
public TableGroupJoin createTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType requestedJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType requestedJoinType,
boolean fetched,
boolean addsPredicate,
SqlAstCreationState creationState) {
@ -154,11 +156,11 @@ public TableGroupJoin createTableGroupJoin(
public TableGroup createRootTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType sqlAstJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType sqlAstJoinType,
boolean fetched,
Consumer<Predicate> predicateConsumer,
@Nullable Consumer<Predicate> predicateConsumer,
SqlAstCreationState creationState) {
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
}

View File

@ -321,6 +321,14 @@ default void pruneForSubclasses(TableGroup tableGroup, Set<String> treatedEntity
*/
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
* entity mappings within an inheritance hierarchy.

View File

@ -6,7 +6,9 @@
*/
package org.hibernate.metamodel.mapping.internal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -255,6 +257,24 @@ public CaseStatementDiscriminatorExpression(TableGroup 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
public void renderToSql(
SqlAppender sqlAppender,

View File

@ -52,6 +52,8 @@
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Singular, any-valued attribute
*
@ -487,9 +489,9 @@ public Object assemble(Serializable cached, SharedSessionContract session) {
public TableGroupJoin createTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType requestedJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType requestedJoinType,
boolean fetched,
boolean addsPredicate,
SqlAstCreationState creationState) {
@ -512,11 +514,11 @@ public TableGroupJoin createTableGroupJoin(
public TableGroup createRootTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType sqlAstJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType sqlAstJoinType,
boolean fetched,
Consumer<Predicate> predicateConsumer,
@Nullable Consumer<Predicate> predicateConsumer,
SqlAstCreationState creationState) {
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
}

View File

@ -45,6 +45,10 @@
import org.hibernate.type.AnyType;
import org.hibernate.type.descriptor.java.JavaType;
import org.checkerframework.checker.nullness.qual.Nullable;
import static java.util.Objects.requireNonNullElse;
/**
* @author Steve Ebersole
*/
@ -343,19 +347,13 @@ public int forEachJdbcType(int offset, IndexedConsumer<JdbcMapping> action) {
public TableGroupJoin createTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType requestedJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType requestedJoinType,
boolean fetched,
boolean addsPredicate,
SqlAstCreationState creationState) {
final SqlAstJoinType joinType;
if ( requestedJoinType == null ) {
joinType = SqlAstJoinType.INNER;
}
else {
joinType = requestedJoinType;
}
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
final TableGroup tableGroup = createRootTableGroupJoin(
navigablePath,
lhs,
@ -374,11 +372,11 @@ public TableGroupJoin createTableGroupJoin(
public TableGroup createRootTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType sqlAstJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType sqlAstJoinType,
boolean fetched,
Consumer<Predicate> predicateConsumer,
@Nullable Consumer<Predicate> predicateConsumer,
SqlAstCreationState creationState) {
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
}

View File

@ -53,6 +53,10 @@
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl;
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
*/
@ -318,13 +322,13 @@ public SqlTuple toSqlExpression(
public TableGroupJoin createTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType requestedJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType requestedJoinType,
boolean fetched,
boolean addsPredicate,
SqlAstCreationState creationState) {
final SqlAstJoinType joinType = requestedJoinType == null ? SqlAstJoinType.INNER : requestedJoinType;
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
final TableGroup tableGroup = createRootTableGroupJoin(
navigablePath,
lhs,
@ -343,11 +347,11 @@ public TableGroupJoin createTableGroupJoin(
public TableGroup createRootTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType sqlAstJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType sqlAstJoinType,
boolean fetched,
Consumer<Predicate> predicateConsumer,
@Nullable Consumer<Predicate> predicateConsumer,
SqlAstCreationState creationState) {
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
}

View File

@ -13,7 +13,6 @@
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EntityMappingType;
@ -21,7 +20,6 @@
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl;
@ -41,7 +39,6 @@
import org.hibernate.sql.ast.tree.from.StandardVirtualTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup;
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.predicate.Predicate;
import org.hibernate.sql.results.graph.DomainResult;
@ -49,12 +46,15 @@
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchOptions;
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.internal.EmbeddableFetchImpl;
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
import org.hibernate.type.descriptor.java.JavaType;
import org.checkerframework.checker.nullness.qual.Nullable;
import static java.util.Objects.requireNonNullElse;
/**
* @author Steve Ebersole
*/
@ -231,19 +231,13 @@ public SqlTuple toSqlExpression(
public TableGroupJoin createTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType requestedJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType requestedJoinType,
boolean fetched,
boolean addsPredicate,
SqlAstCreationState creationState) {
final SqlAstJoinType joinType;
if ( requestedJoinType == null ) {
joinType = SqlAstJoinType.INNER;
}
else {
joinType = requestedJoinType;
}
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
final TableGroup tableGroup = createRootTableGroupJoin(
navigablePath,
lhs,
@ -262,11 +256,11 @@ public TableGroupJoin createTableGroupJoin(
public TableGroup createRootTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType sqlAstJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType sqlAstJoinType,
boolean fetched,
Consumer<Predicate> predicateConsumer,
@Nullable Consumer<Predicate> predicateConsumer,
SqlAstCreationState creationState) {
assert lhs.getModelPart() instanceof PluralAttributeMapping;
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );

View File

@ -56,6 +56,8 @@
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.type.EntityType;
import org.checkerframework.checker.nullness.qual.Nullable;
import static java.util.Objects.requireNonNullElse;
import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper.createInverseModelPart;
import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper.getPropertyOrder;
@ -258,14 +260,13 @@ public ModelPart getKeyTargetMatchPart() {
public TableGroupJoin createTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType requestedJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType requestedJoinType,
boolean fetched,
boolean addsPredicate,
SqlAstCreationState creationState) {
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
final LazyTableGroup lazyTableGroup = createRootTableGroupJoin(
navigablePath,
lhs,
@ -302,11 +303,11 @@ public TableGroupJoin createTableGroupJoin(
public LazyTableGroup createRootTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType requestedJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType requestedJoinType,
boolean fetched,
Consumer<Predicate> predicateConsumer,
@Nullable Consumer<Predicate> predicateConsumer,
SqlAstCreationState creationState) {
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
final boolean canUseInnerJoin = joinType == SqlAstJoinType.INNER || lhs.canUseInnerJoins();
@ -393,7 +394,8 @@ else if ( getNature() == Nature.INDEX ) {
fkTargetModelPart = resolveNamedTargetPart( mapKeyPropertyName, entityMappingType, collectionDescriptor );
}
else {
fkTargetModelPart = getAssociatedEntityMappingType().getIdentifierMapping();
fkTargetModelPart = getAssociatedEntityMappingType().getIdentifierMappingForJoin();
// fkTargetModelPart = getAssociatedEntityMappingType().getIdentifierMapping();
}
}
else if ( StringHelper.isNotEmpty( bootCollectionDescriptor.getMappedByProperty() ) ) {
@ -449,7 +451,8 @@ else if ( StringHelper.isNotEmpty( bootCollectionDescriptor.getMappedByProperty(
}
else {
// non-inverse @ManyToMany
fkTargetModelPart = getAssociatedEntityMappingType().getIdentifierMapping();
fkTargetModelPart = getAssociatedEntityMappingType().getIdentifierMappingForJoin();
// fkTargetModelPart = getAssociatedEntityMappingType().getIdentifierMapping();
}
if ( getNature() == Nature.ELEMENT ) {

View File

@ -120,6 +120,8 @@
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
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.metamodel.mapping.MappingModelCreationLogging.MAPPING_MODEL_CREATION_MESSAGE_LOGGER;
import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey;
@ -781,7 +783,8 @@ private static void interpretPluralAttributeMappingKeyDescriptor(
}
if ( isReferenceToPrimaryKey ) {
fkTargetPart = collectionDescriptor.getOwnerEntityPersister().getIdentifierMapping();
fkTargetPart = collectionDescriptor.getOwnerEntityPersister().getIdentifierMappingForJoin();
// fkTargetPart = collectionDescriptor.getOwnerEntityPersister().getIdentifierMapping();
}
else {
fkTargetPart = declaringType.findContainingEntityMapping().findSubPart( lhsPropertyName );
@ -934,7 +937,8 @@ else if ( modelPart == null ) {
final ModelPart fkTarget;
if ( bootValueMapping.isReferenceToPrimaryKey() ) {
fkTarget = referencedEntityDescriptor.getIdentifierMapping();
fkTarget = referencedEntityDescriptor.getIdentifierMappingForJoin();
// fkTarget = referencedEntityDescriptor.getIdentifierMapping();
}
else {
fkTarget = referencedEntityDescriptor.findByPath( bootValueMapping.getReferencedPropertyName() );
@ -1683,9 +1687,9 @@ public boolean isSimpleJoinPredicate(Predicate predicate) {
public TableGroupJoin createTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType sqlAstJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType sqlAstJoinType,
boolean fetched,
boolean addsPredicate,
SqlAstCreationState creationState) {
@ -1696,11 +1700,11 @@ public TableGroupJoin createTableGroupJoin(
public TableGroup createRootTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType sqlAstJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType sqlAstJoinType,
boolean fetched,
Consumer<Predicate> predicateConsumer,
@Nullable Consumer<Predicate> predicateConsumer,
SqlAstCreationState creationState) {
return null;
}

View File

@ -31,6 +31,8 @@
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.checkerframework.checker.nullness.qual.Nullable;
import static java.util.Objects.requireNonNullElse;
/**
@ -154,9 +156,9 @@ public boolean isSimpleJoinPredicate(Predicate predicate) {
public TableGroupJoin createTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType requestedJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType requestedJoinType,
boolean fetched,
boolean addsPredicate,
SqlAstCreationState creationState) {
@ -193,11 +195,11 @@ public TableGroupJoin createTableGroupJoin(
public TableGroup createRootTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType sqlAstJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType sqlAstJoinType,
boolean fetched,
Consumer<Predicate> predicateConsumer,
@Nullable Consumer<Predicate> predicateConsumer,
SqlAstCreationState creationState) {
return createTableGroupInternal(
true,

View File

@ -76,6 +76,8 @@
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.resolveSoftDeleteMapping;
@ -684,9 +686,9 @@ public boolean isSimpleJoinPredicate(Predicate predicate) {
public TableGroupJoin createTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType requestedJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType requestedJoinType,
boolean fetched,
boolean addsPredicate,
SqlAstCreationState creationState) {
@ -803,7 +805,7 @@ private void applySoftDeleteRestriction(
}
}
public SqlAstJoinType determineSqlJoinType(TableGroup lhs, SqlAstJoinType requestedJoinType, boolean fetched) {
public SqlAstJoinType determineSqlJoinType(TableGroup lhs, @Nullable SqlAstJoinType requestedJoinType, boolean fetched) {
if ( hasSoftDelete() ) {
return SqlAstJoinType.LEFT;
}
@ -825,11 +827,11 @@ public SqlAstJoinType determineSqlJoinType(TableGroup lhs, SqlAstJoinType reques
public TableGroup createRootTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType requestedJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType requestedJoinType,
boolean fetched,
Consumer<Predicate> predicateConsumer,
@Nullable Consumer<Predicate> predicateConsumer,
SqlAstCreationState creationState) {
return createRootTableGroupJoin(
navigablePath,
@ -862,8 +864,10 @@ private TableGroup createRootTableGroupJoin(
if ( collectionDescriptor.isOneToMany() ) {
tableGroup = createOneToManyTableGroup(
lhs.canUseInnerJoins() && joinType == SqlAstJoinType.INNER,
joinType,
navigablePath,
fetched,
addsPredicate,
explicitSourceAlias,
sqlAliasBase,
creationState
@ -897,8 +901,10 @@ public void setForeignKeyDescriptor(ForeignKeyDescriptor fkDescriptor) {
private TableGroup createOneToManyTableGroup(
boolean canUseInnerJoins,
SqlAstJoinType joinType,
NavigablePath navigablePath,
boolean fetched,
boolean addsPredicate,
String sourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstCreationState creationState) {
@ -921,6 +927,12 @@ private TableGroup createOneToManyTableGroup(
elementTableGroup,
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 ) {
final TableGroupJoin tableGroupJoin = ( (TableGroupJoinProducer) indexDescriptor ).createTableGroupJoin(
@ -933,7 +945,7 @@ private TableGroup createOneToManyTableGroup(
false,
creationState
);
tableGroup.registerIndexTableGroup( tableGroupJoin );
tableGroup.registerIndexTableGroup( tableGroupJoin, nestedJoin );
}
return tableGroup;
@ -1023,8 +1035,10 @@ public TableGroup createRootTableGroup(
if ( getCollectionDescriptor().isOneToMany() ) {
return createOneToManyTableGroup(
canUseInnerJoins,
SqlAstJoinType.INNER,
navigablePath,
false,
false,
explicitSourceAlias,
explicitSqlAliasBase,
creationState

View File

@ -67,6 +67,7 @@
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.spi.EntityIdentifierNavigablePath;
import org.hibernate.spi.NavigablePath;
import org.hibernate.spi.TreatedNavigablePath;
@ -107,13 +108,14 @@
import org.hibernate.sql.results.internal.NullValueAssembler;
import org.hibernate.sql.results.internal.domain.CircularBiDirectionalFetchImpl;
import org.hibernate.sql.results.internal.domain.CircularFetchImpl;
import org.hibernate.type.ComponentType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EmbeddedComponentType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.JavaType;
import org.checkerframework.checker.nullness.qual.Nullable;
import static org.hibernate.boot.model.internal.SoftDeleteHelper.createNonSoftDeletedRestriction;
/**
@ -858,6 +860,7 @@ public void setForeignKeyDescriptor(ForeignKeyDescriptor foreignKeyDescriptor) {
// * the association does not force a join (`@NotFound`, nullable 1-1, ...)
// Otherwise we need to join to the associated entity table(s)
final boolean forceJoin = hasNotFoundAction()
|| entityMappingType.getSoftDeleteMapping() != null
|| ( cardinality == Cardinality.ONE_TO_ONE && isNullable() );
this.canUseParentTableGroup = ! forceJoin
&& sideNature == ForeignKeyDescriptor.Nature.KEY
@ -1981,9 +1984,9 @@ public Fetchable getFetchable(int position) {
public TableGroupJoin createTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType requestedJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType requestedJoinType,
boolean fetched,
boolean addsPredicate,
SqlAstCreationState creationState) {
@ -2075,7 +2078,11 @@ public TableGroupJoin createTableGroupJoin(
final TableGroupJoin join = new TableGroupJoin(
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,
null
);
@ -2147,7 +2154,7 @@ public TableGroupJoin createTableGroupJoin(
}
@Override
public SqlAstJoinType determineSqlJoinType(TableGroup lhs, SqlAstJoinType requestedJoinType, boolean fetched) {
public SqlAstJoinType determineSqlJoinType(TableGroup lhs, @Nullable SqlAstJoinType requestedJoinType, boolean fetched) {
if ( requestedJoinType != null ) {
return requestedJoinType;
}
@ -2163,11 +2170,11 @@ public SqlAstJoinType determineSqlJoinType(TableGroup lhs, SqlAstJoinType reques
public LazyTableGroup createRootTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType requestedJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType requestedJoinType,
boolean fetched,
Consumer<Predicate> predicateConsumer,
@Nullable Consumer<Predicate> predicateConsumer,
SqlAstCreationState creationState) {
final SqlAliasBase sqlAliasBase = SqlAliasBase.from(
explicitSqlAliasBase,
@ -2177,18 +2184,16 @@ public LazyTableGroup createRootTableGroupJoin(
);
final SoftDeleteMapping softDeleteMapping = getAssociatedEntityMappingType().getSoftDeleteMapping();
final boolean canUseInnerJoin;
if ( ! lhs.canUseInnerJoins() ) {
canUseInnerJoin = false;
}
else if ( isNullable
|| hasNotFoundAction()
|| softDeleteMapping != null ) {
final SqlAstJoinType currentlyProcessingJoinType = creationState instanceof SqmToSqlAstConverter
? ( (SqmToSqlAstConverter) creationState ).getCurrentlyProcessingJoinType()
: null;
if ( currentlyProcessingJoinType != null && currentlyProcessingJoinType != SqlAstJoinType.INNER ) {
// Don't change the join type though, as that has implications for eager initialization of a LazyTableGroup
canUseInnerJoin = false;
}
else {
canUseInnerJoin = requestedJoinType == SqlAstJoinType.INNER;
canUseInnerJoin = determineSqlJoinType( lhs, requestedJoinType, fetched ) == SqlAstJoinType.INNER;
}
TableGroup realParentTableGroup = lhs;

View File

@ -6176,6 +6176,7 @@ public ModelPart findSubTypesSubPart(String name, EntityMappingType treatTargetT
}
private ModelPart getIdentifierModelPart(String name, EntityMappingType treatTargetType) {
final EntityIdentifierMapping identifierMapping = getIdentifierMappingForJoin();
if ( identifierMapping instanceof NonAggregatedIdentifierMapping ) {
NonAggregatedIdentifierMapping mapping = (NonAggregatedIdentifierMapping) identifierMapping;
final ModelPart subPart = mapping.findSubPart( name, treatTargetType );

View File

@ -1421,6 +1421,15 @@ public void pruneForSubclasses(TableGroup tableGroup, Map<String, EntityNameUse>
}
}
@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(
TableReferenceJoin join,
NamedTableReference tableReference,
@ -1431,9 +1440,11 @@ private boolean applyDiscriminatorPredicate(
final String discriminatorPredicate = getPrunedDiscriminatorPredicate(
entityNameUses,
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 false;

View File

@ -63,6 +63,9 @@
import org.hibernate.type.descriptor.java.JavaType;
import jakarta.persistence.metamodel.Attribute;
import org.checkerframework.checker.nullness.qual.Nullable;
import static java.util.Objects.requireNonNullElse;
/**
* @author Christian Beikov
@ -346,13 +349,13 @@ public boolean isSimpleJoinPredicate(Predicate predicate) {
public TableGroupJoin createTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType requestedJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType requestedJoinType,
boolean fetched,
boolean addsPredicate,
SqlAstCreationState creationState) {
final SqlAstJoinType joinType = requestedJoinType == null ? SqlAstJoinType.INNER : requestedJoinType;
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
final TableGroup tableGroup = createRootTableGroupJoin(
navigablePath,
lhs,
@ -371,18 +374,13 @@ public TableGroupJoin createTableGroupJoin(
public TableGroup createRootTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType sqlAstJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType sqlAstJoinType,
boolean fetched,
Consumer<Predicate> predicateConsumer,
@Nullable Consumer<Predicate> predicateConsumer,
SqlAstCreationState creationState) {
return new StandardVirtualTableGroup(
navigablePath,
this,
lhs,
fetched
);
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
}
@Override

View File

@ -70,6 +70,8 @@
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.type.descriptor.java.JavaType;
import org.checkerframework.checker.nullness.qual.Nullable;
import static java.util.Objects.requireNonNullElse;
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
@ -245,14 +247,13 @@ public JavaType<?> getMappedJavaType() {
public TableGroupJoin createTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType requestedJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType requestedJoinType,
boolean fetched,
boolean addsPredicate,
SqlAstCreationState creationState) {
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
final LazyTableGroup lazyTableGroup = createRootTableGroupJoin(
navigablePath,
lhs,
@ -441,11 +442,11 @@ public TableGroup createTableGroupInternal(
public LazyTableGroup createRootTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType sqlAstJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType sqlAstJoinType,
boolean fetched,
Consumer<Predicate> predicateConsumer,
@Nullable Consumer<Predicate> predicateConsumer,
SqlAstCreationState creationState) {
final SqlAliasBase sqlAliasBase = SqlAliasBase.from(
explicitSqlAliasBase,

View File

@ -41,8 +41,11 @@
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
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.SingularPersistentAttribute;
import org.hibernate.metamodel.model.domain.internal.EntitySqmPathSource;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.IllegalSelectQueryException;
import org.hibernate.query.Order;
@ -63,6 +66,7 @@
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.SqmStatement;
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.SqmAliasedNodeRef;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
@ -102,6 +106,7 @@
import org.hibernate.type.spi.TypeConfiguration;
import jakarta.persistence.Tuple;
import jakarta.persistence.metamodel.Type;
import org.checkerframework.checker.nullness.qual.Nullable;
import static java.util.stream.Collectors.toList;
@ -189,7 +194,7 @@ public static ModelPartContainer getTargetMappingIfNeeded(
// we need to render the target side if in group/order by
if ( association.getTargetKeyPropertyNames().contains( sqmPath.getReferencedPathSource().getPathName() )
&& ( clause == Clause.GROUP || clause == Clause.ORDER
|| !isFkOptimizationAllowed( sqmPath.getLhs() )
|| !isFkOptimizationAllowed( sqmPath.getLhs(), association )
|| queryPart.getFirstQuerySpec().groupByClauseContains( sqmPath.getNavigablePath(), sqlAstCreationState )
|| queryPart.getFirstQuerySpec().orderByClauseContains( sqmPath.getNavigablePath(), sqlAstCreationState ) ) ) {
return association.getAssociatedEntityMappingType();
@ -218,13 +223,21 @@ private static CollectionPart getCollectionPart(PluralAttributeMapping attribute
* 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.
*
* @deprecated Use {@link #isFkOptimizationAllowed(SqmPath, EntityAssociationMapping)} instead
*/
@Deprecated(forRemoval = true, since = "6.6.1")
public static boolean isFkOptimizationAllowed(SqmPath<?> sqmPath) {
if ( sqmPath instanceof SqmJoin<?, ?> ) {
final SqmJoin<?, ?> sqmJoin = (SqmJoin<?, ?>) sqmPath;
switch ( sqmJoin.getSqmJoinType() ) {
case INNER:
case LEFT:
final EntityAssociationMapping associationMapping = resolveAssociationMapping( sqmJoin );
if ( associationMapping != null && isFiltered( associationMapping ) ) {
return false;
}
// FallThrough intended
case INNER:
return !( sqmJoin instanceof SqmQualifiedJoin<?, ?>)
|| ( (SqmQualifiedJoin<?, ?>) sqmJoin ).getJoinPredicate() == null;
default:
@ -234,6 +247,84 @@ public static boolean isFkOptimizationAllowed(SqmPath<?> sqmPath) {
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 instanceof SqmQualifiedJoin<?, ?>)
|| ( (SqmQualifiedJoin<?, ?>) 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) {
final SqmWhereClause where = querySpec.getWhereClause();
if ( where == null || where.getPredicate() == null ) {

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.query.sqm.sql;
import jakarta.annotation.Nullable;
import jakarta.persistence.TemporalType;
import jakarta.persistence.metamodel.SingularAttribute;
import jakarta.persistence.metamodel.Type;
@ -3925,7 +3926,7 @@ private TableGroup createTableGroup(TableGroup parentTableGroup, SqmPath<?> join
this
);
// 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 ) {
parentTableGroup.addNestedTableGroupJoin( tableGroupJoin );
}
@ -3999,6 +4000,13 @@ private void registerPluralTableGroupParts(NavigablePath navigablePath, TableGro
}
}
@Override
public @Nullable SqlAstJoinType getCurrentlyProcessingJoinType() {
return currentlyProcessingJoin == null
? null
: currentlyProcessingJoin.getSqmJoinType().getCorrespondingSqlJoinType();
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SqmPath handling
// - Note that SqmFrom references defined in the FROM-clause are already
@ -4128,8 +4136,7 @@ private Expression visitTableGroup(TableGroup tableGroup, SqmPath<?> path) {
// 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
if ( entityValuedModelPart instanceof EntityAssociationMapping
&& ( (EntityAssociationMapping) entityValuedModelPart ).isFkOptimizationAllowed()
&& isFkOptimizationAllowed( path ) ) {
&& isFkOptimizationAllowed( path, (EntityAssociationMapping) entityValuedModelPart ) ) {
// 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,
// for which we should always use the target's identifier to preserve semantics
@ -8404,7 +8411,7 @@ else if ( getLoadQueryInfluencers().hasEnabledFetchProfiles() ) {
final TableGroupJoinProducer joinProducer = (TableGroupJoinProducer) fetchable;
final TableGroup compatibleTableGroup = lhs.findCompatibleJoinedGroup(
joinProducer,
joinProducer.determineSqlJoinType( lhs, null, true )
joinProducer.getDefaultSqlAstJoinType( lhs )
);
final SqmQueryPart<?> queryPart = getCurrentSqmQueryPart();
if ( compatibleTableGroup == null

View File

@ -21,6 +21,7 @@
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
import org.hibernate.query.sqm.tree.select.SqmQueryPart;
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.SqlAliasBaseGenerator;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
@ -31,6 +32,8 @@
import org.hibernate.sql.ast.tree.expression.QueryTransformer;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import jakarta.annotation.Nullable;
/**
*
*/
@ -101,6 +104,11 @@ public SqmQueryPart<?> getCurrentSqmQueryPart() {
public void registerQueryTransformer(QueryTransformer transformer) {
}
@Override
public @Nullable SqlAstJoinType getCurrentlyProcessingJoinType() {
return null;
}
@Override
public boolean isInTypeInference() {
return false;

View File

@ -18,6 +18,7 @@
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
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.Clause;
import org.hibernate.sql.ast.tree.expression.Expression;
@ -38,6 +39,12 @@ public interface SqmToSqlAstConverter extends SemanticQueryWalker<Object>, SqlAs
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.
* This is useful to avoid type inference based on other incomplete inference information.

View File

@ -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() );
}
}

View File

@ -89,6 +89,7 @@
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator;
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.tree.AbstractUpdateOrDeleteStatement;
import org.hibernate.sql.ast.tree.MutationStatement;
@ -6035,10 +6036,60 @@ protected void renderRootTableGroup(TableGroup tableGroup, List<TableGroupJoin>
}
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 = tableGroup.isRealTableGroup()
&& ( CollectionHelper.isNotEmpty( tableGroup.getTableReferenceJoins() )
|| hasNestedTableGroupsToRender( tableGroup.getNestedTableGroupJoins() ) );
final boolean realTableGroup;
int swappedJoinIndex = -1;
boolean forceLeftJoin = false;
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 ) {
appendSql( OPEN_PARENTHESIS );
}
@ -6066,7 +6117,8 @@ protected void renderTableGroup(TableGroup tableGroup, Predicate predicate, List
tableGroupJoins = null;
}
if ( predicate != null ) {
// Predicate was already rendered when swappedJoinIndex is not equal to -1
if ( predicate != null && swappedJoinIndex == -1 ) {
appendSql( " on " );
predicate.accept( this );
}
@ -6084,7 +6136,7 @@ protected void renderTableGroup(TableGroup tableGroup, Predicate predicate, List
}
if ( !realTableGroup ) {
renderTableReferenceJoins( tableGroup );
renderTableReferenceJoins( tableGroup, swappedJoinIndex, forceLeftJoin );
processNestedTableGroupJoins( tableGroup, tableGroupJoinCollector );
}
if ( tableGroupJoinCollector != null ) {
@ -6363,21 +6415,43 @@ protected void registerAffectedTable(String tableExpression) {
}
protected void renderTableReferenceJoins(TableGroup tableGroup) {
renderTableReferenceJoins( tableGroup, -1, false );
}
protected void renderTableReferenceJoins(TableGroup tableGroup, int swappedJoinIndex, boolean forceLeftJoin) {
final List<TableReferenceJoin> joins = tableGroup.getTableReferenceJoins();
if ( joins == null || joins.isEmpty() ) {
return;
}
for ( TableReferenceJoin tableJoin : joins ) {
appendSql( WHITESPACE );
appendSql( tableJoin.getJoinType().getText() );
appendSql( "join " );
renderNamedTableReference( tableJoin.getJoinedTableReference(), LockMode.NONE );
if ( tableJoin.getPredicate() != null && !tableJoin.getPredicate().isEmpty() ) {
if ( swappedJoinIndex != -1 ) {
// Finish the join against the primary table reference after the swap
final TableReferenceJoin swappedJoin = joins.get( swappedJoinIndex );
if ( swappedJoin.getPredicate() != null && !swappedJoin.getPredicate().isEmpty() ) {
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 );
}
}
}
}

View File

@ -63,9 +63,18 @@ public TableGroup getIndexTableGroup() {
}
public void registerIndexTableGroup(TableGroupJoin indexTableGroupJoin) {
registerIndexTableGroup( indexTableGroupJoin, true );
}
public void registerIndexTableGroup(TableGroupJoin indexTableGroupJoin, boolean nested) {
assert this.indexTableGroup == null;
this.indexTableGroup = indexTableGroupJoin.getJoinedGroup();
addNestedTableGroupJoin( indexTableGroupJoin );
if ( nested ) {
addNestedTableGroupJoin( indexTableGroupJoin );
}
else {
addTableGroupJoin( indexTableGroupJoin );
}
}
@Override

View File

@ -12,10 +12,11 @@
import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.SqlAliasBase;
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.tree.predicate.Predicate;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* @author Steve Ebersole
*/
@ -41,9 +42,9 @@ public interface TableGroupJoinProducer extends TableGroupProducer {
TableGroupJoin createTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType sqlAstJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType sqlAstJoinType,
boolean fetched,
boolean addsPredicate,
SqlAstCreationState creationState);
@ -70,14 +71,14 @@ TableGroupJoin createTableGroupJoin(
TableGroup createRootTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAliasBase explicitSqlAliasBase,
SqlAstJoinType sqlAstJoinType,
@Nullable String explicitSourceAlias,
@Nullable SqlAliasBase explicitSqlAliasBase,
@Nullable SqlAstJoinType sqlAstJoinType,
boolean fetched,
Consumer<Predicate> predicateConsumer,
@Nullable Consumer<Predicate> predicateConsumer,
SqlAstCreationState creationState);
default SqlAstJoinType determineSqlJoinType(TableGroup lhs, SqlAstJoinType requestedJoinType, boolean fetched) {
default SqlAstJoinType determineSqlJoinType(TableGroup lhs, @Nullable SqlAstJoinType requestedJoinType, boolean fetched) {
if ( requestedJoinType != null ) {
return requestedJoinType;
}

View File

@ -477,7 +477,10 @@ public static <T extends Temporal> T truncateToPrecision(T temporal, int precisi
/**
* 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.
*
* @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) {
final int defaultTimestampPrecision = d.getDefaultTimestampPrecision();
if ( defaultTimestampPrecision >= 9 || !temporal.isSupported( ChronoField.NANO_OF_SECOND ) ) {
@ -491,6 +494,9 @@ public static <T extends Temporal> T roundToDefaultPrecision(T temporal, Dialect
}
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 ) {
//noinspection unchecked
return temporal.get( ChronoField.NANO_OF_SECOND ) >= 500_000_000L

View File

@ -5,10 +5,8 @@
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
//@Disabled("test produces broken SQL and issue needs to be fixed")
@TestForIssue(jiraKey = "HHH-15933")
@SessionFactory
@DomainModel(annotatedClasses = { Split.class, Reference.class })

View File

@ -57,6 +57,33 @@ public void cleanup(SessionFactoryScope scope) {
} );
}
@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
public void testLeftJoin(SessionFactoryScope scope) {
scope.inTransaction( s -> {

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -17,7 +17,6 @@
import org.hibernate.testing.orm.junit.Jpa;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -411,7 +410,6 @@ public void testJoinOrderWithRightJoinWithInnerImplicitJoins(EntityManagerFactor
}
@Test
@Disabled("Hibernate doesn't support implicit joins")
public void testJoinOrderWithRightNormalJoinWithInnerImplicitJoins(EntityManagerFactoryScope scope) {
scope.inTransaction( em -> {
List<Tuple> resultList = em.createQuery(

View File

@ -35,7 +35,7 @@
)
public class LocalTimeTest {
private static final LocalTime LOCAL_TIME = DateTimeUtils.roundToDefaultPrecision(
private static final LocalTime LOCAL_TIME = DateTimeUtils.adjustToDefaultPrecision(
LocalTime.now(),
DialectContext.getDialect()
);

View File

@ -8,7 +8,6 @@
import java.util.Set;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.jdbc.SQLStatementInspector;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
@ -21,19 +20,28 @@
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
@TestForIssue(jiraKey = "HHH-16691")
@DomainModel(
annotatedClasses = {
JoinTableOptimizationTest.Document.class, JoinTableOptimizationTest.Person.class
JoinTableOptimizationTest.Document.class,
JoinTableOptimizationTest.Person.class,
JoinTableOptimizationTest.File.class,
JoinTableOptimizationTest.Picture.class
})
@SessionFactory(useCollectingStatementInspector = true)
public class JoinTableOptimizationTest {
@Test
@JiraKey("HHH-16691")
public void testOnlyCollectionTableJoined(SessionFactoryScope scope) {
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
statementInspector.clear();
@ -48,6 +56,7 @@ public void testOnlyCollectionTableJoined(SessionFactoryScope scope) {
}
@Test
@JiraKey("HHH-16691")
public void testInnerJoin(SessionFactoryScope scope) {
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
statementInspector.clear();
@ -68,6 +77,7 @@ public void testInnerJoin(SessionFactoryScope scope) {
}
@Test
@JiraKey("HHH-16691")
public void testLeftJoin(SessionFactoryScope scope) {
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
statementInspector.clear();
@ -88,6 +98,7 @@ public void testLeftJoin(SessionFactoryScope scope) {
}
@Test
@JiraKey("HHH-16691")
public void testInnerJoinCustomOnClause(SessionFactoryScope scope) {
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
statementInspector.clear();
@ -108,6 +119,7 @@ public void testInnerJoinCustomOnClause(SessionFactoryScope scope) {
}
@Test
@JiraKey("HHH-16691")
public void testLeftJoinCustomOnClause(SessionFactoryScope scope) {
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
statementInspector.clear();
@ -147,6 +159,199 @@ public void testElementCollectionJoinCustomOnClause(SessionFactoryScope scope) {
);
}
@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")
public static class Document {
@Id
@ -158,6 +363,16 @@ public static class Document {
@ElementCollection
@CollectionTable(name = "document_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")
public static class Person {
@ -165,6 +380,20 @@ public static class Person {
Long id;
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
public static class Page {
String text;

View File

@ -44,7 +44,7 @@
import jakarta.persistence.Table;
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.
@ -84,7 +84,7 @@ private void checkAttribute(
@Test
void testInstant(SessionFactoryScope scope) {
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
final Instant start = roundToDefaultPrecision( Instant.EPOCH, dialect );
final Instant start = adjustToDefaultPrecision( Instant.EPOCH, dialect );
scope.inTransaction( (session) -> {
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
@ -114,7 +114,7 @@ void testInstant(SessionFactoryScope scope) {
@Test
void testLocalDateTime(SessionFactoryScope scope) {
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
final LocalDateTime start = roundToDefaultPrecision( LocalDateTime.now(), dialect );
final LocalDateTime start = adjustToDefaultPrecision( LocalDateTime.now(), dialect );
scope.inTransaction( (session) -> {
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
@ -144,7 +144,7 @@ void testLocalDateTime(SessionFactoryScope scope) {
@Test
void testLocalDate(SessionFactoryScope scope) {
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
final LocalDate startTime = roundToDefaultPrecision( LocalDate.now(), dialect );
final LocalDate startTime = adjustToDefaultPrecision( LocalDate.now(), dialect );
scope.inTransaction( (session) -> {
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
@ -177,7 +177,7 @@ void testLocalDate(SessionFactoryScope scope) {
@SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Altibase drivers truncate fractional seconds from the LocalTime")
void testLocalTime(SessionFactoryScope scope) {
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
final LocalTime startTime = roundToDefaultPrecision( LocalTime.now(), dialect );
final LocalTime startTime = adjustToDefaultPrecision( LocalTime.now(), dialect );
scope.inTransaction( (session) -> {
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();

View File

@ -45,7 +45,7 @@
import jakarta.persistence.Table;
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.
@ -85,7 +85,7 @@ private void checkAttribute(
@Test
void testInstant(SessionFactoryScope scope) {
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
final Instant start = roundToDefaultPrecision( Instant.EPOCH, dialect );
final Instant start = adjustToDefaultPrecision( Instant.EPOCH, dialect );
scope.inTransaction( (session) -> {
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
@ -115,7 +115,7 @@ void testInstant(SessionFactoryScope scope) {
@Test
void testLocalDateTime(SessionFactoryScope scope) {
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
final LocalDateTime start = roundToDefaultPrecision( LocalDateTime.now(), dialect );
final LocalDateTime start = adjustToDefaultPrecision( LocalDateTime.now(), dialect );
scope.inTransaction( (session) -> {
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
@ -145,7 +145,7 @@ void testLocalDateTime(SessionFactoryScope scope) {
@Test
void testLocalDate(SessionFactoryScope scope) {
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
final LocalDate startTime = roundToDefaultPrecision( LocalDate.now(), dialect );
final LocalDate startTime = adjustToDefaultPrecision( LocalDate.now(), dialect );
scope.inTransaction( (session) -> {
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();
@ -178,7 +178,7 @@ void testLocalDate(SessionFactoryScope scope) {
@SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Altibase drivers truncate fractional seconds from the LocalTime")
void testLocalTime(SessionFactoryScope scope) {
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
final LocalTime startTime = roundToDefaultPrecision( LocalTime.now(), dialect );
final LocalTime startTime = adjustToDefaultPrecision( LocalTime.now(), dialect );
scope.inTransaction( (session) -> {
final EntityWithJavaTimeValues entity = new EntityWithJavaTimeValues();

View File

@ -111,6 +111,23 @@ void basicSelectedTest(SessionFactoryScope scope) {
} );
}
@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")
@Table(name="issues")
public static class Issue {

View File

@ -54,12 +54,12 @@ public class AutoZonedTest {
Zoned z = s.find(Zoned.class, id);
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
assertEquals(
DateTimeUtils.roundToDefaultPrecision( nowZoned.toInstant(), dialect ),
DateTimeUtils.roundToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
DateTimeUtils.adjustToDefaultPrecision( nowZoned.toInstant(), dialect ),
DateTimeUtils.adjustToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
);
assertEquals(
DateTimeUtils.roundToDefaultPrecision( nowOffset.toInstant(), dialect ),
DateTimeUtils.roundToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
DateTimeUtils.adjustToDefaultPrecision( nowOffset.toInstant(), dialect ),
DateTimeUtils.adjustToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
);
assertEquals( nowZoned.toOffsetDateTime().getOffset(), z.zonedDateTime.toOffsetDateTime().getOffset() );
assertEquals( nowOffset.getOffset(), z.offsetDateTime.getOffset() );

View File

@ -54,12 +54,12 @@ public class ColumnZonedTest {
Zoned z = s.find(Zoned.class, id);
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
assertEquals(
DateTimeUtils.roundToDefaultPrecision( nowZoned.toInstant(), dialect ),
DateTimeUtils.roundToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
DateTimeUtils.adjustToDefaultPrecision( nowZoned.toInstant(), dialect ),
DateTimeUtils.adjustToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
);
assertEquals(
DateTimeUtils.roundToDefaultPrecision( nowOffset.toInstant(), dialect ),
DateTimeUtils.roundToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
DateTimeUtils.adjustToDefaultPrecision( nowOffset.toInstant(), dialect ),
DateTimeUtils.adjustToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
);
assertEquals( nowZoned.toOffsetDateTime().getOffset(), z.zonedDateTime.toOffsetDateTime().getOffset() );
assertEquals( nowOffset.getOffset(), z.offsetDateTime.getOffset() );

View File

@ -51,12 +51,12 @@ public class DefaultZonedTest {
Zoned z = s.find(Zoned.class, id);
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
assertEquals(
DateTimeUtils.roundToDefaultPrecision( nowZoned.toInstant(), dialect ),
DateTimeUtils.roundToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
DateTimeUtils.adjustToDefaultPrecision( nowZoned.toInstant(), dialect ),
DateTimeUtils.adjustToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
);
assertEquals(
DateTimeUtils.roundToDefaultPrecision( nowOffset.toInstant(), dialect ),
DateTimeUtils.roundToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
DateTimeUtils.adjustToDefaultPrecision( nowOffset.toInstant(), dialect ),
DateTimeUtils.adjustToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
);
if ( dialect.getTimeZoneSupport() == TimeZoneSupport.NATIVE ) {
assertEquals( nowZoned.toOffsetDateTime().getOffset(), z.zonedDateTime.toOffsetDateTime().getOffset() );

View File

@ -58,12 +58,12 @@ public class JDBCTimeZoneZonedTest {
ZoneOffset systemOffset = systemZone.getRules().getOffset( Instant.now() );
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
assertEquals(
DateTimeUtils.roundToDefaultPrecision( nowZoned.toInstant(), dialect ),
DateTimeUtils.roundToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
DateTimeUtils.adjustToDefaultPrecision( nowZoned.toInstant(), dialect ),
DateTimeUtils.adjustToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
);
assertEquals(
DateTimeUtils.roundToDefaultPrecision( nowOffset.toInstant(), dialect ),
DateTimeUtils.roundToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
DateTimeUtils.adjustToDefaultPrecision( nowOffset.toInstant(), dialect ),
DateTimeUtils.adjustToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
);
assertEquals( systemZone, z.zonedDateTime.getZone() );
assertEquals( systemOffset, z.offsetDateTime.getOffset() );

View File

@ -57,12 +57,12 @@ public class PassThruZonedTest {
ZoneOffset systemOffset = systemZone.getRules().getOffset( Instant.now() );
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
assertEquals(
DateTimeUtils.roundToDefaultPrecision( nowZoned.toInstant(), dialect ),
DateTimeUtils.roundToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
DateTimeUtils.adjustToDefaultPrecision( nowZoned.toInstant(), dialect ),
DateTimeUtils.adjustToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
);
assertEquals(
DateTimeUtils.roundToDefaultPrecision( nowOffset.toInstant(), dialect ),
DateTimeUtils.roundToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
DateTimeUtils.adjustToDefaultPrecision( nowOffset.toInstant(), dialect ),
DateTimeUtils.adjustToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
);
assertEquals( systemZone, z.zonedDateTime.getZone() );
assertEquals( systemOffset, z.offsetDateTime.getOffset() );

View File

@ -46,12 +46,12 @@ public class UTCNormalizedInstantTest {
final Zoned z = s.find(Zoned.class, id);
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
assertEquals(
DateTimeUtils.roundToDefaultPrecision( z.utcInstant, dialect ),
DateTimeUtils.roundToDefaultPrecision( instant, dialect )
DateTimeUtils.adjustToDefaultPrecision( z.utcInstant, dialect ),
DateTimeUtils.adjustToDefaultPrecision( instant, dialect )
);
assertEquals(
DateTimeUtils.roundToDefaultPrecision( z.localInstant, dialect ),
DateTimeUtils.roundToDefaultPrecision( instant, dialect )
DateTimeUtils.adjustToDefaultPrecision( z.localInstant, dialect ),
DateTimeUtils.adjustToDefaultPrecision( instant, dialect )
);
});
}
@ -80,12 +80,12 @@ public class UTCNormalizedInstantTest {
final Zoned z = s.find(Zoned.class, id);
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
assertEquals(
DateTimeUtils.roundToDefaultPrecision( z.utcInstant, dialect ),
DateTimeUtils.roundToDefaultPrecision( instant, dialect )
DateTimeUtils.adjustToDefaultPrecision( z.utcInstant, dialect ),
DateTimeUtils.adjustToDefaultPrecision( instant, dialect )
);
assertEquals(
DateTimeUtils.roundToDefaultPrecision( z.localInstant, dialect ),
DateTimeUtils.roundToDefaultPrecision( instant, dialect )
DateTimeUtils.adjustToDefaultPrecision( z.localInstant, dialect ),
DateTimeUtils.adjustToDefaultPrecision( instant, dialect )
);
});
}

View File

@ -54,12 +54,12 @@ public class UTCNormalizedZonedTest {
Zoned z = s.find(Zoned.class, id);
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
assertEquals(
DateTimeUtils.roundToDefaultPrecision( nowZoned.toInstant(), dialect ),
DateTimeUtils.roundToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
DateTimeUtils.adjustToDefaultPrecision( nowZoned.toInstant(), dialect ),
DateTimeUtils.adjustToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
);
assertEquals(
DateTimeUtils.roundToDefaultPrecision( nowOffset.toInstant(), dialect ),
DateTimeUtils.roundToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
DateTimeUtils.adjustToDefaultPrecision( nowOffset.toInstant(), dialect ),
DateTimeUtils.adjustToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
);
assertEquals( ZoneId.of("Z"), z.zonedDateTime.getZone() );
assertEquals( ZoneOffset.ofHours(0), z.offsetDateTime.getOffset() );