HHH-17170 Support custom sql mutations for associated collections
This commit is contained in:
parent
800f8f6f31
commit
db2f2d8a9f
|
@ -26,7 +26,6 @@ import org.hibernate.metamodel.mapping.CollectionPart;
|
|||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
import org.hibernate.persister.collection.mutation.CollectionTableMapping;
|
||||
import org.hibernate.persister.collection.mutation.DeleteRowsCoordinator;
|
||||
import org.hibernate.persister.collection.mutation.DeleteRowsCoordinatorNoOp;
|
||||
import org.hibernate.persister.collection.mutation.DeleteRowsCoordinatorStandard;
|
||||
|
@ -44,17 +43,14 @@ import org.hibernate.persister.collection.mutation.UpdateRowsCoordinatorStandard
|
|||
import org.hibernate.persister.spi.PersisterCreationContext;
|
||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.model.ast.ColumnValueParameterList;
|
||||
import org.hibernate.sql.model.ast.MutatingTableReference;
|
||||
import org.hibernate.sql.model.ast.RestrictedTableMutation;
|
||||
import org.hibernate.sql.model.ast.TableInsert;
|
||||
import org.hibernate.sql.model.ast.TableMutation;
|
||||
import org.hibernate.sql.model.ast.builder.TableDeleteBuilderStandard;
|
||||
import org.hibernate.sql.model.ast.builder.CollectionRowDeleteBuilder;
|
||||
import org.hibernate.sql.model.ast.builder.TableInsertBuilderStandard;
|
||||
import org.hibernate.sql.model.ast.builder.TableUpdateBuilderStandard;
|
||||
import org.hibernate.sql.model.jdbc.JdbcDeleteMutation;
|
||||
import org.hibernate.sql.model.jdbc.JdbcMutationOperation;
|
||||
import org.hibernate.sql.model.jdbc.JdbcUpdateMutation;
|
||||
|
||||
import static org.hibernate.sql.model.ModelMutationLogging.MODEL_MUTATION_LOGGER;
|
||||
|
||||
|
@ -410,40 +406,6 @@ public class BasicCollectionPersister extends AbstractCollectionPersister {
|
|||
// Update handling
|
||||
|
||||
private JdbcMutationOperation generateUpdateRowOperation(MutatingTableReference tableReference) {
|
||||
if ( getIdentifierTableMapping().getUpdateDetails().getCustomSql() != null ) {
|
||||
return buildCustomSqlUpdateRowOperation( tableReference );
|
||||
}
|
||||
|
||||
return buildGeneratedUpdateRowOperation( tableReference );
|
||||
|
||||
}
|
||||
|
||||
private JdbcMutationOperation buildCustomSqlUpdateRowOperation(MutatingTableReference tableReference) {
|
||||
final PluralAttributeMapping attribute = getAttributeMapping();
|
||||
assert attribute != null;
|
||||
|
||||
final ForeignKeyDescriptor foreignKey = attribute.getKeyDescriptor();
|
||||
assert foreignKey != null;
|
||||
|
||||
final int keyColumnCount = foreignKey.getJdbcTypeCount();
|
||||
final ColumnValueParameterList parameterBinders = new ColumnValueParameterList(
|
||||
tableReference,
|
||||
ParameterUsage.RESTRICT,
|
||||
keyColumnCount
|
||||
);
|
||||
foreignKey.getKeyPart().forEachSelectable( parameterBinders );
|
||||
|
||||
return new JdbcUpdateMutation(
|
||||
getCollectionTableMapping(),
|
||||
this,
|
||||
getCollectionTableMapping().getUpdateDetails().getCustomSql(),
|
||||
getCollectionTableMapping().getUpdateDetails().isCallable(),
|
||||
getCollectionTableMapping().getUpdateDetails().getExpectation(),
|
||||
parameterBinders
|
||||
);
|
||||
}
|
||||
|
||||
private JdbcMutationOperation buildGeneratedUpdateRowOperation(MutatingTableReference tableReference) {
|
||||
final RestrictedTableMutation<JdbcMutationOperation> sqlAst = generateUpdateRowAst( tableReference );
|
||||
|
||||
final SqlAstTranslator<JdbcMutationOperation> translator = getFactory().getJdbcServices()
|
||||
|
@ -458,6 +420,7 @@ public class BasicCollectionPersister extends AbstractCollectionPersister {
|
|||
final PluralAttributeMapping attribute = getAttributeMapping();
|
||||
assert attribute != null;
|
||||
|
||||
// note that custom sql update row details are handled by TableUpdateBuilderStandard
|
||||
final TableUpdateBuilderStandard<?> updateBuilder = new TableUpdateBuilderStandard<>(
|
||||
this,
|
||||
tableReference,
|
||||
|
@ -620,41 +583,6 @@ public class BasicCollectionPersister extends AbstractCollectionPersister {
|
|||
// Delete handling
|
||||
|
||||
private JdbcMutationOperation generateDeleteRowOperation(MutatingTableReference tableReference) {
|
||||
if ( getIdentifierTableMapping().getDeleteRowDetails().getCustomSql() != null ) {
|
||||
return buildCustomSqlDeleteRowOperation( tableReference );
|
||||
}
|
||||
|
||||
return buildGeneratedDeleteRowOperation( tableReference );
|
||||
}
|
||||
|
||||
private JdbcMutationOperation buildCustomSqlDeleteRowOperation(MutatingTableReference tableReference) {
|
||||
final PluralAttributeMapping attribute = getAttributeMapping();
|
||||
assert attribute != null;
|
||||
|
||||
final ForeignKeyDescriptor foreignKey = attribute.getKeyDescriptor();
|
||||
assert foreignKey != null;
|
||||
|
||||
final CollectionTableMapping tableMapping = (CollectionTableMapping) tableReference.getTableMapping();
|
||||
|
||||
final int keyColumnCount = foreignKey.getJdbcTypeCount();
|
||||
final ColumnValueParameterList parameterBinders = new ColumnValueParameterList(
|
||||
tableReference,
|
||||
ParameterUsage.RESTRICT,
|
||||
keyColumnCount
|
||||
);
|
||||
foreignKey.getKeyPart().forEachSelectable( parameterBinders );
|
||||
|
||||
return new JdbcDeleteMutation(
|
||||
tableMapping,
|
||||
this,
|
||||
tableMapping.getDeleteDetails().getCustomSql(),
|
||||
tableMapping.getDeleteDetails().isCallable(),
|
||||
tableMapping.getDeleteDetails().getExpectation(),
|
||||
parameterBinders
|
||||
);
|
||||
}
|
||||
|
||||
private JdbcMutationOperation buildGeneratedDeleteRowOperation(MutatingTableReference tableReference) {
|
||||
final RestrictedTableMutation<JdbcMutationOperation> sqlAst = generateDeleteRowAst( tableReference );
|
||||
|
||||
final SqlAstTranslator<JdbcMutationOperation> translator = getFactory().getJdbcServices()
|
||||
|
@ -672,7 +600,8 @@ public class BasicCollectionPersister extends AbstractCollectionPersister {
|
|||
final ForeignKeyDescriptor fkDescriptor = pluralAttribute.getKeyDescriptor();
|
||||
assert fkDescriptor != null;
|
||||
|
||||
final TableDeleteBuilderStandard deleteBuilder = new TableDeleteBuilderStandard(
|
||||
// note that custom sql delete row details are handled by CollectionRowDeleteBuilder
|
||||
final CollectionRowDeleteBuilder deleteBuilder = new CollectionRowDeleteBuilder(
|
||||
this,
|
||||
tableReference,
|
||||
getFactory(),
|
||||
|
|
|
@ -20,7 +20,6 @@ import org.hibernate.engine.jdbc.batch.internal.BasicBatchKey;
|
|||
import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
|
||||
import org.hibernate.engine.jdbc.mutation.MutationExecutor;
|
||||
import org.hibernate.engine.jdbc.mutation.ParameterUsage;
|
||||
import org.hibernate.sql.model.internal.MutationOperationGroupFactory;
|
||||
import org.hibernate.engine.jdbc.mutation.internal.MutationQueryOptions;
|
||||
import org.hibernate.engine.jdbc.mutation.spi.MutationExecutorService;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
@ -38,7 +37,6 @@ import org.hibernate.metamodel.mapping.SelectableMapping;
|
|||
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
|
||||
import org.hibernate.metamodel.mapping.internal.OneToManyCollectionPart;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
import org.hibernate.persister.collection.mutation.CollectionTableMapping;
|
||||
import org.hibernate.persister.collection.mutation.DeleteRowsCoordinator;
|
||||
import org.hibernate.persister.collection.mutation.DeleteRowsCoordinatorNoOp;
|
||||
import org.hibernate.persister.collection.mutation.DeleteRowsCoordinatorStandard;
|
||||
|
@ -74,11 +72,11 @@ import org.hibernate.sql.model.ast.ColumnWriteFragment;
|
|||
import org.hibernate.sql.model.ast.MutatingTableReference;
|
||||
import org.hibernate.sql.model.ast.RestrictedTableMutation;
|
||||
import org.hibernate.sql.model.ast.TableUpdate;
|
||||
import org.hibernate.sql.model.ast.builder.CollectionRowDeleteByUpdateSetNullBuilder;
|
||||
import org.hibernate.sql.model.ast.builder.TableUpdateBuilderStandard;
|
||||
import org.hibernate.sql.model.internal.MutationOperationGroupFactory;
|
||||
import org.hibernate.sql.model.internal.TableUpdateStandard;
|
||||
import org.hibernate.sql.model.jdbc.JdbcDeleteMutation;
|
||||
import org.hibernate.sql.model.jdbc.JdbcMutationOperation;
|
||||
import org.hibernate.sql.model.jdbc.JdbcUpdateMutation;
|
||||
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
|
||||
import static org.hibernate.sql.model.ModelMutationLogging.MODEL_MUTATION_LOGGER;
|
||||
|
@ -522,41 +520,6 @@ public class OneToManyPersister extends AbstractCollectionPersister {
|
|||
}
|
||||
|
||||
private JdbcMutationOperation generateDeleteRowOperation(MutatingTableReference tableReference) {
|
||||
if ( getIdentifierTableMapping().getDeleteRowDetails().getCustomSql() != null ) {
|
||||
return buildCustomSqlDeleteRowOperation( tableReference );
|
||||
}
|
||||
|
||||
return buildGeneratedDeleteRowOperation( tableReference );
|
||||
}
|
||||
|
||||
private JdbcMutationOperation buildCustomSqlDeleteRowOperation(MutatingTableReference tableReference) {
|
||||
final PluralAttributeMapping attribute = getAttributeMapping();
|
||||
assert attribute != null;
|
||||
|
||||
final ForeignKeyDescriptor foreignKey = attribute.getKeyDescriptor();
|
||||
assert foreignKey != null;
|
||||
|
||||
final CollectionTableMapping tableMapping = (CollectionTableMapping) tableReference.getTableMapping();
|
||||
|
||||
final int keyColumnCount = foreignKey.getJdbcTypeCount();
|
||||
final ColumnValueParameterList parameterBinders = new ColumnValueParameterList(
|
||||
tableReference,
|
||||
ParameterUsage.RESTRICT,
|
||||
keyColumnCount
|
||||
);
|
||||
foreignKey.getKeyPart().forEachSelectable( parameterBinders );
|
||||
|
||||
return new JdbcDeleteMutation(
|
||||
tableMapping,
|
||||
this,
|
||||
tableMapping.getDeleteDetails().getCustomSql(),
|
||||
tableMapping.getDeleteDetails().isCallable(),
|
||||
tableMapping.getDeleteDetails().getExpectation(),
|
||||
parameterBinders
|
||||
);
|
||||
}
|
||||
|
||||
private JdbcMutationOperation buildGeneratedDeleteRowOperation(MutatingTableReference tableReference) {
|
||||
final RestrictedTableMutation<JdbcMutationOperation> sqlAst = generateDeleteRowAst( tableReference );
|
||||
|
||||
final SqlAstTranslator<JdbcMutationOperation> translator = getFactory().getJdbcServices()
|
||||
|
@ -568,7 +531,8 @@ public class OneToManyPersister extends AbstractCollectionPersister {
|
|||
}
|
||||
|
||||
public RestrictedTableMutation<JdbcMutationOperation> generateDeleteRowAst(MutatingTableReference tableReference) {
|
||||
final TableUpdateBuilderStandard<MutationOperation> updateBuilder = new TableUpdateBuilderStandard<>(
|
||||
// note that custom sql delete row details are handled by CollectionRowUpdateBuilder
|
||||
final CollectionRowDeleteByUpdateSetNullBuilder<MutationOperation> updateBuilder = new CollectionRowDeleteByUpdateSetNullBuilder<>(
|
||||
this,
|
||||
tableReference,
|
||||
getFactory(),
|
||||
|
@ -736,41 +700,7 @@ public class OneToManyPersister extends AbstractCollectionPersister {
|
|||
|
||||
|
||||
private JdbcMutationOperation generateWriteIndexOperation(MutatingTableReference tableReference) {
|
||||
if ( getIdentifierTableMapping().getUpdateDetails().getCustomSql() != null ) {
|
||||
return buildCustomSqlWriteIndexOperation( tableReference );
|
||||
}
|
||||
|
||||
return buildGeneratedWriteIndexOperation( tableReference );
|
||||
}
|
||||
|
||||
private JdbcMutationOperation buildCustomSqlWriteIndexOperation(MutatingTableReference tableReference) {
|
||||
final PluralAttributeMapping attribute = getAttributeMapping();
|
||||
assert attribute != null;
|
||||
|
||||
final ForeignKeyDescriptor foreignKey = attribute.getKeyDescriptor();
|
||||
assert foreignKey != null;
|
||||
|
||||
final CollectionTableMapping tableMapping = (CollectionTableMapping) tableReference.getTableMapping();
|
||||
|
||||
final int keyColumnCount = foreignKey.getJdbcTypeCount();
|
||||
final ColumnValueParameterList parameterBinders = new ColumnValueParameterList(
|
||||
tableReference,
|
||||
ParameterUsage.RESTRICT,
|
||||
keyColumnCount
|
||||
);
|
||||
foreignKey.getKeyPart().forEachSelectable( parameterBinders );
|
||||
|
||||
return new JdbcUpdateMutation(
|
||||
tableMapping,
|
||||
this,
|
||||
tableMapping.getUpdateDetails().getCustomSql(),
|
||||
tableMapping.getUpdateDetails().isCallable(),
|
||||
tableMapping.getUpdateDetails().getExpectation(),
|
||||
parameterBinders
|
||||
);
|
||||
}
|
||||
|
||||
private JdbcMutationOperation buildGeneratedWriteIndexOperation(MutatingTableReference tableReference) {
|
||||
// note that custom sql update details are handled by TableUpdateBuilderStandard
|
||||
final TableUpdateBuilderStandard<JdbcMutationOperation> updateBuilder = new TableUpdateBuilderStandard<>(
|
||||
this,
|
||||
tableReference,
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.jdbc.Expectation;
|
||||
import org.hibernate.persister.collection.mutation.CollectionTableMapping;
|
||||
import org.hibernate.sql.model.MutationTarget;
|
||||
import org.hibernate.sql.model.ast.MutatingTableReference;
|
||||
import org.hibernate.sql.model.ast.TableDelete;
|
||||
import org.hibernate.sql.model.internal.TableDeleteCustomSql;
|
||||
import org.hibernate.sql.model.internal.TableDeleteStandard;
|
||||
|
||||
/**
|
||||
* Custom table delete builder for many-to-many collection join tables that handles row deletes
|
||||
*
|
||||
* @author Marco Belladelli
|
||||
*/
|
||||
public class CollectionRowDeleteBuilder extends TableDeleteBuilderStandard {
|
||||
public CollectionRowDeleteBuilder(
|
||||
MutationTarget<?> mutationTarget,
|
||||
MutatingTableReference tableReference,
|
||||
SessionFactoryImplementor sessionFactory,
|
||||
String whereFragment) {
|
||||
super( mutationTarget, tableReference, sessionFactory, whereFragment );
|
||||
assert tableReference.getTableMapping() instanceof CollectionTableMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDelete buildMutation() {
|
||||
final CollectionTableMapping tableMapping = (CollectionTableMapping) getMutatingTable().getTableMapping();
|
||||
if ( tableMapping.getDeleteRowDetails().getCustomSql() != null ) {
|
||||
return new TableDeleteCustomSql(
|
||||
getMutatingTable(),
|
||||
getMutationTarget(),
|
||||
getSqlComment(),
|
||||
getKeyRestrictionBindings(),
|
||||
getOptimisticLockBindings(),
|
||||
getParameters()
|
||||
) {
|
||||
@Override
|
||||
public String getCustomSql() {
|
||||
return tableMapping.getDeleteRowDetails().getCustomSql();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCallable() {
|
||||
return tableMapping.getDeleteRowDetails().isCallable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expectation getExpectation() {
|
||||
return tableMapping.getDeleteRowDetails().getExpectation();
|
||||
}
|
||||
};
|
||||
}
|
||||
return new TableDeleteStandard(
|
||||
getMutatingTable(),
|
||||
getMutationTarget(),
|
||||
getSqlComment(),
|
||||
getKeyRestrictionBindings(),
|
||||
getOptimisticLockBindings(),
|
||||
getParameters(),
|
||||
getWhereFragment()
|
||||
) {
|
||||
@Override
|
||||
public Expectation getExpectation() {
|
||||
return tableMapping.getDeleteRowDetails().getExpectation();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* 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 java.util.List;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.jdbc.Expectation;
|
||||
import org.hibernate.persister.collection.mutation.CollectionTableMapping;
|
||||
import org.hibernate.sql.model.MutationOperation;
|
||||
import org.hibernate.sql.model.MutationTarget;
|
||||
import org.hibernate.sql.model.ast.ColumnValueBinding;
|
||||
import org.hibernate.sql.model.ast.MutatingTableReference;
|
||||
import org.hibernate.sql.model.ast.RestrictedTableMutation;
|
||||
import org.hibernate.sql.model.internal.TableUpdateCustomSql;
|
||||
import org.hibernate.sql.model.internal.TableUpdateStandard;
|
||||
|
||||
/**
|
||||
* Custom table update builder for one-to-many collections that handles row deletes
|
||||
*
|
||||
* @author Marco Belladelli
|
||||
*/
|
||||
public class CollectionRowDeleteByUpdateSetNullBuilder<O extends MutationOperation> extends TableUpdateBuilderStandard<O> {
|
||||
public CollectionRowDeleteByUpdateSetNullBuilder(
|
||||
MutationTarget<?> mutationTarget,
|
||||
MutatingTableReference tableReference,
|
||||
SessionFactoryImplementor sessionFactory,
|
||||
String whereFragment) {
|
||||
super( mutationTarget, tableReference, sessionFactory, whereFragment );
|
||||
assert tableReference.getTableMapping() instanceof CollectionTableMapping;
|
||||
}
|
||||
|
||||
@SuppressWarnings( "unchecked" )
|
||||
@Override
|
||||
public RestrictedTableMutation<O> buildMutation() {
|
||||
final CollectionTableMapping tableMapping = (CollectionTableMapping) getMutatingTable().getTableMapping();
|
||||
final List<ColumnValueBinding> valueBindings = combine(
|
||||
getValueBindings(),
|
||||
getKeyBindings(),
|
||||
getLobValueBindings()
|
||||
);
|
||||
if ( tableMapping.getDeleteRowDetails().getCustomSql() != null ) {
|
||||
return (RestrictedTableMutation<O>) new TableUpdateCustomSql(
|
||||
getMutatingTable(),
|
||||
getMutationTarget(),
|
||||
getSqlComment(),
|
||||
valueBindings,
|
||||
getKeyRestrictionBindings(),
|
||||
getOptimisticLockBindings()
|
||||
) {
|
||||
@Override
|
||||
public String getCustomSql() {
|
||||
return tableMapping.getDeleteRowDetails().getCustomSql();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCallable() {
|
||||
return tableMapping.getDeleteRowDetails().isCallable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expectation getExpectation() {
|
||||
return tableMapping.getDeleteRowDetails().getExpectation();
|
||||
}
|
||||
};
|
||||
}
|
||||
return (RestrictedTableMutation<O>) new TableUpdateStandard(
|
||||
getMutatingTable(),
|
||||
getMutationTarget(),
|
||||
getSqlComment(),
|
||||
valueBindings,
|
||||
getKeyRestrictionBindings(),
|
||||
getOptimisticLockBindings(),
|
||||
getWhereFragment(),
|
||||
null
|
||||
) {
|
||||
@Override
|
||||
public Expectation getExpectation() {
|
||||
return tableMapping.getDeleteRowDetails().getExpectation();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -67,6 +67,10 @@ public class TableDeleteBuilderStandard
|
|||
this.sqlComment = sqlComment;
|
||||
}
|
||||
|
||||
public String getWhereFragment() {
|
||||
return whereFragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWhere(String fragment) {
|
||||
if ( isCustomSql && fragment != null ) {
|
||||
|
|
|
@ -54,6 +54,10 @@ public class TableUpdateBuilderStandard<O extends MutationOperation> extends Abs
|
|||
this.whereFragment = whereFragment;
|
||||
}
|
||||
|
||||
public String getWhereFragment() {
|
||||
return whereFragment;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public RestrictedTableMutation<O> buildMutation() {
|
||||
|
|
Loading…
Reference in New Issue