org.hibernate.hql.spi.id.cte.CteValuesListDeleteHandlerImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-core Show documentation
Show all versions of hibernate-core Show documentation
Hibernate's core ORM functionality
/*
* 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.hql.spi.id.cte;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.hql.internal.ast.HqlSqlWalker;
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
import org.hibernate.persister.collection.AbstractCollectionPersister;
import org.hibernate.sql.Delete;
import org.hibernate.type.CollectionType;
import org.hibernate.type.Type;
/**
* Bulk-id delete handler that uses CTE and VALUES lists.
*
* @author Evandro Pires da Silva
* @author Vlad Mihalcea
*/
public class CteValuesListDeleteHandlerImpl
extends AbstractCteValuesListBulkIdHandler
implements MultiTableBulkIdStrategy.DeleteHandler {
private final List deletes = new ArrayList<>();
public CteValuesListDeleteHandlerImpl(
SessionFactoryImplementor factory,
HqlSqlWalker walker) {
this( factory, walker, null, null );
}
public CteValuesListDeleteHandlerImpl(
SessionFactoryImplementor factory,
HqlSqlWalker walker,
String catalog,
String schema) {
super( factory, walker, catalog, schema );
final String idSubselect = generateIdSubselect( getTargetedQueryable() );
for ( Type type : getTargetedQueryable().getPropertyTypes() ) {
if ( type.isCollectionType() ) {
CollectionType cType = (CollectionType) type;
AbstractCollectionPersister cPersister = (AbstractCollectionPersister) factory.getMetamodel().collectionPersister( cType.getRole() );
if ( cPersister.isManyToMany() ) {
deletes.add( generateDelete(
cPersister.getTableName(),
cPersister.getKeyColumnNames(),
idSubselect,
"bulk delete - m2m join table cleanup"
) );
}
}
}
String[] tableNames = getTargetedQueryable().getConstraintOrderedTableNameClosure();
String[][] columnNames = getTargetedQueryable().getContraintOrderedTableKeyColumnClosure();
for ( int i = 0; i < tableNames.length; i++ ) {
// TODO : an optimization here would be to consider cascade deletes and not gen those delete statements;
// the difficulty is the ordering of the tables here vs the cascade attributes on the persisters ->
// the table info gotten here should really be self-contained (i.e., a class representation
// defining all the needed attributes), then we could then get an array of those
deletes.add( generateDelete( tableNames[i], columnNames[i], idSubselect, "bulk delete" ) );
}
}
@Override
public int execute(
SharedSessionContractImplementor session,
QueryParameters queryParameters) {
CteValuesListBuilder values = prepareCteStatement( session, queryParameters );
if ( !values.getIds().isEmpty() ) {
// Start performing the deletes
for ( String deleteSuffix : deletes ) {
if ( deleteSuffix == null) {
continue;
}
String delete = values.toStatement( deleteSuffix );
try {
try ( PreparedStatement ps = session
.getJdbcCoordinator().getStatementPreparer()
.prepareStatement( delete, false ) ) {
int pos = 1;
for ( Object[] result : values.getIds() ) {
for ( Object column : result ) {
ps.setObject( pos++, column );
}
}
session
.getJdbcCoordinator().getResultSetReturn()
.executeUpdate( ps );
}
}
catch ( SQLException e ) {
throw convert( e, "error performing bulk delete", delete );
}
}
}
return values.getIds().size();
}
private String generateDelete(
String tableName,
String[] columnNames,
String idSubselect,
String comment) {
final Delete delete = new Delete().setTableName( tableName ).setWhere(
"(" + String.join( ", ", (CharSequence[]) columnNames ) + ") in ("
+ idSubselect + ")" );
if ( factory().getSessionFactoryOptions().isCommentsEnabled() ) {
delete.setComment( comment );
}
return delete.toStatementString();
}
@Override
public String[] getSqlStatements() {
return deletes.toArray( new String[deletes.size()] );
}
}