Fix joined subclass wrong table for id and version columns

This commit is contained in:
Andrea Boriero 2020-09-03 14:31:52 +01:00
parent f1ac2f8c09
commit 8fe4b40ef8
8 changed files with 242 additions and 109 deletions

View File

@ -185,7 +185,6 @@ public class EmbeddableMappingType implements ManagedMappingType {
final Selectable selectable = basicValue.getColumn();
final String mappedColumnExpression = mappedColumnExpressions.get( columnPosition++ );
assert mappedColumnExpression.equals( selectable.getText( sessionFactory.getDialect() ) );
attributeMappings.put(
bootPropertyDescriptor.getName(),

View File

@ -65,8 +65,6 @@ import org.hibernate.classic.Lifecycle;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.lock.LockingStrategy;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.engine.internal.CacheHelper;
@ -228,7 +226,6 @@ import org.hibernate.type.TypeHelper;
import org.hibernate.type.VersionType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Basic functionality for persisting an entity via JDBC
@ -1367,29 +1364,7 @@ public abstract class AbstractEntityPersister
);
}
protected TableReferenceJoin createTableReferenceJoin(
int subClassTablePosition,
TableReference rootTableReference,
SqlAstJoinType sqlAstJoinType,
SqlAliasBase sqlAliasBase,
SqlExpressionResolver sqlExpressionResolver) {
final boolean nullable = isNullableSubclassTable( subClassTablePosition );
final TableReference joinedTableReference = new TableReference(
getSubclassTableName( subClassTablePosition ),
sqlAliasBase.generateNewAlias(),
nullable,
getFactory()
);
return new TableReferenceJoin(
nullable ? SqlAstJoinType.LEFT : sqlAstJoinType,
joinedTableReference,
generateJoinPredicate( rootTableReference, joinedTableReference, subClassTablePosition, sqlExpressionResolver )
);
}
private Predicate generateJoinPredicate(
protected Predicate generateJoinPredicate(
TableReference rootTableReference,
TableReference joinedTableReference,
int subClassTablePosition,
@ -5799,8 +5774,8 @@ public abstract class AbstractEntityPersister
private EntityRowIdMapping rowIdMapping;
private EntityDiscriminatorMapping discriminatorMapping;
private Map<String, AttributeMapping> declaredAttributeMappings = new LinkedHashMap<>();
private List<AttributeMapping> attributeMappings;
protected Map<String, AttributeMapping> declaredAttributeMappings = new LinkedHashMap<>();
protected List<Fetchable> staticFetchableList;
protected ReflectionOptimizer.AccessOptimizer accessOptimizer;
@ -5822,53 +5797,21 @@ public abstract class AbstractEntityPersister
.getBootModel()
.getEntityBinding( getEntityName() );
if ( superMappingType != null && shouldProcessSuperMapping() ) {
if ( superMappingType != null ) {
( (InFlightEntityMappingType) superMappingType ).prepareMappingModel( creationProcess );
this.identifierMapping = superMappingType.getIdentifierMapping();
this.versionMapping = superMappingType.getVersionMapping();
this.rowIdMapping = superMappingType.getRowIdMapping();
this.naturalIdMapping = superMappingType.getNaturalIdMapping();
this.discriminatorMapping = superMappingType.getDiscriminatorMapping();
if ( shouldProcessSuperMapping() ) {
this.discriminatorMapping = superMappingType.getDiscriminatorMapping();
this.identifierMapping = superMappingType.getIdentifierMapping();
this.naturalIdMapping = superMappingType.getNaturalIdMapping();
this.versionMapping = superMappingType.getVersionMapping();
this.rowIdMapping = superMappingType.getRowIdMapping();
}
else {
prepareMappingModel( creationProcess, bootEntityDescriptor );
}
}
else {
identifierMapping = creationProcess.processSubPart(
EntityIdentifierMapping.ROLE_LOCAL_NAME,
(role, creationProcess1) ->
generateIdentifierMapping( creationProcess, bootEntityDescriptor )
);
if ( getVersionType() == null ) {
versionMapping = null;
}
else {
final int versionPropertyIndex = getVersionProperty();
final String versionPropertyName = getPropertyNames()[ versionPropertyIndex ];
versionMapping = creationProcess.processSubPart(
versionPropertyName,
(role, creationProcess1) -> generateVersionMapping(
this,
(RootClass) bootEntityDescriptor,
creationProcess
)
);
}
if ( rowIdName == null ) {
rowIdMapping = null;
}
else {
rowIdMapping = creationProcess.processSubPart(
rowIdName,
(role, creationProcess1) -> new EntityRowIdMappingImpl( rowIdName, this.getTableName(), this)
);
}
buildDiscriminatorMapping();
// todo (6.0) : support for natural-id not yet implemented
naturalIdMapping = null;
prepareMappingModel( creationProcess, bootEntityDescriptor );
}
final EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel();
@ -5923,13 +5866,37 @@ public abstract class AbstractEntityPersister
"Entity(" + getEntityName() + ") `staticFetchableList` generator",
() -> {
staticFetchableList = new ArrayList<>( attributeMappings.size() );
visitAttributeMappings( attributeMapping -> staticFetchableList.add( (Fetchable) attributeMapping ) );
visitSubTypeAttributeMappings( attributeMapping -> staticFetchableList.add( (Fetchable) attributeMapping ) );
visitAttributeMappings( attributeMapping -> staticFetchableList.add( attributeMapping ) );
visitSubTypeAttributeMappings( attributeMapping -> staticFetchableList.add( attributeMapping ) );
return true;
}
);
}
private void prepareMappingModel(MappingModelCreationProcess creationProcess, PersistentClass bootEntityDescriptor) {
identifierMapping = creationProcess.processSubPart(
EntityIdentifierMapping.ROLE_LOCAL_NAME,
(role, process) ->
generateIdentifierMapping( process, bootEntityDescriptor )
);
versionMapping = generateVersionMapping( creationProcess, bootEntityDescriptor );
if ( rowIdName == null ) {
rowIdMapping = null;
}
else {
rowIdMapping = creationProcess.processSubPart(
rowIdName,
(role, process) -> new EntityRowIdMappingImpl( rowIdName, this.getTableName(), this)
);
}
// todo (6.0) : support for natural-id not yet implemented
naturalIdMapping = null;
discriminatorMapping = generateDiscriminatorMapping();
}
protected static SqmMultiTableMutationStrategy interpretSqmMultiTableStrategy(
AbstractEntityPersister entityMappingDescriptor,
MappingModelCreationProcess creationProcess) {
@ -5970,12 +5937,12 @@ public abstract class AbstractEntityPersister
return stateArrayPosition;
}
protected void buildDiscriminatorMapping() {
protected EntityDiscriminatorMapping generateDiscriminatorMapping() {
if ( getDiscriminatorType() == null) {
discriminatorMapping = null;
return null;
}
else {
discriminatorMapping = new EntityDiscriminatorMappingImpl(
return new EntityDiscriminatorMappingImpl(
this,
getTableName(),
getDiscriminatorColumnReaders(),
@ -5984,6 +5951,27 @@ public abstract class AbstractEntityPersister
}
}
protected EntityVersionMapping generateVersionMapping(
MappingModelCreationProcess creationProcess,
PersistentClass bootEntityDescriptor) {
if ( getVersionType() == null ) {
return null;
}
else {
final int versionPropertyIndex = getVersionProperty();
final String versionPropertyName = getPropertyNames()[ versionPropertyIndex ];
return creationProcess.processSubPart(
versionPropertyName,
(role, creationProcess1) -> generateVersionMapping(
this,
bootEntityDescriptor,
creationProcess
)
);
}
}
protected boolean shouldProcessSuperMapping(){
return true;
}
@ -6056,7 +6044,7 @@ public abstract class AbstractEntityPersister
}
private EntityIdentifierMapping generateIdentifierMapping(MappingModelCreationProcess creationProcess, PersistentClass bootEntityDescriptor) {
protected EntityIdentifierMapping generateIdentifierMapping(MappingModelCreationProcess creationProcess, PersistentClass bootEntityDescriptor) {
final Type idType = getIdentifierType();
if ( idType instanceof CompositeType ) {
@ -6094,7 +6082,7 @@ public abstract class AbstractEntityPersister
);
}
private EntityIdentifierMapping generateNonEncapsulatedCompositeIdentifierMapping(
protected EntityIdentifierMapping generateNonEncapsulatedCompositeIdentifierMapping(
MappingModelCreationProcess creationProcess,
PersistentClass bootEntityDescriptor,
CompositeType cidType) {
@ -6116,9 +6104,9 @@ public abstract class AbstractEntityPersister
* @param bootModelRootEntityDescriptor The boot-time entity descriptor for the "root entity" in the hierarchy
* @param creationProcess The SF creation process - access to useful things
*/
private static EntityVersionMapping generateVersionMapping(
protected static EntityVersionMapping generateVersionMapping(
AbstractEntityPersister entityPersister,
RootClass bootModelRootEntityDescriptor,
PersistentClass bootModelRootEntityDescriptor,
MappingModelCreationProcess creationProcess) {
final BasicValue bootModelVersionValue = (BasicValue) bootModelRootEntityDescriptor.getVersion().getValue();
final BasicValue.Resolution<?> basicTypeResolution = bootModelVersionValue.resolve();
@ -6595,16 +6583,6 @@ public abstract class AbstractEntityPersister
collectAttributeDefinitions();
}
@Override
public void visitJdbcTypes(
Consumer<JdbcMapping> action,
Clause clause,
TypeConfiguration typeConfiguration) {
getAttributeMappings().forEach(
attributeMapping -> attributeMapping.visitJdbcTypes( action, clause, typeConfiguration )
);
}
@Override
public void visitJdbcValues(
Object value,

View File

@ -49,14 +49,21 @@ import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityVersionMapping;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.internal.BasicEntityIdentifierMappingImpl;
import org.hibernate.metamodel.mapping.internal.JoinedSubclassDiscriminatorMappingImpl;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.persister.spi.PersisterCreationContext;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.CaseFragment;
import org.hibernate.sql.InFragment;
import org.hibernate.sql.Insert;
import org.hibernate.sql.SelectFragment;
import org.hibernate.sql.ast.spi.SqlAliasBase;
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
@ -64,14 +71,17 @@ import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.from.StandardTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.entity.internal.EntityResultJoinedSubclassImpl;
import org.hibernate.type.BasicType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.DiscriminatorType;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
@ -1092,6 +1102,10 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
return subclassTableIsLazyClosure[j];
}
@Override
protected boolean shouldProcessSuperMapping() {
return false;
}
protected boolean isClassOrSuperclassTable(int j) {
return isClassOrSuperclassTable[j];
@ -1189,6 +1203,99 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
throw new HibernateException( "Could not locate table which owns column [" + columnName + "] referenced in order-by mapping" );
}
@Override
protected EntityVersionMapping generateVersionMapping(
MappingModelCreationProcess creationProcess, PersistentClass bootEntityDescriptor) {
if ( getVersionType() == null ) {
return null;
}
else {
if ( getTableName().equals( getVersionedTableName() ) ) {
final int versionPropertyIndex = getVersionProperty();
final String versionPropertyName = getPropertyNames()[versionPropertyIndex];
return creationProcess.processSubPart(
versionPropertyName,
(role, process) -> generateVersionMapping(
this,
bootEntityDescriptor,
process
)
);
}
else if ( getSuperMappingType() != null ) {
return getSuperMappingType().getVersionMapping();
}
}
return null;
}
@Override
protected EntityDiscriminatorMapping generateDiscriminatorMapping() {
EntityMappingType superMappingType = getSuperMappingType();
if ( superMappingType != null ) {
return superMappingType.getDiscriminatorMapping();
}
else {
return super.generateDiscriminatorMapping();
}
}
@Override
protected EntityIdentifierMapping generateIdentifierMapping(MappingModelCreationProcess creationProcess, PersistentClass bootEntityDescriptor) {
final Type idType = getIdentifierType();
if ( idType instanceof CompositeType ) {
final CompositeType cidType = (CompositeType) idType;
// NOTE: the term `isEmbedded` here uses Hibernate's older (pre-JPA) naming for its "non-aggregated"
// composite-id support. It unfortunately conflicts with the JPA usage of "embedded". Here we normalize
// the legacy naming to the more descriptive encapsulated versus non-encapsulated phrasing
final boolean encapsulated = ! cidType.isEmbedded();
if ( encapsulated ) {
// we have an `@EmbeddedId`
return MappingModelCreationHelper.buildEncapsulatedCompositeIdentifierMapping(
this,
bootEntityDescriptor.getIdentifierProperty(),
bootEntityDescriptor.getIdentifierProperty().getName(),
getTableName(),
tableKeyColumns[0],
cidType,
creationProcess
);
}
// otherwise we have a non-encapsulated composite-identifier
return generateNonEncapsulatedCompositeIdentifierMapping( creationProcess, bootEntityDescriptor, cidType );
}
return new BasicEntityIdentifierMappingImpl(
this,
bootEntityDescriptor.getIdentifierProperty().getName(),
getTableName(),
tableKeyColumns[0][0],
(BasicType) idType,
creationProcess
);
}
protected EntityIdentifierMapping generateNonEncapsulatedCompositeIdentifierMapping(
MappingModelCreationProcess creationProcess,
PersistentClass bootEntityDescriptor,
CompositeType cidType) {
assert declaredAttributeMappings != null;
return MappingModelCreationHelper.buildNonEncapsulatedCompositeIdentifierMapping(
this,
getTableName(),
tableKeyColumns[0],
cidType,
bootEntityDescriptor,
declaredAttributeMappings::put,
creationProcess
);
}
@Override
public FilterAliasGenerator getFilterAliasGenerator(String rootAlias) {
return new DynamicFilterAliasGenerator(subclassTableNameClosure, rootAlias);
@ -1225,16 +1332,56 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
SqlExpressionResolver sqlExpressionResolver,
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
SqlAstCreationContext creationContext) {
return super.createRootTableGroup(
navigablePath,
explicitSourceAlias,
canUseInnerJoins,
lockMode,
aliasBaseGenerator,
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
final TableReference primaryTableReference = createPrimaryTableReference(
sqlAliasBase,
sqlExpressionResolver,
additionalPredicateCollectorAccess,
creationContext
);
StandardTableGroup standardTableGroup = new StandardTableGroup(
navigablePath,
this,
lockMode,
primaryTableReference,
sqlAliasBase,
(tableExpression) -> ArrayHelper.contains( getSubclassTableNames(), tableExpression ),
(tableExpression, tableGroup) -> null,
getFactory()
);
final String primaryTableName = primaryTableReference.getTableExpression();
for ( int i = 1; i < getSubclassTableSpan(); i++ ) {
final String subclassTableName = getSubclassTableName( i );
if ( !subclassTableName.equals( primaryTableName ) ) {
final boolean isNullableTable = isNullableSubclassTable( i );
final TableReference joinedTableReference = new TableReference(
subclassTableName,
sqlAliasBase.generateNewAlias(),
isNullableTable,
getFactory()
);
TableReferenceJoin tableReferenceJoin = new TableReferenceJoin(
determineSubclassTableJoinType(
i,
canUseInnerJoins,
true,
Collections.emptySet()
),
joinedTableReference,
generateJoinPredicate(
primaryTableReference,
joinedTableReference,
i,
sqlExpressionResolver
)
);
standardTableGroup.addTableReferenceJoin( tableReferenceJoin );
}
}
return standardTableGroup;
}
@Override

View File

@ -41,6 +41,7 @@ import org.hibernate.mapping.Column;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.Table;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.persister.spi.PersisterCreationContext;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.SelectFragment;
@ -398,10 +399,11 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
}
@Override
protected void buildDiscriminatorMapping() {
protected EntityDiscriminatorMapping generateDiscriminatorMapping() {
if ( hasSubclasses() ) {
super.buildDiscriminatorMapping();
return super.generateDiscriminatorMapping();
}
return null;
}
@Override

View File

@ -4,7 +4,7 @@
* 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.test.constraint;
package org.hibernate.orm.test.constraint;
import java.util.Iterator;
import javax.persistence.Entity;

View File

@ -4,10 +4,9 @@
* 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.test.constraint;
package org.hibernate.orm.test.constraint;
import java.util.Iterator;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
@ -18,10 +17,10 @@ import javax.persistence.OneToOne;
import org.hibernate.boot.model.relational.Namespace;
import org.hibernate.mapping.Table;
import org.junit.Test;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

View File

@ -4,7 +4,7 @@
* 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.test.constraint;
package org.hibernate.orm.test.constraint;
import java.io.Serializable;
import java.util.Arrays;
@ -15,7 +15,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.persistence.AssociationOverride;
import javax.persistence.AssociationOverrides;
import javax.persistence.CollectionTable;
@ -47,10 +46,10 @@ import javax.persistence.SecondaryTable;
import org.hibernate.boot.model.relational.Namespace;
import org.hibernate.mapping.Column;
import org.junit.Test;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@ -190,6 +189,15 @@ public class ForeignKeyConstraintTest extends BaseNonConfigCoreFunctionalTestCas
fail( "ForeignKey '" + foreignKeyName + "' could not be found!" );
}
@Test
public void testGet(){
inTransaction(
session -> {
session.get( Student.class, 1l );
}
);
}
private void assertNoForeignKey(String foreignKeyName, String... columns) {
Set<String> columnSet = new LinkedHashSet<>( Arrays.asList( columns ) );
for ( Namespace namespace : metadata().getDatabase().getNamespaces() ) {

View File

@ -4,7 +4,7 @@
* 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.test.constraint;
package org.hibernate.orm.test.constraint;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
@ -18,10 +18,10 @@ import javax.persistence.PrimaryKeyJoinColumn;
import org.hibernate.boot.model.relational.Namespace;
import org.hibernate.mapping.Table;
import org.junit.Test;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import static org.junit.Assert.assertEquals;