From 3e6fcdeda3a8d04f249e4bbe0c12cbda29c0d8e3 Mon Sep 17 00:00:00 2001 From: Gavin Date: Tue, 29 Nov 2022 10:10:39 +0100 Subject: [PATCH] unify new ValueGenerationStrategy interfaces with existing ValueGeneration stuff - introduce ValueGenerationStrategy to abstract over IMVGS and IDVGS - make ValueGeneration a mixin of IMVGS with IDVGS - make IDVGS accept a Dialect for SQL fragment generation (it was based on an obsolete version of ValueGeneration) - adapt all the code which only handled single-column value generation to handle multiple columns, as introduced by IDVGS Still to do: the whole AnnotationValueGeneration stuff still requires the use of the mixin interface, and you can't use IMVGS or IDVGS directly. That bit is going to require a bit more thinking about backward compatibility. --- .../cfg/annotations/PropertyBinder.java | 16 ++-- .../java/org/hibernate/mapping/Property.java | 8 +- .../metamodel/mapping/AttributeMapping.java | 4 +- .../mapping/GeneratedValueResolver.java | 8 +- .../AbstractSingularAttributeMapping.java | 10 +-- .../internal/BasicAttributeMapping.java | 6 +- .../internal/EmbeddedAttributeMapping.java | 6 +- .../internal/MappingModelCreationHelper.java | 4 +- .../VirtualEmbeddedAttributeMapping.java | 8 +- .../entity/AbstractEntityPersister.java | 25 +++--- .../mutation/AbstractMutationCoordinator.java | 23 +++++- .../entity/mutation/InsertCoordinator.java | 52 +++++------- .../mutation/UpdateCoordinatorStandard.java | 55 +++++-------- .../sql/ast/spi/SqlExpressionResolver.java | 1 - .../ColumnValuesTableMutationBuilder.java | 50 ++++++++++++ .../RestrictedTableMutationBuilder.java | 6 +- .../model/ast/builder/TableDeleteBuilder.java | 2 + .../model/ast/builder/TableInsertBuilder.java | 36 +-------- .../model/ast/builder/TableUpdateBuilder.java | 37 +-------- .../tuple/AbstractNonIdentifierAttribute.java | 2 +- .../tuple/BaselineAttributeInformation.java | 10 +-- .../InDatabaseValueGenerationStrategy.java | 66 +++++++++++----- .../InMemoryValueGenerationStrategy.java | 24 +++--- .../tuple/NonIdentifierAttribute.java | 2 +- .../org/hibernate/tuple/PropertyFactory.java | 8 -- .../org/hibernate/tuple/StandardProperty.java | 2 +- .../org/hibernate/tuple/ValueGeneration.java | 57 ++++--------- .../tuple/ValueGenerationStrategy.java | 52 ++++++++++++ .../tuple/entity/EntityMetamodel.java | 79 +++++++------------ .../org/hibernate/type/ComponentType.java | 5 +- .../metadata/AuditMetadataGenerator.java | 5 +- 31 files changed, 341 insertions(+), 328 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/ColumnValuesTableMutationBuilder.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/ValueGenerationStrategy.java diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java index 941295d737..f1f413e4b0 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java @@ -40,8 +40,10 @@ import org.hibernate.mapping.ToOne; import org.hibernate.mapping.Value; import org.hibernate.metamodel.spi.EmbeddableInstantiator; import org.hibernate.property.access.spi.PropertyAccessStrategy; +import org.hibernate.tuple.ValueGenerationStrategy; import org.hibernate.tuple.AnnotationValueGeneration; import org.hibernate.tuple.AttributeBinder; +import org.hibernate.tuple.InDatabaseValueGenerationStrategy; import org.hibernate.tuple.GenerationTiming; import org.hibernate.tuple.ValueGeneration; import org.hibernate.tuple.ValueGenerator; @@ -383,19 +385,17 @@ public class PropertyBinder { } } - private ValueGeneration determineValueGenerationStrategy(XProperty property) { - ValueGeneration valueGeneration = getValueGenerationFromAnnotations( property ); - + private ValueGenerationStrategy determineValueGenerationStrategy(XProperty property) { + ValueGenerationStrategy valueGeneration = getValueGenerationFromAnnotations( property ); if ( valueGeneration == null ) { return NoValueGeneration.INSTANCE; } - - if ( !valueGeneration.writePropertyValue() ) { + if ( valueGeneration instanceof InDatabaseValueGenerationStrategy) { // if we have an in-db generator, mark it as not insertable nor updatable - insertable = false; - updatable = false; + final boolean writable = ( (InDatabaseValueGenerationStrategy) valueGeneration ).writePropertyValue(); + insertable = writable; + updatable = writable; } - return valueGeneration; } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Property.java b/hibernate-core/src/main/java/org/hibernate/mapping/Property.java index 459b6fe1b2..72d179201a 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Property.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Property.java @@ -27,7 +27,7 @@ import org.hibernate.property.access.spi.PropertyAccessStrategy; import org.hibernate.property.access.spi.PropertyAccessStrategyResolver; import org.hibernate.property.access.spi.Setter; import org.hibernate.service.ServiceRegistry; -import org.hibernate.tuple.ValueGeneration; +import org.hibernate.tuple.ValueGenerationStrategy; import org.hibernate.type.CompositeType; import org.hibernate.type.Type; @@ -45,7 +45,7 @@ public class Property implements Serializable, MetaAttributable { private boolean insertable = true; private boolean selectable = true; private boolean optimisticLocked = true; - private ValueGeneration valueGenerationStrategy; + private ValueGenerationStrategy valueGenerationStrategy; private String propertyAccessorName; private PropertyAccessStrategy propertyAccessStrategy; private boolean lazy; @@ -216,11 +216,11 @@ public class Property implements Serializable, MetaAttributable { return insertable && value.hasAnyInsertableColumns(); } - public ValueGeneration getValueGenerationStrategy() { + public ValueGenerationStrategy getValueGenerationStrategy() { return valueGenerationStrategy; } - public void setValueGenerationStrategy(ValueGeneration valueGenerationStrategy) { + public void setValueGenerationStrategy(ValueGenerationStrategy valueGenerationStrategy) { this.valueGenerationStrategy = valueGenerationStrategy; } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/AttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/AttributeMapping.java index 298997124c..7148a4b471 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/AttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/AttributeMapping.java @@ -9,7 +9,7 @@ package org.hibernate.metamodel.mapping; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.sql.results.graph.DatabaseSnapshotContributor; import org.hibernate.sql.results.graph.Fetchable; -import org.hibernate.tuple.ValueGeneration; +import org.hibernate.tuple.ValueGenerationStrategy; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.MutabilityPlan; import org.hibernate.type.descriptor.java.MutabilityPlanExposer; @@ -70,7 +70,7 @@ public interface AttributeMapping * * @apiNote Only relevant for non-id attributes */ - ValueGeneration getValueGeneration(); + ValueGenerationStrategy getValueGeneration(); @Override default EntityMappingType findContainingEntityMapping() { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/GeneratedValueResolver.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/GeneratedValueResolver.java index 0d3b17bdb4..9892e25946 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/GeneratedValueResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/GeneratedValueResolver.java @@ -9,8 +9,9 @@ package org.hibernate.metamodel.mapping; import org.hibernate.Incubating; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.internal.NoGeneratedValueResolver; +import org.hibernate.tuple.ValueGenerationStrategy; import org.hibernate.tuple.GenerationTiming; -import org.hibernate.tuple.ValueGeneration; +import org.hibernate.tuple.InMemoryValueGenerationStrategy; /** * Generalized contract covering an attribute's generation handling @@ -20,7 +21,7 @@ import org.hibernate.tuple.ValueGeneration; @Incubating public interface GeneratedValueResolver { static GeneratedValueResolver from( - ValueGeneration valueGeneration, + ValueGenerationStrategy valueGeneration, GenerationTiming requestedTiming, int dbSelectionPosition) { assert requestedTiming != GenerationTiming.NEVER; @@ -38,7 +39,8 @@ public interface GeneratedValueResolver { return new InDatabaseGeneratedValueResolver( requestedTiming, dbSelectionPosition ); } else { - return new InMemoryGeneratedValueResolver( valueGeneration.getValueGenerator(), requestedTiming ); + InMemoryValueGenerationStrategy generation = (InMemoryValueGenerationStrategy) valueGeneration; + return new InMemoryGeneratedValueResolver( generation.getValueGenerator(), requestedTiming ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractSingularAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractSingularAttributeMapping.java index 28a5f9b3e3..ddea1449f2 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractSingularAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractSingularAttributeMapping.java @@ -13,7 +13,7 @@ import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.sql.results.graph.FetchOptions; -import org.hibernate.tuple.ValueGeneration; +import org.hibernate.tuple.ValueGenerationStrategy; /** * @author Steve Ebersole @@ -23,7 +23,7 @@ public abstract class AbstractSingularAttributeMapping implements SingularAttributeMapping { private final PropertyAccess propertyAccess; - private final ValueGeneration valueGeneration; + private final ValueGenerationStrategy valueGeneration; public AbstractSingularAttributeMapping( String name, @@ -32,7 +32,7 @@ public abstract class AbstractSingularAttributeMapping FetchOptions mappedFetchOptions, ManagedMappingType declaringType, PropertyAccess propertyAccess, - ValueGeneration valueGeneration) { + ValueGenerationStrategy valueGeneration) { super( name, attributeMetadataAccess, mappedFetchOptions, stateArrayPosition, declaringType ); this.propertyAccess = propertyAccess; this.valueGeneration = valueGeneration != null @@ -48,7 +48,7 @@ public abstract class AbstractSingularAttributeMapping FetchStyle fetchStyle, ManagedMappingType declaringType, PropertyAccess propertyAccess, - ValueGeneration valueGeneration) { + ValueGenerationStrategy valueGeneration) { super( name, attributeMetadataAccess, fetchTiming, fetchStyle, stateArrayPosition, declaringType ); this.propertyAccess = propertyAccess; this.valueGeneration = valueGeneration != null @@ -62,7 +62,7 @@ public abstract class AbstractSingularAttributeMapping } @Override - public ValueGeneration getValueGeneration() { + public ValueGenerationStrategy getValueGeneration() { return valueGeneration; } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java index 79d4314d3f..beb1c654a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java @@ -36,7 +36,7 @@ import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.graph.basic.BasicFetch; import org.hibernate.sql.results.graph.basic.BasicResult; -import org.hibernate.tuple.ValueGeneration; +import org.hibernate.tuple.ValueGenerationStrategy; import org.hibernate.type.descriptor.java.JavaType; /** @@ -87,7 +87,7 @@ public class BasicAttributeMapping JdbcMapping jdbcMapping, ManagedMappingType declaringType, PropertyAccess propertyAccess, - ValueGeneration valueGeneration) { + ValueGenerationStrategy valueGeneration) { super( attributeName, stateArrayPosition, @@ -126,7 +126,7 @@ public class BasicAttributeMapping ManagedMappingType declaringType, BasicValuedModelPart original, PropertyAccess propertyAccess, - ValueGeneration valueGeneration, + ValueGenerationStrategy valueGeneration, boolean insertable, boolean updateable, SelectableMapping selectableMapping) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java index 38238ddcb9..169cba9973 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java @@ -54,7 +54,7 @@ import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable; import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl; import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl; -import org.hibernate.tuple.ValueGeneration; +import org.hibernate.tuple.ValueGenerationStrategy; /** * @author Steve Ebersole @@ -80,7 +80,7 @@ public class EmbeddedAttributeMapping EmbeddableMappingType embeddableMappingType, ManagedMappingType declaringType, PropertyAccess propertyAccess, - ValueGeneration valueGeneration) { + ValueGenerationStrategy valueGeneration) { this( name, navigableRole, @@ -109,7 +109,7 @@ public class EmbeddedAttributeMapping EmbeddableMappingType embeddableMappingType, ManagedMappingType declaringType, PropertyAccess propertyAccess, - ValueGeneration valueGeneration) { + ValueGenerationStrategy valueGeneration) { super( name, stateArrayPosition, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java index 462272af2e..d9e125aeed 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java @@ -88,7 +88,6 @@ import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroupProducer; -import org.hibernate.tuple.ValueGeneration; import org.hibernate.type.AssociationType; import org.hibernate.type.BasicType; import org.hibernate.type.ComponentType; @@ -266,7 +265,6 @@ public class MappingModelCreationHelper { fetchTiming = bootProperty.isLazy() ? FetchTiming.DELAYED : FetchTiming.IMMEDIATE; fetchStyle = bootProperty.isLazy() ? FetchStyle.SELECT : FetchStyle.JOIN; } - final ValueGeneration valueGeneration = bootProperty.getValueGenerationStrategy(); return new BasicAttributeMapping( attrName, @@ -290,7 +288,7 @@ public class MappingModelCreationHelper { attrType, declaringType, propertyAccess, - valueGeneration + bootProperty.getValueGenerationStrategy() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/VirtualEmbeddedAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/VirtualEmbeddedAttributeMapping.java index 4a9956070a..896eae7107 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/VirtualEmbeddedAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/VirtualEmbeddedAttributeMapping.java @@ -17,7 +17,7 @@ import org.hibernate.metamodel.mapping.VirtualModelPart; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.sql.ast.tree.from.TableGroupProducer; -import org.hibernate.tuple.ValueGeneration; +import org.hibernate.tuple.ValueGenerationStrategy; /** * @author Christian Beikov @@ -35,7 +35,8 @@ public class VirtualEmbeddedAttributeMapping extends EmbeddedAttributeMapping im FetchStyle mappedFetchStyle, EmbeddableMappingType embeddableMappingType, ManagedMappingType declaringType, - PropertyAccess propertyAccess, ValueGeneration valueGeneration) { + PropertyAccess propertyAccess, + ValueGenerationStrategy valueGeneration) { super( name, navigableRole, @@ -63,7 +64,8 @@ public class VirtualEmbeddedAttributeMapping extends EmbeddedAttributeMapping im FetchStyle mappedFetchStyle, EmbeddableMappingType embeddableMappingType, ManagedMappingType declaringType, - PropertyAccess propertyAccess, ValueGeneration valueGeneration) { + PropertyAccess propertyAccess, + ValueGenerationStrategy valueGeneration) { super( name, navigableRole, diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index d3a18ccef2..5a7a11522b 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -256,9 +256,10 @@ import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode; import org.hibernate.sql.results.graph.entity.internal.EntityResultImpl; import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.stat.spi.StatisticsImplementor; +import org.hibernate.tuple.ValueGenerationStrategy; +import org.hibernate.tuple.InDatabaseValueGenerationStrategy; import org.hibernate.tuple.GenerationTiming; import org.hibernate.tuple.NonIdentifierAttribute; -import org.hibernate.tuple.ValueGeneration; import org.hibernate.tuple.entity.EntityBasedAssociationAttribute; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.type.AnyType; @@ -2758,17 +2759,19 @@ public abstract class AbstractEntityPersister hasColumns = true; } else { - final ValueGeneration valueGeneration = attributeMapping.getValueGeneration(); + final ValueGenerationStrategy valueGeneration = attributeMapping.getValueGeneration(); if ( valueGeneration.getGenerationTiming().includesUpdate() - && valueGeneration.generatedByDatabase() - && valueGeneration.referenceColumnInSql() ) { - final Dialect dialect = getFactory().getJdbcServices().getDialect(); - update.addColumns( - getPropertyColumnNames( index ), - SINGLE_TRUE, - new String[] { valueGeneration.getDatabaseGeneratedReferencedColumnValue(dialect) } - ); - hasColumns = true; + && valueGeneration.generatedByDatabase() ) { + final InDatabaseValueGenerationStrategy generation = (InDatabaseValueGenerationStrategy) valueGeneration; + if ( generation.referenceColumnsInSql() ) { + final Dialect dialect = getFactory().getJdbcServices().getDialect(); + update.addColumns( + getPropertyColumnNames(index), + SINGLE_TRUE, + generation.getReferencedColumnValues(dialect) + ); + hasColumns = true; + } } } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/AbstractMutationCoordinator.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/AbstractMutationCoordinator.java index ebc1fbc283..a213568dcd 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/AbstractMutationCoordinator.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/AbstractMutationCoordinator.java @@ -9,17 +9,22 @@ package org.hibernate.persister.entity.mutation; import java.util.List; import org.hibernate.Internal; +import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.util.collections.CollectionHelper; +import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.sql.model.ModelMutationLogging; import org.hibernate.sql.model.MutationOperation; import org.hibernate.sql.model.MutationOperationGroup; import org.hibernate.sql.model.ValuesAnalysis; import org.hibernate.sql.model.ast.MutationGroup; +import org.hibernate.sql.model.ast.builder.ColumnValuesTableMutationBuilder; +import org.hibernate.sql.model.ast.builder.MutationGroupBuilder; import org.hibernate.sql.model.internal.MutationOperationGroupNone; import org.hibernate.sql.model.internal.MutationOperationGroupSingle; import org.hibernate.sql.model.internal.MutationOperationGroupStandard; +import org.hibernate.tuple.InDatabaseValueGenerationStrategy; /** * Base support for coordinating mutations against an entity @@ -84,5 +89,21 @@ public abstract class AbstractMutationCoordinator { ); } - + void handleValueGeneration( + AttributeMapping attributeMapping, + MutationGroupBuilder mutationGroupBuilder, + InDatabaseValueGenerationStrategy valueGeneration) { + final Dialect dialect = factory.getJdbcServices().getDialect(); + final boolean writePropertyValue = valueGeneration.writePropertyValue(); + final String[] columnValues = writePropertyValue ? null : valueGeneration.getReferencedColumnValues( dialect ); + attributeMapping.forEachSelectable( (j, mapping) -> { + final String tableName = entityPersister.physicalTableNameForMutation( mapping ); + final ColumnValuesTableMutationBuilder tableUpdateBuilder = mutationGroupBuilder.findTableDetailsBuilder( tableName ); + tableUpdateBuilder.addValueColumn( + mapping.getSelectionExpression(), + writePropertyValue ? "?" : columnValues[j], + mapping.getJdbcMapping() + ); + } ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/InsertCoordinator.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/InsertCoordinator.java index a4d7039aa8..6cb50d4ccd 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/InsertCoordinator.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/InsertCoordinator.java @@ -11,7 +11,6 @@ import java.util.List; import org.hibernate.Internal; import org.hibernate.Session; -import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.batch.internal.BasicBatchKey; import org.hibernate.engine.jdbc.mutation.JdbcValueBindings; import org.hibernate.engine.jdbc.mutation.MutationExecutor; @@ -24,7 +23,6 @@ import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping; -import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.sql.model.MutationOperationGroup; import org.hibernate.sql.model.MutationType; @@ -33,8 +31,9 @@ import org.hibernate.sql.model.ValuesAnalysis; import org.hibernate.sql.model.ast.builder.MutationGroupBuilder; import org.hibernate.sql.model.ast.builder.TableInsertBuilder; import org.hibernate.sql.model.ast.builder.TableInsertBuilderStandard; +import org.hibernate.tuple.ValueGenerationStrategy; +import org.hibernate.tuple.InDatabaseValueGenerationStrategy; import org.hibernate.tuple.InMemoryValueGenerationStrategy; -import org.hibernate.tuple.ValueGeneration; import org.hibernate.tuple.entity.EntityMetamodel; /** @@ -135,14 +134,7 @@ public class InsertCoordinator extends AbstractMutationCoordinator { private Object doStaticInserts(Object id, Object[] values, Object object, SharedSessionContractImplementor session) { final InsertValuesAnalysis insertValuesAnalysis = new InsertValuesAnalysis( entityPersister(), values ); - final TableInclusionChecker tableInclusionChecker = (tableMapping) -> { - if ( tableMapping.isOptional() ) { - return insertValuesAnalysis.hasNonNullBindings( tableMapping ); - } - - return true; - }; - + final TableInclusionChecker tableInclusionChecker = getTableInclusionChecker( insertValuesAnalysis ); final MutationExecutorService mutationExecutorService = session.getSessionFactory() .getServiceRegistry() @@ -278,13 +270,7 @@ public class InsertCoordinator extends AbstractMutationCoordinator { final InsertValuesAnalysis insertValuesAnalysis = new InsertValuesAnalysis( entityPersister(), values ); - final TableInclusionChecker tableInclusionChecker = (tableMapping) -> { - if ( tableMapping.isOptional() ) { - return insertValuesAnalysis.hasNonNullBindings( tableMapping ); - } - - return true; - }; + final TableInclusionChecker tableInclusionChecker = getTableInclusionChecker( insertValuesAnalysis ); decomposeForInsert( mutationExecutor, id, values, insertGroup, insertability, tableInclusionChecker, session ); @@ -310,6 +296,10 @@ public class InsertCoordinator extends AbstractMutationCoordinator { } } + private static TableInclusionChecker getTableInclusionChecker(InsertValuesAnalysis insertValuesAnalysis) { + return (tableMapping) -> !tableMapping.isOptional() || insertValuesAnalysis.hasNonNullBindings( tableMapping ); + } + /** * Transform the array of property indexes to an array of booleans, @@ -387,8 +377,6 @@ public class InsertCoordinator extends AbstractMutationCoordinator { MutationGroupBuilder insertGroupBuilder, boolean[] attributeInclusions) { final List attributeMappings = entityPersister().getAttributeMappings(); - //noinspection resource - final Dialect dialect = factory().getJdbcServices().getDialect(); insertGroupBuilder.forEachTableMutationBuilder( (builder) -> { final EntityTableMapping tableMapping = (EntityTableMapping) builder.getMutatingTable().getTableMapping(); @@ -402,18 +390,12 @@ public class InsertCoordinator extends AbstractMutationCoordinator { final AttributeMapping attributeMapping = attributeMappings.get( attributeIndex ); if ( !attributeInclusions[ attributeIndex ] ) { - final ValueGeneration valueGeneration = attributeMapping.getValueGeneration(); - if ( valueGeneration.getGenerationTiming().includesInsert() - && valueGeneration.getValueGenerator() == null - && valueGeneration.referenceColumnInSql() ) { - // value-generation is only valid for basic attributes - final BasicAttributeMapping basicAttributeMapping = (BasicAttributeMapping) attributeMapping; - final String tableNameForMutation = entityPersister().physicalTableNameForMutation( basicAttributeMapping ); - final TableInsertBuilder tableInsertBuilder = insertGroupBuilder.findTableDetailsBuilder( tableNameForMutation ); - tableInsertBuilder.addValueColumn( - basicAttributeMapping.getSelectionExpression(), - valueGeneration.getDatabaseGeneratedReferencedColumnValue( dialect ), - basicAttributeMapping.getJdbcMapping() + final ValueGenerationStrategy valueGeneration = attributeMapping.getValueGeneration(); + if ( isValueGenerationInSql( valueGeneration ) ) { + handleValueGeneration( + attributeMapping, + insertGroupBuilder, + (InDatabaseValueGenerationStrategy) valueGeneration ); } continue; @@ -454,4 +436,10 @@ public class InsertCoordinator extends AbstractMutationCoordinator { } } ); } + + private static boolean isValueGenerationInSql(ValueGenerationStrategy valueGeneration) { + return valueGeneration.getGenerationTiming().includesInsert() + && valueGeneration.generatedByDatabase() + && ( (InDatabaseValueGenerationStrategy) valueGeneration ).referenceColumnsInSql(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java index 6924c4bb7a..da6a81aafa 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java @@ -14,7 +14,6 @@ import java.util.Set; import org.hibernate.Internal; import org.hibernate.Session; -import org.hibernate.dialect.Dialect; import org.hibernate.engine.OptimisticLockStyle; import org.hibernate.engine.jdbc.batch.internal.BasicBatchKey; import org.hibernate.engine.jdbc.batch.spi.BatchKey; @@ -35,7 +34,6 @@ import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.EntityRowIdMapping; import org.hibernate.metamodel.mapping.EntityVersionMapping; import org.hibernate.metamodel.mapping.SingularAttributeMapping; -import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; @@ -51,8 +49,9 @@ import org.hibernate.sql.model.ast.builder.TableUpdateBuilderSkipped; import org.hibernate.sql.model.ast.builder.TableUpdateBuilderStandard; import org.hibernate.sql.model.internal.MutationOperationGroupSingle; import org.hibernate.sql.model.jdbc.JdbcMutationOperation; +import org.hibernate.tuple.ValueGenerationStrategy; +import org.hibernate.tuple.InDatabaseValueGenerationStrategy; import org.hibernate.tuple.InMemoryValueGenerationStrategy; -import org.hibernate.tuple.ValueGeneration; import org.hibernate.tuple.entity.EntityMetamodel; import static org.hibernate.engine.OptimisticLockStyle.ALL; @@ -213,10 +212,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple } final InclusionChecker updateabilityChecker = (position, attribute) -> { - final ValueGeneration valueGeneration = attribute.getValueGeneration(); - if ( valueGeneration.getGenerationTiming().includesUpdate() - && valueGeneration.getValueGenerator() == null - && valueGeneration.referenceColumnInSql() ) { + if ( isValueGenerationInSql( attribute.getValueGeneration() ) ) { return true; } @@ -253,8 +249,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple if ( optimisticLockStyle == VERSION ) { return versionMapping != null - && versionMapping.getVersionAttribute() == attribute - ; + && versionMapping.getVersionAttribute() == attribute; // && updateableAttributeIndexes[position]; } @@ -313,6 +308,12 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple } } + private static boolean isValueGenerationInSql(ValueGenerationStrategy valueGeneration) { + return valueGeneration.getGenerationTiming().includesUpdate() + && valueGeneration.generatedByDatabase() + && ( (InDatabaseValueGenerationStrategy) valueGeneration ).referenceColumnsInSql(); + } + /** * Which properties appear in the SQL update? * (Initialized, updateable ones!) @@ -657,16 +658,14 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple } final IncludedAttributeAnalysis attributeAnalysis = (IncludedAttributeAnalysis) attributeAnalysisRef; - final ValueGeneration valueGeneration = attributeMapping.getValueGeneration(); if ( attributeAnalysis.includeInSet() ) { // apply the new values final boolean includeInSet; - if ( valueGeneration.getGenerationTiming().includesUpdate() - && valueGeneration.getValueGenerator() == null - && valueGeneration.referenceColumnInSql() - && valueGeneration.getDatabaseGeneratedReferencedColumnValue() != null ) { + final ValueGenerationStrategy valueGeneration = attributeMapping.getValueGeneration(); + if ( isValueGenerationInSql( valueGeneration ) + && !( (InDatabaseValueGenerationStrategy) valueGeneration ).writePropertyValue() ) { // we applied `#getDatabaseGeneratedReferencedColumnValue` earlier includeInSet = false; } @@ -881,7 +880,6 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple final List attributeMappings = entityPersister().getAttributeMappings(); final boolean[] versionability = entityPersister().getPropertyVersionability(); final OptimisticLockStyle optimisticLockStyle = entityPersister().optimisticLockStyle(); - final Dialect dialect = factory().getJdbcServices().getDialect(); updateGroupBuilder.forEachTableMutationBuilder( (builder) -> { final EntityTableMapping tableMapping = (EntityTableMapping) builder.getMutatingTable().getTableMapping(); @@ -898,21 +896,12 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple if ( attributeAnalysis.includeInSet() ) { assert updateValuesAnalysis.tablesNeedingUpdate.contains( tableMapping ); - final ValueGeneration valueGeneration = attributeMapping.getValueGeneration(); - if ( valueGeneration.getGenerationTiming().includesUpdate() - && valueGeneration.getValueGenerator() == null - && valueGeneration.referenceColumnInSql() ) { - // value-generation is only valid for basic attributes - final BasicAttributeMapping basicAttributeMapping = (BasicAttributeMapping) attributeMapping; - final String databaseGeneratedValue = valueGeneration.getDatabaseGeneratedReferencedColumnValue( - dialect - ); - tableUpdateBuilder.addValueColumn( - basicAttributeMapping.getSelectionExpression(), - databaseGeneratedValue == null - ? "?" - : databaseGeneratedValue, - basicAttributeMapping.getJdbcMapping() + final ValueGenerationStrategy valueGeneration = attributeMapping.getValueGeneration(); + if ( isValueGenerationInSql( valueGeneration ) ) { + handleValueGeneration( + attributeMapping, + updateGroupBuilder, + (InDatabaseValueGenerationStrategy) valueGeneration ); } else if ( versionMapping != null @@ -1014,7 +1003,6 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple } ); } - /** * Contains the aggregated analysis of the update values to determine * what SQL UPDATE statement(s) should be used to update the entity @@ -1344,10 +1332,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple null, null, (index,attribute) -> { - final ValueGeneration valueGeneration = attribute.getValueGeneration(); - if ( valueGeneration.getGenerationTiming().includesUpdate() - && valueGeneration.getValueGenerator() == null - && valueGeneration.referenceColumnInSql() ) { + if ( isValueGenerationInSql( attribute.getValueGeneration() ) ) { return true; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlExpressionResolver.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlExpressionResolver.java index 57ad73b573..fac0d6add8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlExpressionResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlExpressionResolver.java @@ -6,7 +6,6 @@ */ package org.hibernate.sql.ast.spi; -import java.util.Objects; import java.util.function.Function; import org.hibernate.metamodel.mapping.SelectableMapping; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/ColumnValuesTableMutationBuilder.java b/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/ColumnValuesTableMutationBuilder.java new file mode 100644 index 0000000000..b7d73db018 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/ColumnValuesTableMutationBuilder.java @@ -0,0 +1,50 @@ +/* + * 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.sql.model.ast.builder; + +import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.metamodel.mapping.SelectableMapping; + +/** + * Common operations of {@link TableUpdateBuilder} and {@link TableInsertBuilder}. + * + * @author Steve Ebersole + * @author Gavin King + */ +public interface ColumnValuesTableMutationBuilder { + /** + * Add a column as part of the values list + */ + void addValueColumn(String columnName, String columnWriteFragment, JdbcMapping jdbcMapping); + + /** + * Add a column as part of the values list + */ + default void addValueColumn(SelectableMapping selectableMapping) { + addValueColumn( + selectableMapping.getSelectionExpression(), + selectableMapping.getWriteExpression(), + selectableMapping.getJdbcMapping() + ); + } + + /** + * Add a key column + */ + void addKeyColumn(String columnName, String valueExpression, JdbcMapping jdbcMapping); + + /** + * Add a key column + */ + default void addKeyColumn(SelectableMapping selectableMapping) { + addKeyColumn( + selectableMapping.getSelectionExpression(), + selectableMapping.getWriteExpression(), + selectableMapping.getJdbcMapping() + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/RestrictedTableMutationBuilder.java b/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/RestrictedTableMutationBuilder.java index e1face0217..9512e8adcb 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/RestrictedTableMutationBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/RestrictedTableMutationBuilder.java @@ -13,8 +13,10 @@ import org.hibernate.sql.model.MutationOperation; import org.hibernate.sql.model.ast.RestrictedTableMutation; /** - * Specialized TableMutationBuilder implementation for building mutations - * which define a where-clause + * Specialized {@link TableMutationBuilder} implementation for building mutations + * which have a {@code where} clause. + * + * Common operations of {@link TableUpdateBuilder} and {@link TableDeleteBuilder}. * * @author Steve Ebersole */ diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/TableDeleteBuilder.java b/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/TableDeleteBuilder.java index a110c6c71d..00d522dee9 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/TableDeleteBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/TableDeleteBuilder.java @@ -10,6 +10,8 @@ import org.hibernate.sql.model.ast.TableDelete; import org.hibernate.sql.model.jdbc.JdbcDeleteMutation; /** + * {@link TableMutationBuilder} implementation for {@code delete} statements. + * * @author Steve Ebersole */ public interface TableDeleteBuilder extends RestrictedTableMutationBuilder { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/TableInsertBuilder.java b/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/TableInsertBuilder.java index 69a9b09c1c..f8f905c7a4 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/TableInsertBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/TableInsertBuilder.java @@ -6,45 +6,13 @@ */ package org.hibernate.sql.model.ast.builder; -import org.hibernate.metamodel.mapping.JdbcMapping; -import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.sql.model.ast.TableInsert; /** - * TableMutationBuilder implementation for insert statements + * {@link TableMutationBuilder} implementation for {@code insert} statements. * * @author Steve Ebersole */ -public interface TableInsertBuilder extends TableMutationBuilder { - /** - * Add a column as part of the values list - */ - default void addValueColumn(SelectableMapping selectableMapping) { - addValueColumn( - selectableMapping.getSelectionExpression(), - selectableMapping.getWriteExpression(), - selectableMapping.getJdbcMapping() - ); - } +public interface TableInsertBuilder extends TableMutationBuilder, ColumnValuesTableMutationBuilder { - /** - * Add a column as part of the values list - */ - void addValueColumn(String columnName, String columnWriteFragment, JdbcMapping jdbcMapping); - - /** - * Add a key column - */ - default void addKeyColumn(SelectableMapping selectableMapping) { - addKeyColumn( - selectableMapping.getSelectionExpression(), - selectableMapping.getWriteExpression(), - selectableMapping.getJdbcMapping() - ); - } - - /** - * Add a key column - */ - void addKeyColumn(String columnName, String valueExpression, JdbcMapping jdbcMapping); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/TableUpdateBuilder.java b/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/TableUpdateBuilder.java index 39657f8c4a..032a52d079 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/TableUpdateBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/TableUpdateBuilder.java @@ -6,28 +6,18 @@ */ package org.hibernate.sql.model.ast.builder; -import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.sql.model.MutationOperation; import org.hibernate.sql.model.ast.RestrictedTableMutation; /** + * {@link TableMutationBuilder} implementation for {@code update} statements. + * * @author Steve Ebersole */ public interface TableUpdateBuilder - extends RestrictedTableMutationBuilder> { - - /** - * Add a column as part of the values list - */ - default void addValueColumn(SelectableMapping selectableMapping) { - addValueColumn( - selectableMapping.getSelectionExpression(), - selectableMapping.getWriteExpression(), - selectableMapping.getJdbcMapping() - ); - } + extends RestrictedTableMutationBuilder>, ColumnValuesTableMutationBuilder { /** * Convenience form of {@link #addValueColumn(SelectableMapping)} matching the @@ -42,26 +32,5 @@ public interface TableUpdateBuilder addValueColumn( selectableMapping ); } - /** - * Add a column as part of the values list - */ - void addValueColumn(String columnName, String columnWriteFragment, JdbcMapping jdbcMapping); - - /** - * Add a key column - */ - default void addKeyColumn(SelectableMapping selectableMapping) { - addKeyColumn( - selectableMapping.getSelectionExpression(), - selectableMapping.getWriteExpression(), - selectableMapping.getJdbcMapping() - ); - } - - /** - * Add a key column - */ - void addKeyColumn(String columnName, String valueExpression, JdbcMapping jdbcMapping); - void setWhere(String fragment); } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java index eb38cc770c..15a729ef5b 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java @@ -69,7 +69,7 @@ public abstract class AbstractNonIdentifierAttribute extends AbstractAttribute i } @Override - public ValueGeneration getValueGenerationStrategy() { + public ValueGenerationStrategy getValueGenerationStrategy() { return attributeInformation.getValueGenerationStrategy(); } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/BaselineAttributeInformation.java b/hibernate-core/src/main/java/org/hibernate/tuple/BaselineAttributeInformation.java index 4d695df090..e4f27de896 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/BaselineAttributeInformation.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/BaselineAttributeInformation.java @@ -16,7 +16,7 @@ public class BaselineAttributeInformation { private final boolean lazy; private final boolean insertable; private final boolean updateable; - private final ValueGeneration valueGenerationStrategy; + private final ValueGenerationStrategy valueGenerationStrategy; private final boolean nullable; private final boolean dirtyCheckable; private final boolean versionable; @@ -28,7 +28,7 @@ public class BaselineAttributeInformation { boolean lazy, boolean insertable, boolean updateable, - ValueGeneration valueGenerationStrategy, + ValueGenerationStrategy valueGenerationStrategy, boolean nullable, boolean dirtyCheckable, boolean versionable, @@ -57,7 +57,7 @@ public class BaselineAttributeInformation { return updateable; } - public ValueGeneration getValueGenerationStrategy() { + public ValueGenerationStrategy getValueGenerationStrategy() { return valueGenerationStrategy; } @@ -89,7 +89,7 @@ public class BaselineAttributeInformation { private boolean lazy; private boolean insertable; private boolean updateable; - private ValueGeneration valueGenerationStrategy; + private ValueGenerationStrategy valueGenerationStrategy; private boolean nullable; private boolean dirtyCheckable; private boolean versionable; @@ -111,7 +111,7 @@ public class BaselineAttributeInformation { return this; } - public Builder setValueGenerationStrategy(ValueGeneration valueGenerationStrategy) { + public Builder setValueGenerationStrategy(ValueGenerationStrategy valueGenerationStrategy) { this.valueGenerationStrategy = valueGenerationStrategy; return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/InDatabaseValueGenerationStrategy.java b/hibernate-core/src/main/java/org/hibernate/tuple/InDatabaseValueGenerationStrategy.java index b4099e2296..e37f1bbc98 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/InDatabaseValueGenerationStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/InDatabaseValueGenerationStrategy.java @@ -6,37 +6,63 @@ */ package org.hibernate.tuple; +import org.hibernate.dialect.Dialect; + /** - * Strategy for describing values which are generated in the database. + * A value generated by the database might be generated implicitly, by a trigger, or using + * a {@code default} column value specified in DDL, for example, or it might be generated + * by a SQL expression occurring explicitly in the SQL {@code insert} or {@code update} + * statement. In this case, the generated value is retrieved from the database using a SQL + * {@code select}. * * @author Steve Ebersole */ -public interface InDatabaseValueGenerationStrategy { - /** - * When is this value generated : NEVER, INSERT, ALWAYS (INSERT+UPDATE) - * - * @return When the value is generated. - */ - GenerationTiming getGenerationTiming(); +public interface InDatabaseValueGenerationStrategy extends ValueGenerationStrategy { /** - * Should the column(s) be referenced in the INSERT / UPDATE SQL? - *

- * This will be {@code false} most often to have a DDL-defined DEFAULT value be applied on INSERT. For - * trigger-generated values this could be {@code true} or {@code false} depending on whether the user wants - * the trigger to have access to some value for the column passed in. + * Determines if the columns whose values are generated are included in the column list of + * the SQL {@code insert} or {@code update} statement, in the case where the value is + * generated by the database. For example, this method should return: + *

    + *
  • {@code true} if the value is generated by calling a SQL function like + * {@code current_timestamp}, or + *
  • {@code false} if the value is generated by a trigger, + * by {@link org.hibernate.annotations.GeneratedColumn generated always as}, or + * using a {@linkplain org.hibernate.annotations.ColumnDefault column default value}. + *
+ * If the value is generated in Java, this method is not called, and so for backward + * compatibility with Hibernate 5 it is permitted to return any value. On the other hand, + * when a property value is generated in Java, the column certainly must be included in + * the column list, and so it's most correct for this method to return {@code true}! * - * @return {@code true} indicates the column should be included in the SQL. + * @return {@code true} if the column is included in the column list of the SQL statement. */ boolean referenceColumnsInSql(); /** - * For columns that will be referenced in the SQL (per {@link #referenceColumnsInSql()}), what value - * should be used in the SQL as the column value. - * - * @return The column value to be used in the SQL. {@code null} for any element indicates to use the Column - * defined value ({@link org.hibernate.mapping.Column#getWriteExpr}). + * Determines if the property values are written to JDBC as the argument of a JDBC {@code ?} + * parameter. */ - String[] getReferencedColumnValues(); + boolean writePropertyValue(); + /** + * A SQL expression indicating how to calculate the generated values when the property values + * are {@linkplain #generatedByDatabase() generated in the database} and the mapped columns + * are {@linkplain #referenceColumnsInSql() included in the SQL statement}. The SQL expressions + * might be: + *
    + *
  • function calls like {@code current_timestamp} or {@code nextval('mysequence')}, or + *
  • syntactic markers like {@code default}. + *
+ * When the property values are generated in Java, this method is not called. + * + * @param dialect The {@linkplain Dialect SQL dialect}, allowing generation of an expression + * in dialect-specific SQL. + * @return The column value to be used in the generated SQL statement. + */ + String[] getReferencedColumnValues(Dialect dialect); + + default boolean generatedByDatabase() { + return true; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/InMemoryValueGenerationStrategy.java b/hibernate-core/src/main/java/org/hibernate/tuple/InMemoryValueGenerationStrategy.java index 2bd9ee12aa..dac8093783 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/InMemoryValueGenerationStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/InMemoryValueGenerationStrategy.java @@ -7,23 +7,23 @@ package org.hibernate.tuple; /** + * Java value generation is the responsibility of an associated {@link ValueGenerator}. + * In this case, the generated value is written to the database just like any other field + * or property value. + * * @author Steve Ebersole */ -public interface InMemoryValueGenerationStrategy { - /** - * When is this value generated : NEVER, INSERT, ALWAYS (INSERT+UPDATE) - * - * @return When the value is generated. - */ - GenerationTiming getGenerationTiming(); +public interface InMemoryValueGenerationStrategy extends ValueGenerationStrategy { /** - * Obtain the in-VM value generator. - *

- * May return {@code null}. In fact for values that are generated "in the database" via execution of the - * INSERT/UPDATE statement, the expectation is that {@code null} be returned here + * Obtain the {@linkplain ValueGenerator Java value generator}, if the value is generated in + * Java, or return {@code null} if the value is generated by the database. * - * @return The strategy for performing in-VM value generation + * @return The value generator */ ValueGenerator getValueGenerator(); + + default boolean generatedByDatabase() { + return false; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/NonIdentifierAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/NonIdentifierAttribute.java index 3e49ba7a62..b1d5d0553b 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/NonIdentifierAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/NonIdentifierAttribute.java @@ -19,7 +19,7 @@ public interface NonIdentifierAttribute extends Attribute { boolean isUpdateable(); - ValueGeneration getValueGenerationStrategy(); + ValueGenerationStrategy getValueGenerationStrategy(); boolean isNullable(); diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java b/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java index 6f710c7761..38e843ce83 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java @@ -6,24 +6,16 @@ */ package org.hibernate.tuple; -import java.lang.reflect.Constructor; - import org.hibernate.HibernateException; import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.id.IdentifierGenerator; -import org.hibernate.internal.util.ReflectHelper; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; -import org.hibernate.metamodel.RepresentationMode; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.property.access.spi.Getter; -import org.hibernate.property.access.spi.PropertyAccess; -import org.hibernate.property.access.spi.PropertyAccessStrategy; -import org.hibernate.property.access.spi.PropertyAccessStrategyResolver; import org.hibernate.tuple.entity.EntityBasedAssociationAttribute; import org.hibernate.tuple.entity.EntityBasedBasicAttribute; import org.hibernate.tuple.entity.EntityBasedCompositionAttribute; diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/StandardProperty.java b/hibernate-core/src/main/java/org/hibernate/tuple/StandardProperty.java index cd63f58280..cecab73a79 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/StandardProperty.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/StandardProperty.java @@ -42,7 +42,7 @@ public class StandardProperty extends AbstractNonIdentifierAttribute implements boolean lazy, boolean insertable, boolean updateable, - ValueGeneration valueGenerationStrategy, + ValueGenerationStrategy valueGenerationStrategy, boolean nullable, boolean checkable, boolean versionable, diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/ValueGeneration.java b/hibernate-core/src/main/java/org/hibernate/tuple/ValueGeneration.java index 11ad5a5e98..c23f7eda88 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/ValueGeneration.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/ValueGeneration.java @@ -8,50 +8,14 @@ package org.hibernate.tuple; import org.hibernate.dialect.Dialect; -import java.io.Serializable; - /** - * Describes the generation of values of a certain field or property of an entity. A generated - * value might be generated in Java, or by the database. - *

    - *
  • Java value generation is the responsibility of an associated {@link ValueGenerator}. - * In this case, the generated value is written to the database just like any other field - * or property value. - *
  • A value generated by the database might be generated implicitly, by a trigger, or using - * a {@code default} column value specified in DDL, for example, or it might be generated - * by a SQL expression occurring explicitly in the SQL {@code insert} or {@code update} - * statement. In this case, the generated value is retrieved from the database using a SQL - * {@code select}. - *
- * - * @see org.hibernate.annotations.ValueGenerationType - * @see org.hibernate.annotations.Generated - * @see org.hibernate.annotations.GeneratorType + * A value generator that can adapt to both Java value generation and database value + * generation. * * @author Steve Ebersole + * @author Gavin King */ -public interface ValueGeneration extends Serializable { - /** - * Specifies that the property value is generated: - *
    - *
  • {@linkplain GenerationTiming#INSERT when the entity is inserted}, - *
  • {@linkplain GenerationTiming#UPDATE when the entity is updated}, - *
  • {@linkplain GenerationTiming#ALWAYS whenever the entity is inserted or updated}, or - *
  • {@linkplain GenerationTiming#NEVER never}. - *
- * - * @return The {@link GenerationTiming} specifying when the value is generated. - */ - GenerationTiming getGenerationTiming(); - - /** - * Obtain the {@linkplain ValueGenerator Java value generator}, if the value is generated in - * Java, or return {@code null} if the value is generated by the database. - * - * @return The value generator - */ - ValueGenerator getValueGenerator(); - +public interface ValueGeneration extends InMemoryValueGenerationStrategy, InDatabaseValueGenerationStrategy { /** * Determines if the column whose value is generated is included in the column list of the * SQL {@code insert} or {@code update} statement, in the case where the value is generated @@ -110,6 +74,17 @@ public interface ValueGeneration extends Serializable { return getDatabaseGeneratedReferencedColumnValue(); } + @Override + default String[] getReferencedColumnValues(Dialect dialect) { + String columnValue = getDatabaseGeneratedReferencedColumnValue( dialect ); + return columnValue == null ? null : new String[] { columnValue }; + } + + @Override + default boolean referenceColumnsInSql() { + return referenceColumnInSql(); + } + /** * Determines if the property value is generated in Java, or by the database. *

@@ -137,6 +112,6 @@ public interface ValueGeneration extends Serializable { default boolean writePropertyValue() { return !generatedByDatabase() // value generated in memory and then written as normal // current value of property of entity instance written completely as normal - || referenceColumnInSql() && getDatabaseGeneratedReferencedColumnValue()==null; + || referenceColumnsInSql() && getDatabaseGeneratedReferencedColumnValue()==null; } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/ValueGenerationStrategy.java b/hibernate-core/src/main/java/org/hibernate/tuple/ValueGenerationStrategy.java new file mode 100644 index 0000000000..f2404548db --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/ValueGenerationStrategy.java @@ -0,0 +1,52 @@ +/* + * 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 . + */ +package org.hibernate.tuple; + +import java.io.Serializable; + +/** + * Describes the generation of values of a certain field or property of an entity. A generated + * value might be generated in Java, or by the database. + *

    + *
  • Java value generation is the responsibility of an associated {@link ValueGenerator}. + * In this case, the generated value is written to the database just like any other field + * or property value. + *
  • A value generated by the database might be generated implicitly, by a trigger, or using + * a {@code default} column value specified in DDL, for example, or it might be generated + * by a SQL expression occurring explicitly in the SQL {@code insert} or {@code update} + * statement. In this case, the generated value is retrieved from the database using a SQL + * {@code select}. + *
+ * + * @see org.hibernate.annotations.ValueGenerationType + * @see org.hibernate.annotations.Generated + * @see org.hibernate.annotations.GeneratorType + * + * @author Steve Ebersole + * @author Gavin King + */ +public interface ValueGenerationStrategy extends Serializable { + /** + * Specifies that the property value is generated: + *
    + *
  • {@linkplain GenerationTiming#INSERT when the entity is inserted}, + *
  • {@linkplain GenerationTiming#UPDATE when the entity is updated}, + *
  • {@linkplain GenerationTiming#ALWAYS whenever the entity is inserted or updated}, or + *
  • {@linkplain GenerationTiming#NEVER never}. + *
+ * + * @return The {@link GenerationTiming} specifying when the value is generated. + */ + GenerationTiming getGenerationTiming(); + + /** + * Determines if the property value is generated in Java, or by the database. + * @return {@code true} if the value is generated by the database, or false if it is + * generated in Java using a {@link ValueGenerator}. + */ + boolean generatedByDatabase(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java index 9932fad67a..e9718ad299 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java @@ -40,13 +40,13 @@ import org.hibernate.mapping.Subclass; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.spi.PersisterCreationContext; +import org.hibernate.tuple.ValueGenerationStrategy; +import org.hibernate.tuple.InDatabaseValueGenerationStrategy; import org.hibernate.tuple.GenerationTiming; import org.hibernate.tuple.IdentifierProperty; -import org.hibernate.tuple.InDatabaseValueGenerationStrategy; import org.hibernate.tuple.InMemoryValueGenerationStrategy; import org.hibernate.tuple.NonIdentifierAttribute; import org.hibernate.tuple.PropertyFactory; -import org.hibernate.tuple.ValueGeneration; import org.hibernate.tuple.ValueGenerator; import org.hibernate.type.AssociationType; import org.hibernate.type.CollectionType; @@ -458,28 +458,25 @@ public class EntityMetamodel implements Serializable { private static GenerationStrategyPair buildGenerationStrategyPair( final SessionFactoryImplementor sessionFactory, final Property mappingProperty) { - final ValueGeneration valueGeneration = mappingProperty.getValueGenerationStrategy(); + final ValueGenerationStrategy valueGeneration = mappingProperty.getValueGenerationStrategy(); if ( valueGeneration != null && valueGeneration.getGenerationTiming() != GenerationTiming.NEVER ) { // the property is generated in full. build the generation strategy pair. if ( !valueGeneration.generatedByDatabase() ) { // in-memory generation return new GenerationStrategyPair( - FullInMemoryValueGenerationStrategy.create( valueGeneration ) + FullInMemoryValueGenerationStrategy.create( (InMemoryValueGenerationStrategy) valueGeneration ) ); } else { // in-db generation - return new GenerationStrategyPair( - create( - sessionFactory, - mappingProperty, - valueGeneration - ) - ); + return new GenerationStrategyPair( (InDatabaseValueGenerationStrategy) valueGeneration ); } } else if ( mappingProperty.getValue() instanceof Component ) { - final CompositeGenerationStrategyPairBuilder builder = new CompositeGenerationStrategyPairBuilder( mappingProperty ); + final CompositeGenerationStrategyPairBuilder builder = new CompositeGenerationStrategyPairBuilder( + mappingProperty, + sessionFactory.getJdbcServices().getDialect() + ); interpretPartialCompositeValueGeneration( sessionFactory, (Component) mappingProperty.getValue(), builder ); return builder.buildPair(); } @@ -498,36 +495,6 @@ public class EntityMetamodel implements Serializable { } } - public static InDatabaseValueGenerationStrategyImpl create( - SessionFactoryImplementor factory, - Property mappingProperty, - ValueGeneration valueGeneration) { - final int numberOfMappedColumns = mappingProperty.getType().getColumnSpan( factory ); - final Dialect dialect = factory.getJdbcServices().getDialect(); - if ( numberOfMappedColumns == 1 ) { - return new InDatabaseValueGenerationStrategyImpl( - valueGeneration.getGenerationTiming(), - valueGeneration.referenceColumnInSql(), - new String[] { valueGeneration.getDatabaseGeneratedReferencedColumnValue(dialect) } - - ); - } - else { - if ( valueGeneration.getDatabaseGeneratedReferencedColumnValue(dialect) != null ) { - LOG.debugf( - "Value generator specified column value in reference to multi-column attribute [%s -> %s]; ignoring", - mappingProperty.getPersistentClass(), - mappingProperty.getName() - ); - } - return new InDatabaseValueGenerationStrategyImpl( - valueGeneration.getGenerationTiming(), - valueGeneration.referenceColumnInSql(), - new String[numberOfMappedColumns] - ); - } - } - public static class GenerationStrategyPair { private final InMemoryValueGenerationStrategy inMemoryStrategy; private final InDatabaseValueGenerationStrategy inDatabaseStrategy; @@ -540,7 +507,7 @@ public class EntityMetamodel implements Serializable { this( inMemoryStrategy, NoInDatabaseValueGenerationStrategy.INSTANCE ); } - public GenerationStrategyPair(InDatabaseValueGenerationStrategyImpl inDatabaseStrategy) { + public GenerationStrategyPair(InDatabaseValueGenerationStrategy inDatabaseStrategy) { this( NoInMemoryValueGenerationStrategy.INSTANCE, inDatabaseStrategy ); } @@ -583,14 +550,16 @@ public class EntityMetamodel implements Serializable { private static class CompositeGenerationStrategyPairBuilder { private final Property mappingProperty; + private final Dialect dialect; private boolean hadInMemoryGeneration; private boolean hadInDatabaseGeneration; private List inDatabaseStrategies; - public CompositeGenerationStrategyPairBuilder(Property mappingProperty) { + public CompositeGenerationStrategyPairBuilder(Property mappingProperty, Dialect dialect) { this.mappingProperty = mappingProperty; + this.dialect = dialect; } public void addPair(GenerationStrategyPair generationStrategyPair) { @@ -675,8 +644,8 @@ public class EntityMetamodel implements Serializable { // override base-line value referenceColumns = true; } - if ( subStrategy.getReferencedColumnValues() != null ) { - if ( subStrategy.getReferencedColumnValues().length != property.getColumnSpan() ) { + if ( subStrategy.getReferencedColumnValues(dialect) != null ) { + if ( subStrategy.getReferencedColumnValues(dialect).length != property.getColumnSpan() ) { throw new ValueGenerationStrategyException( "Internal error : mismatch between number of collected 'referenced column values'" + " and number of columns for composite attribute : " + mappingProperty.getName() + @@ -684,7 +653,7 @@ public class EntityMetamodel implements Serializable { ); } System.arraycopy( - subStrategy.getReferencedColumnValues(), + subStrategy.getReferencedColumnValues(dialect), 0, columnValues, columnIndex, @@ -730,7 +699,7 @@ public class EntityMetamodel implements Serializable { this.generator = generator; } - public static FullInMemoryValueGenerationStrategy create(ValueGeneration valueGeneration) { + public static FullInMemoryValueGenerationStrategy create(InMemoryValueGenerationStrategy valueGeneration) { return new FullInMemoryValueGenerationStrategy( valueGeneration.getGenerationTiming(), valueGeneration.getValueGenerator() @@ -765,9 +734,14 @@ public class EntityMetamodel implements Serializable { } @Override - public String[] getReferencedColumnValues() { + public String[] getReferencedColumnValues(Dialect dialect) { return null; } + + @Override + public boolean writePropertyValue() { + return true; + } } private static class InDatabaseValueGenerationStrategyImpl implements InDatabaseValueGenerationStrategy { @@ -795,9 +769,14 @@ public class EntityMetamodel implements Serializable { } @Override - public String[] getReferencedColumnValues() { + public String[] getReferencedColumnValues(Dialect dialect) { return referencedColumnValues; } + + @Override + public boolean writePropertyValue() { + return false; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java b/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java index 8796d4cde6..24a9a7bb39 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java @@ -35,6 +35,7 @@ import org.hibernate.metamodel.spi.EmbeddableInstantiator; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.resource.beans.spi.ManagedBeanRegistry; +import org.hibernate.tuple.ValueGenerationStrategy; import org.hibernate.tuple.PropertyFactory; import org.hibernate.tuple.StandardProperty; import org.hibernate.tuple.ValueGeneration; @@ -52,7 +53,7 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen private final String[] propertyNames; private final Type[] propertyTypes; - private final ValueGeneration[] propertyValueGenerationStrategies; + private final ValueGenerationStrategy[] propertyValueGenerationStrategies; private final boolean[] propertyNullability; private final int[] originalPropertyOrder; protected final int propertySpan; @@ -437,7 +438,7 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen return propertyTypes; } - public ValueGeneration[] getPropertyValueGenerationStrategies() { + public ValueGenerationStrategy[] getPropertyValueGenerationStrategies() { return propertyValueGenerationStrategies; } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/AuditMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/AuditMetadataGenerator.java index dbbaad60f4..c4f1513171 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/AuditMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/AuditMetadataGenerator.java @@ -33,9 +33,8 @@ import org.hibernate.envers.internal.entities.mapper.SubclassPropertyMapper; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.hibernate.mapping.SyntheticProperty; +import org.hibernate.tuple.ValueGenerationStrategy; import org.hibernate.tuple.GeneratedValueGeneration; -import org.hibernate.tuple.GenerationTiming; -import org.hibernate.tuple.ValueGeneration; import org.jboss.logging.Logger; @@ -120,7 +119,7 @@ public final class AuditMetadataGenerator extends AbstractMetadataGenerator { private boolean isPropertyInsertable(Property property) { if ( !property.isInsertable() ) { - final ValueGeneration generation = property.getValueGenerationStrategy(); + final ValueGenerationStrategy generation = property.getValueGenerationStrategy(); if ( generation instanceof GeneratedValueGeneration ) { final GeneratedValueGeneration valueGeneration = (GeneratedValueGeneration) generation; if ( valueGeneration.getGenerationTiming().includesInsert() ) {