HHH-16773 Introduce support for group/order by PK functional dependency
This commit is contained in:
parent
51460470f4
commit
e5d59b64fd
|
@ -19,26 +19,13 @@ import java.util.TimeZone;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import jakarta.persistence.TemporalType;
|
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.PessimisticLockException;
|
import org.hibernate.PessimisticLockException;
|
||||||
import org.hibernate.QueryTimeoutException;
|
import org.hibernate.QueryTimeoutException;
|
||||||
import org.hibernate.boot.model.FunctionContributions;
|
import org.hibernate.boot.model.FunctionContributions;
|
||||||
import org.hibernate.boot.model.TypeContributions;
|
import org.hibernate.boot.model.TypeContributions;
|
||||||
import org.hibernate.dialect.DatabaseVersion;
|
import org.hibernate.dialect.*;
|
||||||
import org.hibernate.dialect.Dialect;
|
|
||||||
import org.hibernate.dialect.NationalizationSupport;
|
|
||||||
import org.hibernate.dialect.PgJdbcHelper;
|
|
||||||
import org.hibernate.dialect.PostgreSQLCastingInetJdbcType;
|
|
||||||
import org.hibernate.dialect.PostgreSQLCastingIntervalSecondJdbcType;
|
|
||||||
import org.hibernate.dialect.PostgreSQLCastingJsonJdbcType;
|
|
||||||
import org.hibernate.dialect.PostgreSQLDriverKind;
|
|
||||||
import org.hibernate.dialect.RowLockStrategy;
|
|
||||||
import org.hibernate.dialect.SimpleDatabaseVersion;
|
|
||||||
import org.hibernate.dialect.SpannerDialect;
|
|
||||||
import org.hibernate.dialect.TimeZoneSupport;
|
|
||||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
import org.hibernate.dialect.function.FormatFunction;
|
import org.hibernate.dialect.function.FormatFunction;
|
||||||
import org.hibernate.dialect.function.PostgreSQLTruncFunction;
|
import org.hibernate.dialect.function.PostgreSQLTruncFunction;
|
||||||
|
@ -87,11 +74,38 @@ import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
|
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
|
||||||
import static org.hibernate.query.sqm.TemporalUnit.DAY;
|
import static org.hibernate.query.sqm.TemporalUnit.DAY;
|
||||||
import static org.hibernate.query.sqm.TemporalUnit.EPOCH;
|
import static org.hibernate.query.sqm.TemporalUnit.EPOCH;
|
||||||
import static org.hibernate.query.sqm.TemporalUnit.NATIVE;
|
import static org.hibernate.query.sqm.TemporalUnit.NATIVE;
|
||||||
import static org.hibernate.type.SqlTypes.*;
|
import static org.hibernate.type.SqlTypes.ARRAY;
|
||||||
|
import static org.hibernate.type.SqlTypes.BINARY;
|
||||||
|
import static org.hibernate.type.SqlTypes.BLOB;
|
||||||
|
import static org.hibernate.type.SqlTypes.CHAR;
|
||||||
|
import static org.hibernate.type.SqlTypes.CLOB;
|
||||||
|
import static org.hibernate.type.SqlTypes.GEOGRAPHY;
|
||||||
|
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
||||||
|
import static org.hibernate.type.SqlTypes.INET;
|
||||||
|
import static org.hibernate.type.SqlTypes.INTEGER;
|
||||||
|
import static org.hibernate.type.SqlTypes.JSON;
|
||||||
|
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
||||||
|
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
||||||
|
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
||||||
|
import static org.hibernate.type.SqlTypes.NCHAR;
|
||||||
|
import static org.hibernate.type.SqlTypes.NCLOB;
|
||||||
|
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
||||||
|
import static org.hibernate.type.SqlTypes.OTHER;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_UTC;
|
||||||
|
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||||
|
import static org.hibernate.type.SqlTypes.UUID;
|
||||||
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
|
import static org.hibernate.type.SqlTypes.VARCHAR;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||||
|
@ -1022,6 +1036,11 @@ public class CockroachLegacyDialect extends Dialect {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||||
|
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP_AND_CONSTANTS;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RowLockStrategy getWriteRowLockStrategy() {
|
public RowLockStrategy getWriteRowLockStrategy() {
|
||||||
return getVersion().isSameOrAfter( 20, 1 ) ? RowLockStrategy.TABLE : RowLockStrategy.NONE;
|
return getVersion().isSameOrAfter( 20, 1 ) ? RowLockStrategy.TABLE : RowLockStrategy.NONE;
|
||||||
|
|
|
@ -19,14 +19,7 @@ import java.util.TimeZone;
|
||||||
import org.hibernate.PessimisticLockException;
|
import org.hibernate.PessimisticLockException;
|
||||||
import org.hibernate.boot.model.FunctionContributions;
|
import org.hibernate.boot.model.FunctionContributions;
|
||||||
import org.hibernate.boot.model.TypeContributions;
|
import org.hibernate.boot.model.TypeContributions;
|
||||||
import org.hibernate.dialect.DatabaseVersion;
|
import org.hibernate.dialect.*;
|
||||||
import org.hibernate.dialect.Dialect;
|
|
||||||
import org.hibernate.dialect.H2DurationIntervalSecondJdbcType;
|
|
||||||
import org.hibernate.dialect.OracleDialect;
|
|
||||||
import org.hibernate.dialect.Replacer;
|
|
||||||
import org.hibernate.dialect.SelectItemReferenceStrategy;
|
|
||||||
import org.hibernate.dialect.SimpleDatabaseVersion;
|
|
||||||
import org.hibernate.dialect.TimeZoneSupport;
|
|
||||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
import org.hibernate.dialect.hint.IndexQueryHintHandler;
|
import org.hibernate.dialect.hint.IndexQueryHintHandler;
|
||||||
import org.hibernate.dialect.identity.H2FinalTableIdentityColumnSupport;
|
import org.hibernate.dialect.identity.H2FinalTableIdentityColumnSupport;
|
||||||
|
@ -829,6 +822,11 @@ public class H2LegacyDialect extends Dialect {
|
||||||
return getVersion().isSameOrAfter( 1, 4, 198 );
|
return getVersion().isSameOrAfter( 1, 4, 198 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||||
|
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP_AND_CONSTANTS;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IdentityColumnSupport getIdentityColumnSupport() {
|
public IdentityColumnSupport getIdentityColumnSupport() {
|
||||||
return getVersion().isSameOrAfter( 2 ) ? H2FinalTableIdentityColumnSupport.INSTANCE : H2IdentityColumnSupport.INSTANCE;
|
return getVersion().isSameOrAfter( 2 ) ? H2FinalTableIdentityColumnSupport.INSTANCE : H2IdentityColumnSupport.INSTANCE;
|
||||||
|
|
|
@ -14,11 +14,7 @@ import org.hibernate.JDBCException;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.StaleObjectStateException;
|
import org.hibernate.StaleObjectStateException;
|
||||||
import org.hibernate.boot.model.FunctionContributions;
|
import org.hibernate.boot.model.FunctionContributions;
|
||||||
import org.hibernate.dialect.BooleanDecoder;
|
import org.hibernate.dialect.*;
|
||||||
import org.hibernate.dialect.DatabaseVersion;
|
|
||||||
import org.hibernate.dialect.Dialect;
|
|
||||||
import org.hibernate.dialect.OracleDialect;
|
|
||||||
import org.hibernate.dialect.SimpleDatabaseVersion;
|
|
||||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
import org.hibernate.dialect.identity.HSQLIdentityColumnSupport;
|
import org.hibernate.dialect.identity.HSQLIdentityColumnSupport;
|
||||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||||
|
@ -786,6 +782,11 @@ public class HSQLLegacyDialect extends Dialect {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||||
|
return FunctionalDependencyAnalysisSupportImpl.TABLE_REFERENCE;
|
||||||
|
}
|
||||||
|
|
||||||
// Do not drop constraints explicitly, just do this by cascading instead.
|
// Do not drop constraints explicitly, just do this by cascading instead.
|
||||||
@Override
|
@Override
|
||||||
public boolean dropConstraints() {
|
public boolean dropConstraints() {
|
||||||
|
|
|
@ -11,13 +11,7 @@ import java.sql.SQLException;
|
||||||
|
|
||||||
import org.hibernate.boot.model.FunctionContributions;
|
import org.hibernate.boot.model.FunctionContributions;
|
||||||
import org.hibernate.boot.model.TypeContributions;
|
import org.hibernate.boot.model.TypeContributions;
|
||||||
import org.hibernate.dialect.DatabaseVersion;
|
import org.hibernate.dialect.*;
|
||||||
import org.hibernate.dialect.Dialect;
|
|
||||||
import org.hibernate.dialect.InnoDBStorageEngine;
|
|
||||||
import org.hibernate.dialect.MySQLServerConfiguration;
|
|
||||||
import org.hibernate.dialect.MySQLStorageEngine;
|
|
||||||
import org.hibernate.dialect.NationalizationSupport;
|
|
||||||
import org.hibernate.dialect.VarcharUUIDJdbcType;
|
|
||||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
import org.hibernate.dialect.sequence.MariaDBSequenceSupport;
|
import org.hibernate.dialect.sequence.MariaDBSequenceSupport;
|
||||||
import org.hibernate.dialect.sequence.SequenceSupport;
|
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||||
|
@ -249,6 +243,11 @@ public class MariaDBLegacyDialect extends MySQLLegacyDialect {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||||
|
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP_AND_CONSTANTS;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData)
|
public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData)
|
||||||
throws SQLException {
|
throws SQLException {
|
||||||
|
|
|
@ -17,16 +17,7 @@ import org.hibernate.PessimisticLockException;
|
||||||
import org.hibernate.boot.model.FunctionContributions;
|
import org.hibernate.boot.model.FunctionContributions;
|
||||||
import org.hibernate.boot.model.TypeContributions;
|
import org.hibernate.boot.model.TypeContributions;
|
||||||
import org.hibernate.cfg.Environment;
|
import org.hibernate.cfg.Environment;
|
||||||
import org.hibernate.dialect.DatabaseVersion;
|
import org.hibernate.dialect.*;
|
||||||
import org.hibernate.dialect.Dialect;
|
|
||||||
import org.hibernate.dialect.InnoDBStorageEngine;
|
|
||||||
import org.hibernate.dialect.MyISAMStorageEngine;
|
|
||||||
import org.hibernate.dialect.MySQLCastingJsonJdbcType;
|
|
||||||
import org.hibernate.dialect.MySQLServerConfiguration;
|
|
||||||
import org.hibernate.dialect.MySQLStorageEngine;
|
|
||||||
import org.hibernate.dialect.Replacer;
|
|
||||||
import org.hibernate.dialect.RowLockStrategy;
|
|
||||||
import org.hibernate.dialect.SelectItemReferenceStrategy;
|
|
||||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
import org.hibernate.dialect.hint.IndexQueryHintHandler;
|
import org.hibernate.dialect.hint.IndexQueryHintHandler;
|
||||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||||
|
@ -1367,6 +1358,11 @@ public class MySQLLegacyDialect extends Dialect {
|
||||||
return getMySQLVersion().isSameOrAfter( 8 );
|
return getMySQLVersion().isSameOrAfter( 8 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||||
|
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canDisableConstraints() {
|
public boolean canDisableConstraints() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -26,20 +26,7 @@ import org.hibernate.QueryTimeoutException;
|
||||||
import org.hibernate.boot.model.FunctionContributions;
|
import org.hibernate.boot.model.FunctionContributions;
|
||||||
import org.hibernate.boot.model.TypeContributions;
|
import org.hibernate.boot.model.TypeContributions;
|
||||||
import org.hibernate.community.dialect.sequence.PostgreSQLLegacySequenceSupport;
|
import org.hibernate.community.dialect.sequence.PostgreSQLLegacySequenceSupport;
|
||||||
import org.hibernate.dialect.DatabaseVersion;
|
import org.hibernate.dialect.*;
|
||||||
import org.hibernate.dialect.Dialect;
|
|
||||||
import org.hibernate.dialect.NationalizationSupport;
|
|
||||||
import org.hibernate.dialect.OracleDialect;
|
|
||||||
import org.hibernate.dialect.PgJdbcHelper;
|
|
||||||
import org.hibernate.dialect.PostgreSQLCastingInetJdbcType;
|
|
||||||
import org.hibernate.dialect.PostgreSQLCastingIntervalSecondJdbcType;
|
|
||||||
import org.hibernate.dialect.PostgreSQLCastingJsonJdbcType;
|
|
||||||
import org.hibernate.dialect.PostgreSQLDriverKind;
|
|
||||||
import org.hibernate.dialect.PostgreSQLStructCastingJdbcType;
|
|
||||||
import org.hibernate.dialect.Replacer;
|
|
||||||
import org.hibernate.dialect.RowLockStrategy;
|
|
||||||
import org.hibernate.dialect.SelectItemReferenceStrategy;
|
|
||||||
import org.hibernate.dialect.TimeZoneSupport;
|
|
||||||
import org.hibernate.dialect.aggregate.AggregateSupport;
|
import org.hibernate.dialect.aggregate.AggregateSupport;
|
||||||
import org.hibernate.dialect.aggregate.PostgreSQLAggregateSupport;
|
import org.hibernate.dialect.aggregate.PostgreSQLAggregateSupport;
|
||||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
|
@ -1287,6 +1274,11 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||||
|
return FunctionalDependencyAnalysisSupportImpl.TABLE_REFERENCE;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RowLockStrategy getWriteRowLockStrategy() {
|
public RowLockStrategy getWriteRowLockStrategy() {
|
||||||
return RowLockStrategy.TABLE;
|
return RowLockStrategy.TABLE;
|
||||||
|
|
|
@ -988,6 +988,11 @@ public class CockroachDialect extends Dialect {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||||
|
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP_AND_CONSTANTS;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RowLockStrategy getWriteRowLockStrategy() {
|
public RowLockStrategy getWriteRowLockStrategy() {
|
||||||
return RowLockStrategy.TABLE;
|
return RowLockStrategy.TABLE;
|
||||||
|
|
|
@ -5299,4 +5299,12 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
||||||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||||
return DmlTargetColumnQualifierSupport.NONE;
|
return DmlTargetColumnQualifierSupport.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get this dialect's level of support for primary key functional dependency analysis
|
||||||
|
* within {@code GROUP BY} and {@code ORDER BY} clauses.
|
||||||
|
*/
|
||||||
|
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||||
|
return FunctionalDependencyAnalysisSupportImpl.NONE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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.dialect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialect support information for primary key functional dependency analysis
|
||||||
|
* within {@code GROUP BY} and {@code ORDER BY} clauses.
|
||||||
|
*
|
||||||
|
* @author Marco Belladelli
|
||||||
|
*/
|
||||||
|
public interface FunctionalDependencyAnalysisSupport {
|
||||||
|
/**
|
||||||
|
* Supports primary key functional dependency analysis
|
||||||
|
*/
|
||||||
|
boolean supportsAnalysis();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supports functional dependency analysis through joined tables and result sets (e.g. unions)
|
||||||
|
*/
|
||||||
|
boolean supportsTableGroups();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Also supports functional dependency analysis for constant values other than table columns
|
||||||
|
*/
|
||||||
|
boolean supportsConstants();
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* 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.dialect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Marco Belladelli
|
||||||
|
*/
|
||||||
|
public class FunctionalDependencyAnalysisSupportImpl implements FunctionalDependencyAnalysisSupport {
|
||||||
|
private final boolean supportsAnalysis;
|
||||||
|
private final boolean supportsTableGroups;
|
||||||
|
private final boolean supportsConstants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No support for functional dependency analysis
|
||||||
|
*/
|
||||||
|
public static final FunctionalDependencyAnalysisSupportImpl NONE = new FunctionalDependencyAnalysisSupportImpl(
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only supports the analysis for a single table reference, i.e. no support for joins / unions
|
||||||
|
*/
|
||||||
|
public static final FunctionalDependencyAnalysisSupportImpl TABLE_REFERENCE = new FunctionalDependencyAnalysisSupportImpl(
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supports the analysis for single tables, a group of joined tables or a result set (e.g. union)
|
||||||
|
* as long as only table columns are selected, i.e. no constants (see {@link #TABLE_GROUP_AND_CONSTANTS})
|
||||||
|
*/
|
||||||
|
public static final FunctionalDependencyAnalysisSupportImpl TABLE_GROUP = new FunctionalDependencyAnalysisSupportImpl(
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fully supports the analysis for joined / union table groups, including any constant value
|
||||||
|
* (e.g. the literal {@code clazz_} column used as table per class inheritance discriminator column)
|
||||||
|
*/
|
||||||
|
public static final FunctionalDependencyAnalysisSupportImpl TABLE_GROUP_AND_CONSTANTS = new FunctionalDependencyAnalysisSupportImpl(
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
public FunctionalDependencyAnalysisSupportImpl(
|
||||||
|
boolean supportsAnalysis,
|
||||||
|
boolean supportsTableGroups,
|
||||||
|
boolean supportsConstants) {
|
||||||
|
this.supportsAnalysis = supportsAnalysis;
|
||||||
|
this.supportsTableGroups = supportsTableGroups;
|
||||||
|
this.supportsConstants = supportsConstants;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsAnalysis() {
|
||||||
|
return supportsAnalysis;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsTableGroups() {
|
||||||
|
return supportsTableGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsConstants() {
|
||||||
|
return supportsConstants;
|
||||||
|
}
|
||||||
|
}
|
|
@ -69,11 +69,11 @@ import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorH2
|
||||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
|
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
|
||||||
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||||
import org.hibernate.type.descriptor.jdbc.H2FormatJsonJdbcType;
|
import org.hibernate.type.descriptor.jdbc.H2FormatJsonJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.TimeAsTimestampWithTimeZoneJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TimeAsTimestampWithTimeZoneJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.TimeUtcAsJdbcTimeJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TimeUtcAsJdbcTimeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsInstantJdbcType;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.TimeUtcAsOffsetTimeJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TimeUtcAsOffsetTimeJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsInstantJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||||
|
@ -823,6 +823,11 @@ public class H2Dialect extends Dialect {
|
||||||
return getVersion().isSameOrAfter( 1, 4, 198 );
|
return getVersion().isSameOrAfter( 1, 4, 198 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||||
|
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP_AND_CONSTANTS;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IdentityColumnSupport getIdentityColumnSupport() {
|
public IdentityColumnSupport getIdentityColumnSupport() {
|
||||||
return getVersion().isSameOrAfter( 2 )
|
return getVersion().isSameOrAfter( 2 )
|
||||||
|
|
|
@ -605,6 +605,11 @@ public class HSQLDialect extends Dialect {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||||
|
return FunctionalDependencyAnalysisSupportImpl.TABLE_REFERENCE;
|
||||||
|
}
|
||||||
|
|
||||||
// Do not drop constraints explicitly, just do this by cascading instead.
|
// Do not drop constraints explicitly, just do this by cascading instead.
|
||||||
@Override
|
@Override
|
||||||
public boolean dropConstraints() {
|
public boolean dropConstraints() {
|
||||||
|
|
|
@ -248,6 +248,11 @@ public class MariaDBDialect extends MySQLDialect {
|
||||||
return getVersion().isSameOrAfter( 10, 5 );
|
return getVersion().isSameOrAfter( 10, 5 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||||
|
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP_AND_CONSTANTS;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData)
|
public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData)
|
||||||
throws SQLException {
|
throws SQLException {
|
||||||
|
|
|
@ -1470,6 +1470,11 @@ public class MySQLDialect extends Dialect {
|
||||||
return getMySQLVersion().isSameOrAfter( 8 );
|
return getMySQLVersion().isSameOrAfter( 8 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||||
|
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canDisableConstraints() {
|
public boolean canDisableConstraints() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -78,7 +78,6 @@ import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||||
import org.hibernate.type.JavaObjectType;
|
import org.hibernate.type.JavaObjectType;
|
||||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||||
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
@ -99,7 +98,34 @@ import jakarta.persistence.TemporalType;
|
||||||
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
|
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
|
||||||
import static org.hibernate.query.sqm.TemporalUnit.DAY;
|
import static org.hibernate.query.sqm.TemporalUnit.DAY;
|
||||||
import static org.hibernate.query.sqm.TemporalUnit.EPOCH;
|
import static org.hibernate.query.sqm.TemporalUnit.EPOCH;
|
||||||
import static org.hibernate.type.SqlTypes.*;
|
import static org.hibernate.type.SqlTypes.ARRAY;
|
||||||
|
import static org.hibernate.type.SqlTypes.BINARY;
|
||||||
|
import static org.hibernate.type.SqlTypes.BLOB;
|
||||||
|
import static org.hibernate.type.SqlTypes.CHAR;
|
||||||
|
import static org.hibernate.type.SqlTypes.CLOB;
|
||||||
|
import static org.hibernate.type.SqlTypes.FLOAT;
|
||||||
|
import static org.hibernate.type.SqlTypes.GEOGRAPHY;
|
||||||
|
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
||||||
|
import static org.hibernate.type.SqlTypes.INET;
|
||||||
|
import static org.hibernate.type.SqlTypes.JSON;
|
||||||
|
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
||||||
|
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
||||||
|
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
||||||
|
import static org.hibernate.type.SqlTypes.NCHAR;
|
||||||
|
import static org.hibernate.type.SqlTypes.NCLOB;
|
||||||
|
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
||||||
|
import static org.hibernate.type.SqlTypes.OTHER;
|
||||||
|
import static org.hibernate.type.SqlTypes.SQLXML;
|
||||||
|
import static org.hibernate.type.SqlTypes.STRUCT;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_UTC;
|
||||||
|
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||||
|
import static org.hibernate.type.SqlTypes.UUID;
|
||||||
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
|
import static org.hibernate.type.SqlTypes.VARCHAR;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||||
|
@ -1308,6 +1334,11 @@ public class PostgreSQLDialect extends Dialect {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||||
|
return FunctionalDependencyAnalysisSupportImpl.TABLE_REFERENCE;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RowLockStrategy getWriteRowLockStrategy() {
|
public RowLockStrategy getWriteRowLockStrategy() {
|
||||||
return RowLockStrategy.TABLE;
|
return RowLockStrategy.TABLE;
|
||||||
|
|
|
@ -11,6 +11,8 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.hibernate.dialect.Dialect;
|
||||||
|
import org.hibernate.dialect.FunctionalDependencyAnalysisSupport;
|
||||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||||
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
||||||
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
|
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
|
||||||
|
@ -23,6 +25,7 @@ import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||||
import org.hibernate.metamodel.mapping.ValuedModelPart;
|
import org.hibernate.metamodel.mapping.ValuedModelPart;
|
||||||
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
|
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
|
||||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||||
|
import org.hibernate.persister.entity.UnionSubclassEntityPersister;
|
||||||
import org.hibernate.query.derived.AnonymousTupleEntityValuedModelPart;
|
import org.hibernate.query.derived.AnonymousTupleEntityValuedModelPart;
|
||||||
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
||||||
|
@ -49,6 +52,7 @@ import org.hibernate.sql.results.graph.Fetchable;
|
||||||
|
|
||||||
public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpretation<T>
|
public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpretation<T>
|
||||||
implements SqlTupleContainer, Assignable {
|
implements SqlTupleContainer, Assignable {
|
||||||
|
private final Expression sqlExpression;
|
||||||
|
|
||||||
public static <T> EntityValuedPathInterpretation<T> from(
|
public static <T> EntityValuedPathInterpretation<T> from(
|
||||||
SqmEntityValuedSimplePath<T> sqmPath,
|
SqmEntityValuedSimplePath<T> sqmPath,
|
||||||
|
@ -266,7 +270,7 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// When the table group is selected and the navigablePath is selected we need to expand
|
// When the table group is selected and the navigablePath is selected we need to expand
|
||||||
// to all columns, as we also expand this to all columns in the select clause
|
// to all columns, as we must make sure we include all columns present in the select clause
|
||||||
expandToAllColumns = isSelected( tableGroup, navigablePath, querySpec );
|
expandToAllColumns = isSelected( tableGroup, navigablePath, querySpec );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -277,33 +281,38 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
||||||
final SqlExpressionResolver sqlExprResolver = sqlAstCreationState.getSqlExpressionResolver();
|
final SqlExpressionResolver sqlExprResolver = sqlAstCreationState.getSqlExpressionResolver();
|
||||||
final Expression sqlExpression;
|
final Expression sqlExpression;
|
||||||
if ( expandToAllColumns ) {
|
if ( expandToAllColumns ) {
|
||||||
// Expand to all columns of the entity mapping type, as we already did for the selection
|
// Expand to all columns of the entity mapping type to ensure a correct group / order by expression,
|
||||||
|
// or use only the primary key if the dialect supports functional dependency
|
||||||
|
final Dialect dialect = sqlAstCreationState.getCreationContext()
|
||||||
|
.getSessionFactory()
|
||||||
|
.getJdbcServices()
|
||||||
|
.getDialect();
|
||||||
final EntityMappingType entityMappingType = mapping.getEntityMappingType();
|
final EntityMappingType entityMappingType = mapping.getEntityMappingType();
|
||||||
final EntityIdentifierMapping identifierMapping = entityMappingType.getIdentifierMapping();
|
final EntityIdentifierMapping identifierMapping = entityMappingType.getIdentifierMapping();
|
||||||
final EntityDiscriminatorMapping discriminatorMapping = entityMappingType.getDiscriminatorMapping();
|
final List<Expression> expressions = new ArrayList<>( identifierMapping.getJdbcTypeCount() );
|
||||||
final int numberOfFetchables = entityMappingType.getNumberOfFetchables();
|
|
||||||
final List<Expression> expressions = new ArrayList<>(
|
|
||||||
numberOfFetchables + identifierMapping.getJdbcTypeCount()
|
|
||||||
+ ( discriminatorMapping == null ? 0 : 1 )
|
|
||||||
);
|
|
||||||
final TableGroup parentTableGroup = tableGroup;
|
|
||||||
final SelectableConsumer selectableConsumer = (selectionIndex, selectableMapping) -> {
|
final SelectableConsumer selectableConsumer = (selectionIndex, selectableMapping) -> {
|
||||||
final TableReference tableReference = parentTableGroup.resolveTableReference(
|
final TableReference tableReference = tableGroup.resolveTableReference(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
selectableMapping.getContainingTableExpression()
|
selectableMapping.getContainingTableExpression()
|
||||||
);
|
);
|
||||||
expressions.add(
|
expressions.add( sqlExprResolver.resolveSqlExpression( tableReference, selectableMapping ) );
|
||||||
sqlExprResolver.resolveSqlExpression( tableReference, selectableMapping )
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
identifierMapping.forEachSelectable( selectableConsumer );
|
identifierMapping.forEachSelectable( selectableConsumer );
|
||||||
if ( discriminatorMapping != null ) {
|
if ( !supportsFunctionalDependency( dialect, entityMappingType ) ) {
|
||||||
discriminatorMapping.forEachSelectable( selectableConsumer );
|
final EntityDiscriminatorMapping discriminatorMapping = entityMappingType.getDiscriminatorMapping();
|
||||||
}
|
if ( discriminatorMapping != null ) {
|
||||||
for ( int i = 0; i < numberOfFetchables; i++ ) {
|
expressions.add( discriminatorMapping.resolveSqlExpression(
|
||||||
final Fetchable fetchable = entityMappingType.getFetchable( i );
|
navigablePath,
|
||||||
if ( fetchable.isSelectable() ) {
|
discriminatorMapping.getUnderlyingJdbcMapping(),
|
||||||
fetchable.forEachSelectable( selectableConsumer );
|
tableGroup,
|
||||||
|
sqlAstCreationState
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
for ( int i = 0; i < entityMappingType.getNumberOfFetchables(); i++ ) {
|
||||||
|
final Fetchable fetchable = entityMappingType.getFetchable( i );
|
||||||
|
if ( fetchable.isSelectable() ) {
|
||||||
|
fetchable.forEachSelectable( selectableConsumer );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sqlExpression = new SqlTuple( expressions, entityMappingType );
|
sqlExpression = new SqlTuple( expressions, entityMappingType );
|
||||||
|
@ -373,7 +382,21 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Expression sqlExpression;
|
private static boolean supportsFunctionalDependency(Dialect dialect, EntityMappingType entityMappingType) {
|
||||||
|
final FunctionalDependencyAnalysisSupport analysisSupport = dialect.getFunctionalDependencyAnalysisSupport();
|
||||||
|
if ( analysisSupport.supportsAnalysis() ) {
|
||||||
|
if ( entityMappingType.getSqmMultiTableMutationStrategy() == null ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return analysisSupport.supportsTableGroups() && ( analysisSupport.supportsConstants() ||
|
||||||
|
// Union entity persisters use a literal 'clazz_' column as a discriminator
|
||||||
|
// that breaks functional dependency for dialects that don't support constants
|
||||||
|
!( entityMappingType.getEntityPersister() instanceof UnionSubclassEntityPersister ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public EntityValuedPathInterpretation(
|
public EntityValuedPathInterpretation(
|
||||||
Expression sqlExpression,
|
Expression sqlExpression,
|
||||||
|
|
|
@ -6,6 +6,11 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.orm.test.inheritance;
|
package org.hibernate.orm.test.inheritance;
|
||||||
|
|
||||||
|
import org.hibernate.dialect.Dialect;
|
||||||
|
import org.hibernate.dialect.FunctionalDependencyAnalysisSupport;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
import org.hibernate.persister.entity.UnionSubclassEntityPersister;
|
||||||
|
|
||||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||||
import org.hibernate.testing.orm.junit.DomainModel;
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
import org.hibernate.testing.orm.junit.Jira;
|
import org.hibernate.testing.orm.junit.Jira;
|
||||||
|
@ -22,8 +27,11 @@ import jakarta.persistence.DiscriminatorValue;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.GeneratedValue;
|
import jakarta.persistence.GeneratedValue;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Inheritance;
|
||||||
|
import jakarta.persistence.InheritanceType;
|
||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.MappedSuperclass;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@ -33,19 +41,34 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
@SessionFactory( useCollectingStatementInspector = true )
|
@SessionFactory( useCollectingStatementInspector = true )
|
||||||
@DomainModel( annotatedClasses = {
|
@DomainModel( annotatedClasses = {
|
||||||
InheritanceQueryGroupByTest.Parent.class,
|
InheritanceQueryGroupByTest.Parent.class,
|
||||||
InheritanceQueryGroupByTest.ChildOne.class,
|
InheritanceQueryGroupByTest.SingleTableParent.class,
|
||||||
InheritanceQueryGroupByTest.ChildTwo.class,
|
InheritanceQueryGroupByTest.SingleTableChildOne.class,
|
||||||
InheritanceQueryGroupByTest.MyEntity.class
|
InheritanceQueryGroupByTest.SingleTableChildTwo.class,
|
||||||
|
InheritanceQueryGroupByTest.JoinedParent.class,
|
||||||
|
InheritanceQueryGroupByTest.JoinedChildOne.class,
|
||||||
|
InheritanceQueryGroupByTest.JoinedChildTwo.class,
|
||||||
|
InheritanceQueryGroupByTest.TPCParent.class,
|
||||||
|
InheritanceQueryGroupByTest.TPCChildOne.class,
|
||||||
|
InheritanceQueryGroupByTest.TPCChildTwo.class,
|
||||||
|
InheritanceQueryGroupByTest.MyEntity.class,
|
||||||
} )
|
} )
|
||||||
@Jira( "https://hibernate.atlassian.net/browse/HHH-16349" )
|
@Jira( "https://hibernate.atlassian.net/browse/HHH-16349" )
|
||||||
|
@Jira( "https://hibernate.atlassian.net/browse/HHH-16773" )
|
||||||
public class InheritanceQueryGroupByTest {
|
public class InheritanceQueryGroupByTest {
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public void setUp(SessionFactoryScope scope) {
|
public void setUp(SessionFactoryScope scope) {
|
||||||
scope.inTransaction( session -> {
|
scope.inTransaction( session -> {
|
||||||
final ChildOne childOne = new ChildOne( "child_one", 1 );
|
final SingleTableChildOne st1 = new SingleTableChildOne();
|
||||||
session.persist( childOne );
|
st1.setName( "single_table_child_one" );
|
||||||
session.persist( new MyEntity( 1, childOne ) );
|
session.persist( st1 );
|
||||||
session.persist( new MyEntity( 2, childOne ) );
|
final JoinedChildOne j1 = new JoinedChildOne();
|
||||||
|
j1.setName( "joined_child_one" );
|
||||||
|
session.persist( j1 );
|
||||||
|
final TPCChildOne tpc1 = new TPCChildOne();
|
||||||
|
tpc1.setName( "tpc_child_one" );
|
||||||
|
session.persist( tpc1 );
|
||||||
|
session.persist( new MyEntity( 1, st1, j1, tpc1 ) );
|
||||||
|
session.persist( new MyEntity( 2, st1, j1, tpc1 ) );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,82 +76,199 @@ public class InheritanceQueryGroupByTest {
|
||||||
public void tearDown(SessionFactoryScope scope) {
|
public void tearDown(SessionFactoryScope scope) {
|
||||||
scope.inTransaction( session -> {
|
scope.inTransaction( session -> {
|
||||||
session.createMutationQuery( "delete from MyEntity" ).executeUpdate();
|
session.createMutationQuery( "delete from MyEntity" ).executeUpdate();
|
||||||
session.createMutationQuery( "delete from Parent" ).executeUpdate();
|
session.createMutationQuery( "delete from " + Parent.class.getName() ).executeUpdate();
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGroupBy(SessionFactoryScope scope) {
|
public void testGroupBySingleTable(SessionFactoryScope scope) {
|
||||||
|
testGroupBy( scope, "singleTableParent", SingleTableParent.class, "single_table_child_one", 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGroupByJoined(SessionFactoryScope scope) {
|
||||||
|
testGroupBy( scope, "joinedParent", JoinedParent.class, "joined_child_one", 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGroupByTPC(SessionFactoryScope scope) {
|
||||||
|
testGroupBy( scope, "tpcParent", TPCParent.class, "tpc_child_one", 4 );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testGroupBy(
|
||||||
|
SessionFactoryScope scope,
|
||||||
|
String parentProp,
|
||||||
|
Class<?> parentEntityClass,
|
||||||
|
String parentName,
|
||||||
|
int childPropCount) {
|
||||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
statementInspector.clear();
|
statementInspector.clear();
|
||||||
scope.inTransaction( session -> {
|
scope.inTransaction( session -> {
|
||||||
final MyPojo myPojo = session.createQuery(
|
final MyPojo myPojo = session.createQuery(
|
||||||
"select new "
|
String.format(
|
||||||
+ MyPojo.class.getName()
|
"select new %s(sum(e.amount), re) from MyEntity e join e.%s re group by re",
|
||||||
+ "(sum(e.amount), re) from MyEntity e join e.parent re group by re",
|
MyPojo.class.getName(),
|
||||||
|
parentProp
|
||||||
|
),
|
||||||
MyPojo.class
|
MyPojo.class
|
||||||
).getSingleResult();
|
).getSingleResult();
|
||||||
assertThat( myPojo.getAmount() ).isEqualTo( 3L );
|
assertThat( myPojo.getAmount() ).isEqualTo( 3L );
|
||||||
assertThat( myPojo.getParent().getName() ).isEqualTo( "child_one" );
|
assertThat( myPojo.getParent().getName() ).isEqualTo( parentName );
|
||||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_one_col", 2 );
|
final EntityMappingType entityMappingType = scope.getSessionFactory()
|
||||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", 2 );
|
.getMappingMetamodel()
|
||||||
|
.findEntityDescriptor( parentEntityClass );
|
||||||
|
final int expectedCount = supportsFunctionalDependency( scope, entityMappingType ) ?
|
||||||
|
childPropCount :
|
||||||
|
childPropCount + 1;
|
||||||
|
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_one_col", expectedCount );
|
||||||
|
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", expectedCount );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGroupByNotSelected(SessionFactoryScope scope) {
|
public void testGroupByNotSelectedSingleTable(SessionFactoryScope scope) {
|
||||||
|
testGroupByNotSelected( scope, "singleTableParent", "single_table_parent_id", 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGroupByNotSelectedJoined(SessionFactoryScope scope) {
|
||||||
|
testGroupByNotSelected( scope, "joinedParent", "joined_parent_id", 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGroupByNotSelectedTPC(SessionFactoryScope scope) {
|
||||||
|
testGroupByNotSelected( scope, "tpcParent", "tpc_parent_id", 3 );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testGroupByNotSelected(
|
||||||
|
SessionFactoryScope scope,
|
||||||
|
String parentProp,
|
||||||
|
String parentFkName,
|
||||||
|
int childPropCount) {
|
||||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
statementInspector.clear();
|
statementInspector.clear();
|
||||||
scope.inTransaction( session -> {
|
scope.inTransaction( session -> {
|
||||||
final Long sum = session.createQuery(
|
final Long sum = session.createQuery(
|
||||||
"select sum(e.amount) from MyEntity e join e.parent re group by re",
|
String.format( "select sum(e.amount) from MyEntity e join e.%s re group by re", parentProp ),
|
||||||
Long.class
|
Long.class
|
||||||
).getSingleResult();
|
).getSingleResult();
|
||||||
assertThat( sum ).isEqualTo( 3L );
|
assertThat( sum ).isEqualTo( 3L );
|
||||||
// When not selected, group by should only use the foreign key (parent_id)
|
// When not selected, group by should only use the foreign key (parent_id)
|
||||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "parent_id", 2 );
|
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, parentFkName, 2 );
|
||||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_one_col", 0 );
|
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_one_col", childPropCount );
|
||||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", 0 );
|
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", childPropCount );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGroupByAndOrderBy(SessionFactoryScope scope) {
|
public void testGroupByAndOrderBySingleTable(SessionFactoryScope scope) {
|
||||||
|
testGroupByAndOrderBy( scope, "singleTableParent", SingleTableParent.class, "single_table_child_one", 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGroupByAndOrderByJoined(SessionFactoryScope scope) {
|
||||||
|
testGroupByAndOrderBy( scope, "joinedParent", JoinedParent.class, "joined_child_one", 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGroupByAndOrderByTPC(SessionFactoryScope scope) {
|
||||||
|
testGroupByAndOrderBy( scope, "tpcParent", TPCParent.class, "tpc_child_one", 4 );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testGroupByAndOrderBy(
|
||||||
|
SessionFactoryScope scope,
|
||||||
|
String parentProp,
|
||||||
|
Class<?> parentEntityClass,
|
||||||
|
String parentName,
|
||||||
|
int childPropCount) {
|
||||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
statementInspector.clear();
|
statementInspector.clear();
|
||||||
scope.inTransaction( session -> {
|
scope.inTransaction( session -> {
|
||||||
final MyPojo myPojo = session.createQuery(
|
final MyPojo myPojo = session.createQuery(
|
||||||
"select new "
|
String.format(
|
||||||
+ MyPojo.class.getName()
|
"select new %s(sum(e.amount), re) from MyEntity e join e.%s re group by re order by re",
|
||||||
+ "(sum(e.amount), re) from MyEntity e join e.parent re group by re order by re",
|
MyPojo.class.getName(),
|
||||||
|
parentProp
|
||||||
|
),
|
||||||
MyPojo.class
|
MyPojo.class
|
||||||
).getSingleResult();
|
).getSingleResult();
|
||||||
assertThat( myPojo.getAmount() ).isEqualTo( 3L );
|
assertThat( myPojo.getAmount() ).isEqualTo( 3L );
|
||||||
assertThat( myPojo.getParent().getName() ).isEqualTo( "child_one" );
|
assertThat( myPojo.getParent().getName() ).isEqualTo( parentName );
|
||||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_one_col", 3 );
|
final EntityMappingType entityMappingType = scope.getSessionFactory()
|
||||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", 3 );
|
.getMappingMetamodel()
|
||||||
|
.findEntityDescriptor( parentEntityClass );
|
||||||
|
final int expectedCount = supportsFunctionalDependency( scope, entityMappingType ) ?
|
||||||
|
childPropCount :
|
||||||
|
childPropCount + 2;
|
||||||
|
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_one_col", expectedCount );
|
||||||
|
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", expectedCount );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGroupByAndOrderByNotSelected(SessionFactoryScope scope) {
|
public void testGroupByAndOrderByNotSelectedSingleTable(SessionFactoryScope scope) {
|
||||||
|
testGroupByAndOrderByNotSelected( scope, "singleTableParent", "single_table_parent_id", 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGroupByAndOrderByNotSelectedJoined(SessionFactoryScope scope) {
|
||||||
|
testGroupByAndOrderByNotSelected( scope, "joinedParent", "joined_parent_id", 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGroupByAndOrderByNotSelectedTPC(SessionFactoryScope scope) {
|
||||||
|
testGroupByAndOrderByNotSelected( scope, "tpcParent", "tpc_parent_id", 3 );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testGroupByAndOrderByNotSelected(
|
||||||
|
SessionFactoryScope scope,
|
||||||
|
String parentProp,
|
||||||
|
String parentFkName,
|
||||||
|
int childPropCount) {
|
||||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||||
statementInspector.clear();
|
statementInspector.clear();
|
||||||
scope.inTransaction( session -> {
|
scope.inTransaction( session -> {
|
||||||
final Long sum = session.createQuery(
|
final Long sum = session.createQuery(
|
||||||
"select sum(e.amount) from MyEntity e join e.parent re group by re order by re",
|
String.format(
|
||||||
|
"select sum(e.amount) from MyEntity e join e.%s re group by re order by re",
|
||||||
|
parentProp
|
||||||
|
),
|
||||||
Long.class
|
Long.class
|
||||||
).getSingleResult();
|
).getSingleResult();
|
||||||
assertThat( sum ).isEqualTo( 3L );
|
assertThat( sum ).isEqualTo( 3L );
|
||||||
// When not selected, group by and order by should only use the foreign key (parent_id)
|
// When not selected, group by should only use the foreign key (parent_id)
|
||||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "parent_id", 3 );
|
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, parentFkName, 3 );
|
||||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_one_col", 0 );
|
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_one_col", childPropCount );
|
||||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", 0 );
|
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", childPropCount );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity( name = "Parent" )
|
private static Dialect getDialect(SessionFactoryScope scope) {
|
||||||
@DiscriminatorColumn( name = "disc_col", discriminatorType = DiscriminatorType.INTEGER )
|
return scope.getSessionFactory().getJdbcServices().getDialect();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean supportsFunctionalDependency(
|
||||||
|
SessionFactoryScope scope,
|
||||||
|
EntityMappingType entityMappingType) {
|
||||||
|
final FunctionalDependencyAnalysisSupport analysisSupport = scope.getSessionFactory()
|
||||||
|
.getJdbcServices()
|
||||||
|
.getDialect()
|
||||||
|
.getFunctionalDependencyAnalysisSupport();
|
||||||
|
if ( analysisSupport.supportsAnalysis() ) {
|
||||||
|
if ( entityMappingType.getSqmMultiTableMutationStrategy() == null ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return analysisSupport.supportsTableGroups() && ( analysisSupport.supportsConstants() ||
|
||||||
|
// Union entity persisters use a literal 'clazz_' column as a discriminator
|
||||||
|
// that breaks functional dependency for dialects that don't support constants
|
||||||
|
!( entityMappingType.getEntityPersister() instanceof UnionSubclassEntityPersister ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@MappedSuperclass
|
||||||
public abstract static class Parent {
|
public abstract static class Parent {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue
|
@GeneratedValue
|
||||||
|
@ -136,13 +276,6 @@ public class InheritanceQueryGroupByTest {
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
public Parent() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Parent(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
@ -150,36 +283,64 @@ public class InheritanceQueryGroupByTest {
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity( name = "ChildOne" )
|
@Entity( name = "SingleTableParent" )
|
||||||
|
@Inheritance( strategy = InheritanceType.SINGLE_TABLE )
|
||||||
|
@DiscriminatorColumn( name = "disc_col", discriminatorType = DiscriminatorType.INTEGER )
|
||||||
|
public static class SingleTableParent extends Parent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "SingleTableChildOne" )
|
||||||
@DiscriminatorValue( "1" )
|
@DiscriminatorValue( "1" )
|
||||||
public static class ChildOne extends Parent {
|
public static class SingleTableChildOne extends SingleTableParent {
|
||||||
@Column( name = "child_one_col" )
|
@Column( name = "child_one_col" )
|
||||||
private Integer childOneProp;
|
private Integer childOneProp;
|
||||||
|
|
||||||
public ChildOne() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public ChildOne(String name, Integer childOneProp) {
|
|
||||||
super( name );
|
|
||||||
this.childOneProp = childOneProp;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity( name = "ChildTwo" )
|
@Entity( name = "SingleTableChildTwo" )
|
||||||
@DiscriminatorValue( "2" )
|
@DiscriminatorValue( "2" )
|
||||||
public static class ChildTwo extends Parent {
|
public static class SingleTableChildTwo extends SingleTableParent {
|
||||||
@Column( name = "child_two_col" )
|
@Column( name = "child_two_col" )
|
||||||
private Integer childTwoProp;
|
private Integer childTwoProp;
|
||||||
|
}
|
||||||
|
|
||||||
public ChildTwo() {
|
@Entity( name = "JoinedParent" )
|
||||||
}
|
@Inheritance( strategy = InheritanceType.JOINED )
|
||||||
|
public static class JoinedParent extends Parent {
|
||||||
|
}
|
||||||
|
|
||||||
public ChildTwo(String name, Integer childTwoProp) {
|
@Entity( name = "JoinedChildOne" )
|
||||||
super( name );
|
public static class JoinedChildOne extends JoinedParent {
|
||||||
this.childTwoProp = childTwoProp;
|
@Column( name = "child_one_col" )
|
||||||
}
|
private Integer childOneProp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "JoinedChildTwo" )
|
||||||
|
public static class JoinedChildTwo extends JoinedParent {
|
||||||
|
@Column( name = "child_two_col" )
|
||||||
|
private Integer childTwoProp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "TPCParent" )
|
||||||
|
@Inheritance( strategy = InheritanceType.TABLE_PER_CLASS )
|
||||||
|
public static class TPCParent extends Parent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "TPCChildOne" )
|
||||||
|
public static class TPCChildOne extends TPCParent {
|
||||||
|
@Column( name = "child_one_col" )
|
||||||
|
private Integer childOneProp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "TPCChildTwo" )
|
||||||
|
public static class TPCChildTwo extends TPCParent {
|
||||||
|
@Column( name = "child_two_col" )
|
||||||
|
private Integer childTwoProp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity( name = "MyEntity" )
|
@Entity( name = "MyEntity" )
|
||||||
|
@ -191,15 +352,29 @@ public class InheritanceQueryGroupByTest {
|
||||||
private Integer amount;
|
private Integer amount;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn( name = "parent_id" )
|
@JoinColumn( name = "single_table_parent_id" )
|
||||||
private Parent parent;
|
private SingleTableParent singleTableParent;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn( name = "joined_parent_id" )
|
||||||
|
private JoinedParent joinedParent;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn( name = "tpc_parent_id" )
|
||||||
|
private TPCParent tpcParent;
|
||||||
|
|
||||||
public MyEntity() {
|
public MyEntity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public MyEntity(Integer amount, Parent parent) {
|
public MyEntity(
|
||||||
|
Integer amount,
|
||||||
|
SingleTableParent singleTableParent,
|
||||||
|
JoinedParent joinedParent,
|
||||||
|
TPCParent tpcParent) {
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
this.parent = parent;
|
this.singleTableParent = singleTableParent;
|
||||||
|
this.joinedParent = joinedParent;
|
||||||
|
this.tpcParent = tpcParent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue