HHH-15818 - Drop PropertyMapping in favor of new mapping-model

This commit is contained in:
Steve Ebersole 2022-12-05 18:45:56 -06:00
parent 3d72eabf6c
commit 33c00d78c3
10 changed files with 141 additions and 77 deletions

View File

@ -75,18 +75,16 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
return;
}
if ( persister.canExtractIdOutOfEntity() ) {
final Object oid = persister.getIdentifier( object, session );
final Object oid = persister.getIdentifier( object, session );
if ( id == null ) {
throw new AssertionFailure( "null id in " + persister.getEntityName()
+ " entry (don't flush the Session after an exception occurs)" );
}
if ( id == null ) {
throw new AssertionFailure( "null id in " + persister.getEntityName()
+ " entry (don't flush the Session after an exception occurs)" );
}
if ( !persister.getIdentifierType().isEqual( id, oid, session.getFactory() ) ) {
throw new HibernateException( "identifier of an instance of " + persister.getEntityName()
+ " was altered from " + oid + " to " + id );
}
if ( !persister.getIdentifierType().isEqual( id, oid, session.getFactory() ) ) {
throw new HibernateException( "identifier of an instance of " + persister.getEntityName()
+ " was altered from " + oid + " to " + id );
}
}

View File

@ -372,6 +372,7 @@ public abstract class AbstractEntityCollectionPart implements EntityCollectionPa
while ( ( dotIndex = referencedPropertyName.indexOf( '.', dotIndex + 1 ) ) != -1 ) {
targetKeyPropertyNames.add( referencedPropertyName.substring( 0, dotIndex ) );
}
// todo (PropertyMapping) : the problem here is timing. this needs to be delayed.
final Type propertyType = ( (PropertyMapping) elementTypeDescriptor.getEntityPersister() )
.toType( referencedPropertyName );
ToOneAttributeMapping.addPrefixedPropertyNames(

View File

@ -1173,6 +1173,7 @@ public abstract class AbstractCollectionPersister
@Override
public Type toType(String propertyName) throws QueryException {
// todo (PropertyMapping) : simple delegation (aka, easy to remove)
if ( "index".equals( propertyName ) ) {
return indexType;
}
@ -1308,6 +1309,7 @@ public abstract class AbstractCollectionPersister
@Override
public String[] toColumns(String propertyName) throws QueryException {
// todo (PropertyMapping) : simple delegation (aka, easy to remove)
if ( "index".equals( propertyName ) ) {
if ( indexFragments == null ) {
String[] tmp = new String[indexColumnNames.length];

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.persister.collection;
import org.hibernate.MappingException;
import org.hibernate.Remove;
import org.hibernate.boot.Metadata;
import org.hibernate.persister.entity.AbstractPropertyMapping;
import org.hibernate.type.CompositeType;
@ -14,6 +15,8 @@ import org.hibernate.type.Type;
/**
* @author Gavin King
*/
@Deprecated( since = "6", forRemoval = true )
@Remove()
public class CompositeElementPropertyMapping extends AbstractPropertyMapping {
private final CompositeType compositeType;

View File

@ -7,12 +7,15 @@
package org.hibernate.persister.collection;
import org.hibernate.QueryException;
import org.hibernate.Remove;
import org.hibernate.persister.entity.PropertyMapping;
import org.hibernate.type.Type;
/**
* @author Gavin King
*/
@Deprecated( since = "6", forRemoval = true )
@Remove()
public class ElementPropertyMapping implements PropertyMapping {
private final String[] elementColumns;

View File

@ -40,6 +40,7 @@ import org.hibernate.LazyInitializationException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.QueryException;
import org.hibernate.Remove;
import org.hibernate.StaleObjectStateException;
@ -2131,6 +2132,7 @@ public abstract class AbstractEntityPersister
*/
@Override
public Type toType(String propertyName) throws QueryException {
// todo (PropertyMapping) : simple delegation (aka, easy to remove)
return propertyMapping.toType( propertyName );
}
@ -2159,29 +2161,31 @@ public abstract class AbstractEntityPersister
*/
@Override
public int getSubclassPropertyTableNumber(String propertyPath) {
String rootPropertyName = StringHelper.root( propertyPath );
Type type = propertyMapping.toType( rootPropertyName );
if ( type.isAssociationType() ) {
AssociationType assocType = (AssociationType) type;
if ( assocType.useLHSPrimaryKey() ) {
// performance op to avoid the array search
return 0;
}
else if ( type.isCollectionType() ) {
// properly handle property-ref-based associations
rootPropertyName = assocType.getLHSPropertyName();
}
}
//Enable for HHH-440, which we don't like:
/*if ( type.isComponentType() && !propertyName.equals(rootPropertyName) ) {
String unrooted = StringHelper.unroot(propertyName);
int idx = ArrayHelper.indexOf( getSubclassColumnClosure(), unrooted );
if ( idx != -1 ) {
return getSubclassColumnTableNumberClosure()[idx];
}
}*/
int index = ArrayHelper.indexOf( getSubclassPropertyNameClosure(), rootPropertyName ); //TODO: optimize this better!
return index == -1 ? 0 : getSubclassPropertyTableNumber( index );
// todo (PropertyMapping) : clean this up
throw new NotYetImplementedFor6Exception( getClass() );
// String rootPropertyName = StringHelper.root( propertyPath );
// Type type = propertyMapping.toType( rootPropertyName );
// if ( type.isAssociationType() ) {
// AssociationType assocType = (AssociationType) type;
// if ( assocType.useLHSPrimaryKey() ) {
// // performance op to avoid the array search
// return 0;
// }
// else if ( type.isCollectionType() ) {
// // properly handle property-ref-based associations
// rootPropertyName = assocType.getLHSPropertyName();
// }
// }
// //Enable for HHH-440, which we don't like:
// /*if ( type.isComponentType() && !propertyName.equals(rootPropertyName) ) {
// String unrooted = StringHelper.unroot(propertyName);
// int idx = ArrayHelper.indexOf( getSubclassColumnClosure(), unrooted );
// if ( idx != -1 ) {
// return getSubclassColumnTableNumberClosure()[idx];
// }
// }*/
// int index = ArrayHelper.indexOf( getSubclassPropertyNameClosure(), rootPropertyName ); //TODO: optimize this better!
// return index == -1 ? 0 : getSubclassPropertyTableNumber( index );
}
@Override
@ -3766,7 +3770,7 @@ public abstract class AbstractEntityPersister
@Override
public Boolean isTransient(Object entity, SharedSessionContractImplementor session) throws HibernateException {
final Object id = canExtractIdOutOfEntity() ? getIdentifier(entity, session) : null;
final Object id = getIdentifier(entity, session);
// we *always* assume an instance with a null
// identifier or no identifier property is unsaved!
if ( id == null ) {
@ -3869,13 +3873,6 @@ public abstract class AbstractEntityPersister
return entityMetamodel.isExplicitPolymorphism();
}
@Override
public boolean canExtractIdOutOfEntity() {
return hasIdentifierProperty()
|| entityMetamodel.getIdentifierProperty().isEmbedded()
|| entityMetamodel.getIdentifierProperty().hasIdentifierMapper();
}
@Override
public String[] getKeyColumnNames() {
return getIdentifierColumnNames();
@ -3913,6 +3910,7 @@ public abstract class AbstractEntityPersister
*/
@Override
public Type getPropertyType(String propertyName) throws MappingException {
// todo (PropertyMapping) : caller also deprecated (aka, easy to remove)
return propertyMapping.toType( propertyName );
}

View File

@ -13,6 +13,7 @@ import java.util.Set;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.Remove;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.engine.spi.Mapping;
@ -38,6 +39,8 @@ import org.hibernate.type.Type;
*
* @author Gavin King
*/
@Deprecated( since = "6", forRemoval = true )
@Remove()
public abstract class AbstractPropertyMapping implements PropertyMapping {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( AbstractPropertyMapping.class );

View File

@ -349,7 +349,10 @@ public interface EntityPersister
* @return The type.
* @throws MappingException Typically indicates an unknown
* property name.
*
* @deprecated See {@linkplain #findAttributeMapping(String)}
*/
@Deprecated( since = "6", forRemoval = true )
Type getPropertyType(String propertyName) throws MappingException;
/**
@ -392,9 +395,12 @@ public interface EntityPersister
* the id during session calls.
*
* @return True if either (1) {@link #hasIdentifierProperty()} or
* (2) the identifier is an embedded composite identifier; false otherwise.
* (2) the identifier is an embedded composite identifier; false otherwise.
*/
boolean canExtractIdOutOfEntity();
@Deprecated(since = "6")
default boolean canExtractIdOutOfEntity() {
return true;
}
/**
* Determine whether optimistic locking by column is enabled for this

View File

@ -27,9 +27,6 @@ import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.engine.internal.ManagedTypeHelper.asHibernateProxy;
import static org.hibernate.engine.internal.ManagedTypeHelper.isHibernateProxy;
/**
* Base for types which map associations to persistent entities.
*
@ -318,11 +315,7 @@ public abstract class EntityType extends AbstractType implements AssociationType
@Override
public int getHashCode(Object x, SessionFactoryImplementor factory) {
EntityPersister persister = getAssociatedEntityPersister( factory );
if ( !persister.canExtractIdOutOfEntity() ) {
return super.getHashCode( x );
}
final EntityPersister persister = getAssociatedEntityPersister( factory );
final Object id;
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( x );
if ( lazyInitializer != null ) {
@ -347,11 +340,7 @@ public abstract class EntityType extends AbstractType implements AssociationType
return x == y;
}
EntityPersister persister = getAssociatedEntityPersister( factory );
if ( !persister.canExtractIdOutOfEntity() ) {
return super.isEqual( x, y );
}
final EntityPersister persister = getAssociatedEntityPersister( factory );
final Class<?> mappedClass = persister.getMappedClass();
Object xid;
final LazyInitializer lazyInitializerX = HibernateProxy.extractLazyInitializer( x );

View File

@ -6,9 +6,6 @@
*/
package org.hibernate.envers.strategy.internal;
import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.MIDDLE_ENTITY_ALIAS;
import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.REVISION_PARAMETER;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
@ -36,12 +33,15 @@ import org.hibernate.envers.internal.entities.mapper.relation.MiddleComponentDat
import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.internal.revisioninfo.RevisionInfoNumberReader;
import org.hibernate.envers.internal.synchronization.SessionCacheCleaner;
import org.hibernate.envers.internal.tools.MutableInteger;
import org.hibernate.envers.internal.tools.query.Parameters;
import org.hibernate.envers.internal.tools.query.QueryBuilder;
import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.envers.strategy.spi.AuditStrategyContext;
import org.hibernate.envers.strategy.spi.MappingContext;
import org.hibernate.event.spi.EventSource;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.entity.UnionSubclassEntityPersister;
@ -54,6 +54,9 @@ import org.hibernate.type.MapType;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.MIDDLE_ENTITY_ALIAS;
import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.REVISION_PARAMETER;
/**
* An audit strategy implementation that persists and fetches audit information using a validity
* algorithm, based on the start-revision and end-revision of a row in the audit table schema.
@ -548,7 +551,6 @@ public class ValidityAuditStrategy implements AuditStrategy {
final Queryable revisionEntity = getQueryable( configuration.getRevisionInfo().getRevisionInfoEntityName(), session );
final Number revisionNumber = getRevisionNumber( configuration, revision );
final Type revisionNumberType = revisionEntity.getIdentifierType();
// The expected SQL is an update statement as follows:
// UPDATE audited_entity SET REVEND = ? [, REVEND_TSTMP = ?] WHERE (entity_id) = ? AND REV <> ? AND REVEND is null
@ -556,30 +558,34 @@ public class ValidityAuditStrategy implements AuditStrategy {
context.setTableName( getUpdateTableName( rootEntity, rootAuditEntity, auditEntity ) );
// Apply "SET REVEND = ?" portion of the SQL
final String revEndColumnName = configuration.getRevisionEndFieldName();
context.addColumn( rootAuditEntity.toColumns( revEndColumnName )[ 0 ] );
context.bind( revisionNumber, revisionNumberType );
final String revEndAttributeName = configuration.getRevisionEndFieldName();
final String revEndColumnName = rootAuditEntity.findAttributeMapping( revEndAttributeName )
.getSelectable( 0 )
.getSelectionExpression();
context.addColumn( revEndColumnName );
context.bind( revisionNumber, revisionEntity.getIdentifierMapping() );
if ( configuration.isRevisionEndTimestampEnabled() ) {
final String revEndTimestampColumnName = configuration.getRevisionEndTimestampFieldName();
final Type revEndTimestampType = rootAuditEntity.getPropertyType( revEndTimestampColumnName );
final Object revisionTimestamp = revisionTimestampGetter.get( revision );
final String revEndTimestampAttributeName = configuration.getRevisionEndTimestampFieldName();
final AttributeMapping revEndTimestampAttributeMapping = rootAuditEntity.findAttributeMapping( revEndTimestampAttributeName );
// Apply optional "[, REVEND_TSTMP = ?]" portion of the SQL
context.addColumn( rootAuditEntity.toColumns( revEndTimestampColumnName )[ 0 ] );
context.bind( getRevEndTimestampValue( configuration, revisionTimestamp ), revEndTimestampType );
context.addColumn( revEndTimestampAttributeMapping.getSelectable( 0 ).getSelectionExpression() );
context.bind( getRevEndTimestampValue( configuration, revisionTimestamp ), revEndTimestampAttributeMapping );
}
// Apply "WHERE (entity_id) = ?"
context.addPrimaryKeyColumns( rootEntity.getIdentifierColumnNames() );
context.bind( id, rootEntity.getIdentifierType() );
context.bind( id, rootEntity.getIdentifierMapping() );
// Apply "AND REV <> ?"
// todo (PropertyMapping) : need to be able to handle paths
final String path = configuration.getRevisionNumberPath();
context.addWhereColumn( rootAuditEntity.toColumns( path )[ 0 ], " <> ?" );
context.bind( revisionNumber, rootAuditEntity.getPropertyType( path ) );
// Apply "AND REVEND is null"
context.addWhereColumn( auditEntity.toColumns( revEndColumnName )[ 0 ], " is null" );
context.addWhereColumn( revEndColumnName, " is null" );
return context;
}
@ -607,8 +613,6 @@ public class ValidityAuditStrategy implements AuditStrategy {
final Queryable entity = getQueryable( entityName, session );
final Queryable auditEntity = getQueryable( auditEntityName, session );
final String revEndTimestampColumnName = configuration.getRevisionEndTimestampFieldName();
final Type revEndTimestampType = auditEntity.getPropertyType( revEndTimestampColumnName );
// The expected SQL is an update statement as follows:
// UPDATE audited_entity SET REVEND_TSTMP = ? WHERE (entity_id) = ? AND REV <> ? AND REVEND_TSMTP is null
@ -617,8 +621,11 @@ public class ValidityAuditStrategy implements AuditStrategy {
// Apply "SET REVEND_TSTMP = ?" portion of the SQL
final Object revisionTimestamp = revisionTimestampGetter.get( revision );
context.addColumn( auditEntity.toColumns( revEndTimestampColumnName )[ 0 ] );
context.bind( getRevEndTimestampValue( configuration, revisionTimestamp ), revEndTimestampType );
final String revEndTimestampAttributeName = configuration.getRevisionEndTimestampFieldName();
final AttributeMapping revEndTimestampAttributeMapping = auditEntity.findAttributeMapping( revEndTimestampAttributeName );
final String revEndTimestampColumnName = revEndTimestampAttributeMapping.getSelectable( 0 ).getSelectionExpression();
context.addColumn( revEndTimestampColumnName );
context.bind( getRevEndTimestampValue( configuration, revisionTimestamp ), revEndTimestampAttributeMapping );
// Apply "WHERE (entity_id) = ? AND REV <> ?" portion of the SQL
final Number revisionNumber = getRevisionNumber( configuration, revision );
@ -628,11 +635,12 @@ public class ValidityAuditStrategy implements AuditStrategy {
context.bind( id, entity.getIdentifierType() );
// Apply "AND REV <> ?"
// todo (PropertyMapping) : need to be able to handle paths
context.addWhereColumn( configuration.getRevisionFieldName(), " <> ?" );
context.bind( revisionNumber, auditEntity.getPropertyType( configuration.getRevisionNumberPath() ) );
// Apply "AND REVEND_TSTMP is null"
context.addWhereColumn( auditEntity.toColumns( revEndTimestampColumnName )[ 0 ], " is null" );
context.addWhereColumn( revEndTimestampColumnName, " is null" );
return context;
}
@ -669,15 +677,23 @@ public class ValidityAuditStrategy implements AuditStrategy {
}
public void bind(Object value, Type type) {
bindings.add( new QueryParameterBinding( value, type ) );
bindings.add( new QueryParameterBindingType( value, type ) );
}
public void bind(Object value, ModelPart part) {
bindings.add( new QueryParameterBindingPart( value, part ) );
}
}
private static class QueryParameterBinding {
private interface QueryParameterBinding {
int bind(int index, PreparedStatement statement, SessionImplementor session) throws SQLException;
}
private static class QueryParameterBindingType implements QueryParameterBinding {
private final Type type;
private final Object value;
public QueryParameterBinding(Object value, Type type) {
public QueryParameterBindingType(Object value, Type type) {
this.type = type;
this.value = value;
}
@ -687,4 +703,49 @@ public class ValidityAuditStrategy implements AuditStrategy {
return type.getColumnSpan( session.getSessionFactory() );
}
}
private static class QueryParameterBindingPart implements QueryParameterBinding {
private final ModelPart modelPart;
private final Object value;
public QueryParameterBindingPart(Object value, ModelPart modelPart) {
this.value = value;
this.modelPart = modelPart;
}
@Override
public int bind(
int index,
PreparedStatement statement,
SessionImplementor session) {
final MutableInteger position = new MutableInteger( index );
modelPart.breakDownJdbcValues(
value,
(jdbcValue, jdbcValueMapping) -> {
try {
//noinspection unchecked
jdbcValueMapping.getJdbcMapping().getJdbcValueBinder().bind(
statement,
jdbcValue,
position.getAndIncrease(),
session
);
}
catch (SQLException e) {
throw session.getJdbcServices().getSqlExceptionHelper().convert(
e,
String.format(
Locale.ROOT,
"Error binding JDBC value relative to `%s`",
modelPart.getNavigableRole().getFullPath()
)
);
}
},
session
);
return modelPart.getJdbcTypeCount();
}
}
}