HHH-15837 - Cleanup the tuple package

This commit is contained in:
Steve Ebersole 2022-12-09 11:38:01 -06:00
parent ac32410438
commit 48c383121f
12 changed files with 293 additions and 71 deletions

View File

@ -94,6 +94,11 @@ public class BatchBuilderImpl implements BatchBuilder {
return 0;
}
@Override
public KeyDetails getKeyDetails() {
return null;
}
@Override
public boolean isOptional() {
return false;

View File

@ -190,7 +190,9 @@ public interface EntityMappingType
// Inheritance
/**
* Get the class that this class is mapped as a subclass of
* Get the name of the entity that is the "super class" for this entity
*
* @see #getSuperMappingType
*/
default String getMappedSuperclass() {
return getSuperMappingType().getEntityName();

View File

@ -29,16 +29,12 @@ import org.hibernate.type.descriptor.java.JavaType;
* @author Steve Ebersole
*/
public interface ModelPart extends MappingModelExpressible {
MappingType getPartMappingType();
JavaType<?> getJavaType();
String getPartName();
/**
* @asciidoc
*
* The path for this fetchable back to an entity in the domain model.
* The path for this fetchable back to an entity in the domain model. Acts as a unique
* identifier for individual parts.
*
* Some examples:
*
@ -63,6 +59,43 @@ public interface ModelPart extends MappingModelExpressible {
*/
NavigableRole getNavigableRole();
/**
* The local part name, which is generally the unqualified role name
*/
String getPartName();
/**
* The type for this part.
*/
MappingType getPartMappingType();
/**
* The Java type for this part. Generally equivalent to
* {@link MappingType#getMappedJavaType()} relative to
* {@link #getPartMappingType()}
*/
JavaType<?> getJavaType();
/**
* Whether this model part describes something that physically
* exists in the domain model.
* <p/>
* For example, take an entity defining its identifier with multiple
* {@link jakarta.persistence.Id} attributes; a "non-aggregated" identifier.
* Internally, Hibernate models the
* {@linkplain EntityMappingType#getIdentifierMapping() identifier mapping}
* for that entity as a virtual {@link EmbeddableMappingType}. The entity
* might also define an {@link jakarta.persistence.IdClass}, but that is a
* different mapping; in this case, the entity will have 2 mappings - one
* physical (the {@link jakarta.persistence.IdClass}) and the other virtual
* (the "non-aggregated" embeddable)
* <p/>
* Also indicates whether the part is castable to {@link VirtualModelPart}
*/
default boolean isVirtual() {
return false;
}
/**
* Create a DomainResult for a specific reference to this ModelPart.
*/

View File

@ -20,7 +20,15 @@ public interface TableDetails {
String getTableName();
/**
* Whether this table is the root for a given {@link ModelPartContainer}
* Details about the primary-key of this table
*/
KeyDetails getKeyDetails();
/**
* Whether this table is the root for a given {@link ModelPartContainer}.
* <p/>
* Only relevant for entity-mappings where this indicates whether this
* table holds the entity's identifier.
*/
boolean isIdentifierTable();
@ -36,7 +44,7 @@ public interface TableDetails {
/**
* Group of columns defined on the primary key
*/
List<KeyColumn> getKeyColumns();
List<? extends KeyColumn> getKeyColumns();
/**
* Get a key column by relative position

View File

@ -7,9 +7,16 @@
package org.hibernate.metamodel.mapping;
/**
* Marker interface for parts of the application domain model that do not actually exist in the model classes
* Marker interface for parts of the application domain model that do not actually
* exist in the model classes.
*
* @see #isVirtual()
*
* @author Steve Ebersole
*/
public interface VirtualModelPart extends ModelPart {
@Override
default boolean isVirtual() {
return true;
}
}

View File

@ -49,6 +49,12 @@ public class CollectionTableMapping implements TableMapping {
return tableName;
}
@Override
public KeyDetails getKeyDetails() {
// todo (tuple-cleanup) : implement this
return null;
}
public boolean isJoinTable() {
return isJoinTable;
}

View File

@ -1294,7 +1294,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
if ( superMappingType == null ) {
return getMappedTableDetails();
}
return superMappingType.getIdentifierTableDetails();
return getRootEntityDescriptor().getIdentifierTableDetails();
}
@Override

View File

@ -17,6 +17,7 @@ import org.hibernate.jdbc.Expectation;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.TableDetails;
import org.hibernate.sql.model.MutationType;
import org.hibernate.sql.model.TableMapping;
@ -92,6 +93,11 @@ public class EntityTableMapping implements TableMapping {
return tableName;
}
@Override
public KeyDetails getKeyDetails() {
return keyMapping;
}
@Override public int getRelativePosition() {
return relativePosition;
}
@ -203,7 +209,7 @@ public class EntityTableMapping implements TableMapping {
void consume(Object jdbcValue, KeyColumn columnMapping);
}
public static class KeyMapping {
public static class KeyMapping implements KeyDetails {
private final List<KeyColumn> keyColumns;
private final ModelPart identifierPart;
@ -230,12 +236,34 @@ public class EntityTableMapping implements TableMapping {
);
}
@Override
public int getColumnCount() {
return keyColumns.size();
}
@Override
public List<KeyColumn> getKeyColumns() {
return keyColumns;
}
@Override
public KeyColumn getKeyColumn(int position) {
return keyColumns.get( position );
}
@Override
public void forEachKeyColumn(KeyColumnConsumer consumer) {
for ( int i = 0; i < keyColumns.size(); i++ ) {
consumer.consume( i, keyColumns.get( i ) );
}
}
public void forEachKeyColumn(Consumer<KeyColumn> keyColumnConsumer) {
keyColumns.forEach( keyColumnConsumer );
}
}
public static class KeyColumn implements SelectableMapping {
public static class KeyColumn implements SelectableMapping, TableDetails.KeyColumn {
private final String tableName;
private final String columnName;
private final String writeExpression;

View File

@ -41,6 +41,8 @@ import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.profile.FetchProfile;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.generator.Generator;
import org.hibernate.generator.InMemoryGenerator;
import org.hibernate.graph.spi.AppliedGraph;
import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
import org.hibernate.id.CompositeNestedGeneratedValueGenerator;
@ -382,8 +384,6 @@ import org.hibernate.sql.results.graph.FetchableContainer;
import org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiation;
import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.sql.results.internal.StandardEntityGraphTraversalStateImpl;
import org.hibernate.generator.Generator;
import org.hibernate.generator.InMemoryGenerator;
import org.hibernate.type.BasicType;
import org.hibernate.type.CustomType;
import org.hibernate.type.EnumType;

View File

@ -24,7 +24,6 @@ import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.orm.test.common.JournalingBatchObserver;
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
import org.hibernate.sql.model.MutationType;
import org.hibernate.sql.model.TableMapping;
import org.hibernate.sql.model.jdbc.JdbcValueDescriptor;
import org.hibernate.type.StandardBasicTypes;
@ -32,7 +31,6 @@ import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.jdbc.Expectations.NONE;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@ -42,52 +40,6 @@ import static org.junit.Assert.assertTrue;
*/
public class BatchingTest extends BaseCoreFunctionalTestCase implements BatchKey {
private final String SANDBOX_TBL = "SANDBOX_JDBC_TST";
private final TableMapping SANDBOX_TBL_MAPPING = new TableMapping() {
@Override
public String getTableName() {
return SANDBOX_TBL;
}
@Override
public int getRelativePosition() {
return 0;
}
@Override
public boolean isOptional() {
return false;
}
@Override
public boolean isInverse() {
return false;
}
@Override
public boolean isIdentifierTable() {
return true;
}
@Override
public MutationDetails getInsertDetails() {
return new MutationDetails( MutationType.INSERT, NONE, null, false );
}
@Override
public MutationDetails getUpdateDetails() {
return new MutationDetails( MutationType.UPDATE, NONE, null, false );
}
@Override
public boolean isCascadeDeleteEnabled() {
return false;
}
@Override
public MutationDetails getDeleteDetails() {
return new MutationDetails( MutationType.DELETE, NONE, null, false );
}
};
@Override
public int getBatchedStatementCount() {

View File

@ -0,0 +1,185 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.orm.test.mapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.TableDetails;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.orm.domain.StandardDomainModel;
import org.hibernate.testing.orm.domain.retail.CardPayment;
import org.hibernate.testing.orm.domain.retail.Payment;
import org.hibernate.testing.orm.domain.retail.Vendor;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Basic;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.Table;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @see org.hibernate.persister.entity.mutation.EntityTableMapping
*
* @author Steve Ebersole
*/
@DomainModel(
standardModels = StandardDomainModel.RETAIL,
annotatedClasses = {
EntityTableMappingsTests.UnionRoot.class,
EntityTableMappingsTests.UnionSub1.class,
EntityTableMappingsTests.UnionSub2.class
}
)
@SessionFactory(exportSchema = false)
public class EntityTableMappingsTests {
@Test
public void testSingleTableHierarchyTableDetails(SessionFactoryScope scope) {
final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory()
.getRuntimeMetamodels()
.getMappingMetamodel();
verifyTableMappings(
mappingMetamodel.getEntityDescriptor( Vendor.class ),
"Vendor"
);
}
@Test
public void testJoinedHierarchyTableDetails(SessionFactoryScope scope) {
final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory()
.getRuntimeMetamodels()
.getMappingMetamodel();
// root
verifyTableMappings(
mappingMetamodel.getEntityDescriptor( Payment.class ),
"payments"
);
// sub
verifyTableMappings(
mappingMetamodel.getEntityDescriptor( CardPayment.class ),
"payments",
"CardPayment"
);
}
@Test
public void testUnionHierarchyTableDetails(SessionFactoryScope scope) {
final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory()
.getRuntimeMetamodels()
.getMappingMetamodel();
// root
final EntityPersister rootDescriptor = mappingMetamodel.getEntityDescriptor( UnionRoot.class );
verifyTableMappings(
rootDescriptor,
"UnionRoot",
"UnionRoot"
);
// sub1
final EntityPersister sub1Descriptor = mappingMetamodel.getEntityDescriptor( UnionSub1.class );
verifyTableMappings(
sub1Descriptor,
"unions_subs1",
"unions_subs1"
);
// sub2
final EntityPersister sub2Descriptor = mappingMetamodel.getEntityDescriptor( UnionSub2.class );
verifyTableMappings(
sub2Descriptor,
"unions_subs2",
"unions_subs2"
);
}
private void verifyTableMappings(EntityMappingType entityDescriptor, String tableName) {
verifyTableMappings( entityDescriptor, tableName, tableName );
}
private void verifyTableMappings(
EntityMappingType entityDescriptor,
String identifierTableName,
String mappedTableName) {
final TableDetails idTable = entityDescriptor.getIdentifierTableDetails();
assertThat( idTable.getTableName() ).isEqualTo( identifierTableName );
assertThat( idTable.isIdentifierTable() ).isTrue();
assertThat( idTable.getKeyDetails().getColumnCount() ).isEqualTo( 1 );
assertThat( idTable.getKeyDetails().getKeyColumn( 0 ).getColumnName() ).isEqualTo( "id" );
final TableDetails mappedTable = entityDescriptor.getMappedTableDetails();
assertThat( mappedTable.getTableName() ).isEqualTo( mappedTableName );
assertThat( idTable.getKeyDetails().getColumnCount() ).isEqualTo( 1 );
assertThat( idTable.getKeyDetails().getKeyColumn( 0 ).getColumnName() ).isEqualTo( "id" );
if ( mappedTableName.equals( identifierTableName ) ) {
assertThat( mappedTable.isIdentifierTable() ).isTrue();
}
}
@Entity( name = "UnionRoot" )
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public static abstract class UnionRoot {
@Id
private Integer id;
@Basic
private String name;
private UnionRoot() {
// for use by Hibernate
}
public UnionRoot(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Entity( name = "UnionSub1" )
@Table( name = "unions_subs1" )
public static class UnionSub1 extends UnionRoot {
private UnionSub1() {
// for use by Hibernate
}
public UnionSub1(Integer id, String name) {
super( id, name );
}
}
@Entity( name = "UnionSub2" )
@Table( name = "unions_subs2" )
public static class UnionSub2 extends UnionRoot {
private UnionSub2() {
// for use by Hibernate
}
public UnionSub2(Integer id, String name) {
super( id, name );
}
}
}

View File

@ -651,15 +651,11 @@ public class ValidityAuditStrategy implements AuditStrategy {
}
private String getUpdateTableName(Queryable rootEntity, Queryable rootAuditEntity, Queryable auditEntity) {
if ( UnionSubclassEntityPersister.class.isInstance( rootEntity ) ) {
// This is the condition causing all the problems of the generated SQL update;
// the problem being that we currently try to update the inline view made of the union query.
//
// This is hacky to get the root table name for the union subclass style entities because
// it relies on internal behavior of UnionSubclassEntityPersister.
return auditEntity.getSubclassTableName( 0 );
if ( rootEntity instanceof UnionSubclassEntityPersister ) {
// we need to specially handle union-subclass mappings
return auditEntity.getMappedTableDetails().getTableName();
}
return rootAuditEntity.getTableName();
return rootAuditEntity.getMappedTableDetails().getTableName();
}
/**