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.Pattern;
|
||||
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.PessimisticLockException;
|
||||
import org.hibernate.QueryTimeoutException;
|
||||
import org.hibernate.boot.model.FunctionContributions;
|
||||
import org.hibernate.boot.model.TypeContributions;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
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.*;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
import org.hibernate.dialect.function.FormatFunction;
|
||||
import org.hibernate.dialect.function.PostgreSQLTruncFunction;
|
||||
|
@ -87,11 +74,38 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
|
||||
import static org.hibernate.query.sqm.TemporalUnit.DAY;
|
||||
import static org.hibernate.query.sqm.TemporalUnit.EPOCH;
|
||||
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.appendAsLocalTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||
|
@ -1022,6 +1036,11 @@ public class CockroachLegacyDialect extends Dialect {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP_AND_CONSTANTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RowLockStrategy getWriteRowLockStrategy() {
|
||||
return getVersion().isSameOrAfter( 20, 1 ) ? RowLockStrategy.TABLE : RowLockStrategy.NONE;
|
||||
|
|
|
@ -19,14 +19,7 @@ import java.util.TimeZone;
|
|||
import org.hibernate.PessimisticLockException;
|
||||
import org.hibernate.boot.model.FunctionContributions;
|
||||
import org.hibernate.boot.model.TypeContributions;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
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.*;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
import org.hibernate.dialect.hint.IndexQueryHintHandler;
|
||||
import org.hibernate.dialect.identity.H2FinalTableIdentityColumnSupport;
|
||||
|
@ -829,6 +822,11 @@ public class H2LegacyDialect extends Dialect {
|
|||
return getVersion().isSameOrAfter( 1, 4, 198 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP_AND_CONSTANTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityColumnSupport getIdentityColumnSupport() {
|
||||
return getVersion().isSameOrAfter( 2 ) ? H2FinalTableIdentityColumnSupport.INSTANCE : H2IdentityColumnSupport.INSTANCE;
|
||||
|
|
|
@ -14,11 +14,7 @@ import org.hibernate.JDBCException;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.StaleObjectStateException;
|
||||
import org.hibernate.boot.model.FunctionContributions;
|
||||
import org.hibernate.dialect.BooleanDecoder;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.OracleDialect;
|
||||
import org.hibernate.dialect.SimpleDatabaseVersion;
|
||||
import org.hibernate.dialect.*;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
import org.hibernate.dialect.identity.HSQLIdentityColumnSupport;
|
||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||
|
@ -786,6 +782,11 @@ public class HSQLLegacyDialect extends Dialect {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||
return FunctionalDependencyAnalysisSupportImpl.TABLE_REFERENCE;
|
||||
}
|
||||
|
||||
// Do not drop constraints explicitly, just do this by cascading instead.
|
||||
@Override
|
||||
public boolean dropConstraints() {
|
||||
|
|
|
@ -11,13 +11,7 @@ import java.sql.SQLException;
|
|||
|
||||
import org.hibernate.boot.model.FunctionContributions;
|
||||
import org.hibernate.boot.model.TypeContributions;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
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.*;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
import org.hibernate.dialect.sequence.MariaDBSequenceSupport;
|
||||
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||
|
@ -249,6 +243,11 @@ public class MariaDBLegacyDialect extends MySQLLegacyDialect {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP_AND_CONSTANTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData)
|
||||
throws SQLException {
|
||||
|
|
|
@ -17,16 +17,7 @@ import org.hibernate.PessimisticLockException;
|
|||
import org.hibernate.boot.model.FunctionContributions;
|
||||
import org.hibernate.boot.model.TypeContributions;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
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.*;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
import org.hibernate.dialect.hint.IndexQueryHintHandler;
|
||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||
|
@ -1367,6 +1358,11 @@ public class MySQLLegacyDialect extends Dialect {
|
|||
return getMySQLVersion().isSameOrAfter( 8 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDisableConstraints() {
|
||||
return true;
|
||||
|
|
|
@ -26,20 +26,7 @@ import org.hibernate.QueryTimeoutException;
|
|||
import org.hibernate.boot.model.FunctionContributions;
|
||||
import org.hibernate.boot.model.TypeContributions;
|
||||
import org.hibernate.community.dialect.sequence.PostgreSQLLegacySequenceSupport;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
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.*;
|
||||
import org.hibernate.dialect.aggregate.AggregateSupport;
|
||||
import org.hibernate.dialect.aggregate.PostgreSQLAggregateSupport;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
|
@ -1287,6 +1274,11 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||
return FunctionalDependencyAnalysisSupportImpl.TABLE_REFERENCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RowLockStrategy getWriteRowLockStrategy() {
|
||||
return RowLockStrategy.TABLE;
|
||||
|
|
|
@ -988,6 +988,11 @@ public class CockroachDialect extends Dialect {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP_AND_CONSTANTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RowLockStrategy getWriteRowLockStrategy() {
|
||||
return RowLockStrategy.TABLE;
|
||||
|
|
|
@ -5299,4 +5299,12 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
|||
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
|
||||
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.spi.SequenceInformationExtractor;
|
||||
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.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.TimestampUtcAsInstantJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||
|
@ -823,6 +823,11 @@ public class H2Dialect extends Dialect {
|
|||
return getVersion().isSameOrAfter( 1, 4, 198 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP_AND_CONSTANTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityColumnSupport getIdentityColumnSupport() {
|
||||
return getVersion().isSameOrAfter( 2 )
|
||||
|
|
|
@ -605,6 +605,11 @@ public class HSQLDialect extends Dialect {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||
return FunctionalDependencyAnalysisSupportImpl.TABLE_REFERENCE;
|
||||
}
|
||||
|
||||
// Do not drop constraints explicitly, just do this by cascading instead.
|
||||
@Override
|
||||
public boolean dropConstraints() {
|
||||
|
|
|
@ -248,6 +248,11 @@ public class MariaDBDialect extends MySQLDialect {
|
|||
return getVersion().isSameOrAfter( 10, 5 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP_AND_CONSTANTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData)
|
||||
throws SQLException {
|
||||
|
|
|
@ -1470,6 +1470,11 @@ public class MySQLDialect extends Dialect {
|
|||
return getMySQLVersion().isSameOrAfter( 8 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDisableConstraints() {
|
||||
return true;
|
||||
|
|
|
@ -78,7 +78,6 @@ import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
|||
import org.hibernate.type.JavaObjectType;
|
||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||
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.ClobJdbcType;
|
||||
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.query.sqm.TemporalUnit.DAY;
|
||||
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.appendAsLocalTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||
|
@ -1308,6 +1334,11 @@ public class PostgreSQLDialect extends Dialect {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||
return FunctionalDependencyAnalysisSupportImpl.TABLE_REFERENCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RowLockStrategy getWriteRowLockStrategy() {
|
||||
return RowLockStrategy.TABLE;
|
||||
|
|
|
@ -11,6 +11,8 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
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.EntityAssociationMapping;
|
||||
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.internal.EntityCollectionPart;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
import org.hibernate.persister.entity.UnionSubclassEntityPersister;
|
||||
import org.hibernate.query.derived.AnonymousTupleEntityValuedModelPart;
|
||||
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||
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>
|
||||
implements SqlTupleContainer, Assignable {
|
||||
private final Expression sqlExpression;
|
||||
|
||||
public static <T> EntityValuedPathInterpretation<T> from(
|
||||
SqmEntityValuedSimplePath<T> sqmPath,
|
||||
|
@ -266,7 +270,7 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
|||
}
|
||||
else {
|
||||
// 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 );
|
||||
}
|
||||
}
|
||||
|
@ -277,35 +281,40 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
|||
final SqlExpressionResolver sqlExprResolver = sqlAstCreationState.getSqlExpressionResolver();
|
||||
final Expression sqlExpression;
|
||||
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 EntityIdentifierMapping identifierMapping = entityMappingType.getIdentifierMapping();
|
||||
final EntityDiscriminatorMapping discriminatorMapping = entityMappingType.getDiscriminatorMapping();
|
||||
final int numberOfFetchables = entityMappingType.getNumberOfFetchables();
|
||||
final List<Expression> expressions = new ArrayList<>(
|
||||
numberOfFetchables + identifierMapping.getJdbcTypeCount()
|
||||
+ ( discriminatorMapping == null ? 0 : 1 )
|
||||
);
|
||||
final TableGroup parentTableGroup = tableGroup;
|
||||
final List<Expression> expressions = new ArrayList<>( identifierMapping.getJdbcTypeCount() );
|
||||
final SelectableConsumer selectableConsumer = (selectionIndex, selectableMapping) -> {
|
||||
final TableReference tableReference = parentTableGroup.resolveTableReference(
|
||||
final TableReference tableReference = tableGroup.resolveTableReference(
|
||||
navigablePath,
|
||||
selectableMapping.getContainingTableExpression()
|
||||
);
|
||||
expressions.add(
|
||||
sqlExprResolver.resolveSqlExpression( tableReference, selectableMapping )
|
||||
);
|
||||
expressions.add( sqlExprResolver.resolveSqlExpression( tableReference, selectableMapping ) );
|
||||
};
|
||||
identifierMapping.forEachSelectable( selectableConsumer );
|
||||
if ( !supportsFunctionalDependency( dialect, entityMappingType ) ) {
|
||||
final EntityDiscriminatorMapping discriminatorMapping = entityMappingType.getDiscriminatorMapping();
|
||||
if ( discriminatorMapping != null ) {
|
||||
discriminatorMapping.forEachSelectable( selectableConsumer );
|
||||
expressions.add( discriminatorMapping.resolveSqlExpression(
|
||||
navigablePath,
|
||||
discriminatorMapping.getUnderlyingJdbcMapping(),
|
||||
tableGroup,
|
||||
sqlAstCreationState
|
||||
) );
|
||||
}
|
||||
for ( int i = 0; i < numberOfFetchables; i++ ) {
|
||||
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 );
|
||||
}
|
||||
else {
|
||||
|
@ -373,7 +382,21 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
|||
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(
|
||||
Expression sqlExpression,
|
||||
|
|
|
@ -6,6 +6,11 @@
|
|||
*/
|
||||
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.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.Jira;
|
||||
|
@ -22,8 +27,11 @@ import jakarta.persistence.DiscriminatorValue;
|
|||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Inheritance;
|
||||
import jakarta.persistence.InheritanceType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
@ -33,19 +41,34 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
@SessionFactory( useCollectingStatementInspector = true )
|
||||
@DomainModel( annotatedClasses = {
|
||||
InheritanceQueryGroupByTest.Parent.class,
|
||||
InheritanceQueryGroupByTest.ChildOne.class,
|
||||
InheritanceQueryGroupByTest.ChildTwo.class,
|
||||
InheritanceQueryGroupByTest.MyEntity.class
|
||||
InheritanceQueryGroupByTest.SingleTableParent.class,
|
||||
InheritanceQueryGroupByTest.SingleTableChildOne.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-16773" )
|
||||
public class InheritanceQueryGroupByTest {
|
||||
@BeforeAll
|
||||
public void setUp(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
final ChildOne childOne = new ChildOne( "child_one", 1 );
|
||||
session.persist( childOne );
|
||||
session.persist( new MyEntity( 1, childOne ) );
|
||||
session.persist( new MyEntity( 2, childOne ) );
|
||||
final SingleTableChildOne st1 = new SingleTableChildOne();
|
||||
st1.setName( "single_table_child_one" );
|
||||
session.persist( st1 );
|
||||
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) {
|
||||
scope.inTransaction( session -> {
|
||||
session.createMutationQuery( "delete from MyEntity" ).executeUpdate();
|
||||
session.createMutationQuery( "delete from Parent" ).executeUpdate();
|
||||
session.createMutationQuery( "delete from " + Parent.class.getName() ).executeUpdate();
|
||||
} );
|
||||
}
|
||||
|
||||
@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();
|
||||
statementInspector.clear();
|
||||
scope.inTransaction( session -> {
|
||||
final MyPojo myPojo = session.createQuery(
|
||||
"select new "
|
||||
+ MyPojo.class.getName()
|
||||
+ "(sum(e.amount), re) from MyEntity e join e.parent re group by re",
|
||||
String.format(
|
||||
"select new %s(sum(e.amount), re) from MyEntity e join e.%s re group by re",
|
||||
MyPojo.class.getName(),
|
||||
parentProp
|
||||
),
|
||||
MyPojo.class
|
||||
).getSingleResult();
|
||||
assertThat( myPojo.getAmount() ).isEqualTo( 3L );
|
||||
assertThat( myPojo.getParent().getName() ).isEqualTo( "child_one" );
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_one_col", 2 );
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", 2 );
|
||||
assertThat( myPojo.getParent().getName() ).isEqualTo( parentName );
|
||||
final EntityMappingType entityMappingType = scope.getSessionFactory()
|
||||
.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
|
||||
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();
|
||||
statementInspector.clear();
|
||||
scope.inTransaction( session -> {
|
||||
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
|
||||
).getSingleResult();
|
||||
assertThat( sum ).isEqualTo( 3L );
|
||||
// When not selected, group by should only use the foreign key (parent_id)
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "parent_id", 2 );
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_one_col", 0 );
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", 0 );
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, parentFkName, 2 );
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_one_col", childPropCount );
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", childPropCount );
|
||||
} );
|
||||
}
|
||||
|
||||
@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();
|
||||
statementInspector.clear();
|
||||
scope.inTransaction( session -> {
|
||||
final MyPojo myPojo = session.createQuery(
|
||||
"select new "
|
||||
+ MyPojo.class.getName()
|
||||
+ "(sum(e.amount), re) from MyEntity e join e.parent re group by re order by re",
|
||||
String.format(
|
||||
"select new %s(sum(e.amount), re) from MyEntity e join e.%s re group by re order by re",
|
||||
MyPojo.class.getName(),
|
||||
parentProp
|
||||
),
|
||||
MyPojo.class
|
||||
).getSingleResult();
|
||||
assertThat( myPojo.getAmount() ).isEqualTo( 3L );
|
||||
assertThat( myPojo.getParent().getName() ).isEqualTo( "child_one" );
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_one_col", 3 );
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", 3 );
|
||||
assertThat( myPojo.getParent().getName() ).isEqualTo( parentName );
|
||||
final EntityMappingType entityMappingType = scope.getSessionFactory()
|
||||
.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
|
||||
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();
|
||||
statementInspector.clear();
|
||||
scope.inTransaction( session -> {
|
||||
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
|
||||
).getSingleResult();
|
||||
assertThat( sum ).isEqualTo( 3L );
|
||||
// When not selected, group by and order by should only use the foreign key (parent_id)
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "parent_id", 3 );
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_one_col", 0 );
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", 0 );
|
||||
// When not selected, group by should only use the foreign key (parent_id)
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, parentFkName, 3 );
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_one_col", childPropCount );
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", childPropCount );
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity( name = "Parent" )
|
||||
@DiscriminatorColumn( name = "disc_col", discriminatorType = DiscriminatorType.INTEGER )
|
||||
private static Dialect getDialect(SessionFactoryScope scope) {
|
||||
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 {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
|
@ -136,13 +276,6 @@ public class InheritanceQueryGroupByTest {
|
|||
|
||||
private String name;
|
||||
|
||||
public Parent() {
|
||||
}
|
||||
|
||||
public Parent(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
@ -150,36 +283,64 @@ public class InheritanceQueryGroupByTest {
|
|||
public String getName() {
|
||||
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" )
|
||||
public static class ChildOne extends Parent {
|
||||
public static class SingleTableChildOne extends SingleTableParent {
|
||||
@Column( name = "child_one_col" )
|
||||
private Integer childOneProp;
|
||||
|
||||
public ChildOne() {
|
||||
}
|
||||
|
||||
public ChildOne(String name, Integer childOneProp) {
|
||||
super( name );
|
||||
this.childOneProp = childOneProp;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "ChildTwo" )
|
||||
@Entity( name = "SingleTableChildTwo" )
|
||||
@DiscriminatorValue( "2" )
|
||||
public static class ChildTwo extends Parent {
|
||||
public static class SingleTableChildTwo extends SingleTableParent {
|
||||
@Column( name = "child_two_col" )
|
||||
private Integer childTwoProp;
|
||||
|
||||
public ChildTwo() {
|
||||
}
|
||||
|
||||
public ChildTwo(String name, Integer childTwoProp) {
|
||||
super( name );
|
||||
this.childTwoProp = childTwoProp;
|
||||
@Entity( name = "JoinedParent" )
|
||||
@Inheritance( strategy = InheritanceType.JOINED )
|
||||
public static class JoinedParent extends Parent {
|
||||
}
|
||||
|
||||
@Entity( name = "JoinedChildOne" )
|
||||
public static class JoinedChildOne extends JoinedParent {
|
||||
@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" )
|
||||
|
@ -191,15 +352,29 @@ public class InheritanceQueryGroupByTest {
|
|||
private Integer amount;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn( name = "parent_id" )
|
||||
private Parent parent;
|
||||
@JoinColumn( name = "single_table_parent_id" )
|
||||
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(Integer amount, Parent parent) {
|
||||
public MyEntity(
|
||||
Integer amount,
|
||||
SingleTableParent singleTableParent,
|
||||
JoinedParent joinedParent,
|
||||
TPCParent tpcParent) {
|
||||
this.amount = amount;
|
||||
this.parent = parent;
|
||||
this.singleTableParent = singleTableParent;
|
||||
this.joinedParent = joinedParent;
|
||||
this.tpcParent = tpcParent;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue