HHH-11262 - Bulk Operations attempt to create temporary tables, but user does not have permission to create table
This commit is contained in:
parent
8f6e4d5675
commit
d48f393420
|
@ -2865,6 +2865,24 @@ public abstract class Dialect implements ConversionContext {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this dialect/database support non-query statements (e.g. INSERT, UPDATE, DELETE) with CTE (Common Table Expressions)?
|
||||
*
|
||||
* @return {@code true} if non-query statements are supported with CTE
|
||||
*/
|
||||
public boolean supportsNonQueryWithCTE() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this dialect/database support VALUES list (e.g. VALUES (1), (2), (3) )
|
||||
*
|
||||
* @return {@code true} if VALUES list are supported
|
||||
*/
|
||||
public boolean supportsValuesList() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isLegacyLimitHandlerBehaviorEnabled() {
|
||||
return legacyLimitHandlerBehavior;
|
||||
}
|
||||
|
|
|
@ -65,4 +65,12 @@ public class MySQL57InnoDBDialect extends MySQL5InnoDBDialect {
|
|||
// from_unixtime(), timestamp() are functions that return TIMESTAMP that do not support a
|
||||
// fractional seconds precision argument (so there's no need to override them here):
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href="https://dev.mysql.com/worklog/task/?id=7019">MySQL 5.7 work log</a>
|
||||
* @return supports IN clause row value expressions
|
||||
*/
|
||||
public boolean supportsRowValueConstructorSyntaxInInList() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,4 +56,13 @@ public class PostgreSQL82Dialect extends PostgreSQL81Dialect {
|
|||
public String getDropSequenceString(String sequenceName) {
|
||||
return "drop sequence if exists " + sequenceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsValuesList() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean supportsRowValueConstructorSyntaxInInList() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,4 +17,9 @@ public class PostgreSQL91Dialect extends PostgreSQL9Dialect {
|
|||
public boolean supportsPartitionBy() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsNonQueryWithCTE() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,4 +105,9 @@ public class SQLServer2005Dialect extends SQLServerDialect {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsNonQueryWithCTE() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,4 +56,9 @@ public class SQLServer2008Dialect extends SQLServer2005Dialect {
|
|||
|
||||
return orderByElement.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsValuesList() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.hql.spi.id;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.JDBCException;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
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.internal.ast.tree.AbstractRestrictableStatement;
|
||||
import org.hibernate.hql.internal.ast.tree.FromElement;
|
||||
import org.hibernate.param.ParameterSpecification;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
|
||||
/**
|
||||
* Base class for all strategies that select the ids to be updated/deleted prior to executing the update/delete operation.
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public abstract class AbstractIdsBulkIdHandler
|
||||
extends AbstractTableBasedBulkIdHandler {
|
||||
|
||||
private final Queryable targetedPersister;
|
||||
|
||||
private final String idSelect;
|
||||
private final List<ParameterSpecification> idSelectParameterSpecifications;
|
||||
|
||||
public AbstractIdsBulkIdHandler(
|
||||
SessionFactoryImplementor sessionFactory, HqlSqlWalker walker) {
|
||||
super(sessionFactory, walker);
|
||||
|
||||
final AbstractRestrictableStatement statement = (AbstractRestrictableStatement) walker.getAST();
|
||||
final FromElement fromElement = statement.getFromClause().getFromElement();
|
||||
|
||||
this.targetedPersister = fromElement.getQueryable();
|
||||
|
||||
final ProcessedWhereClause processedWhereClause = processWhereClause( statement.getWhereClause() );
|
||||
this.idSelectParameterSpecifications = processedWhereClause.getIdSelectParameterSpecifications();
|
||||
|
||||
final String bulkTargetAlias = fromElement.getTableAlias();
|
||||
|
||||
this.idSelect = generateIdSelect( bulkTargetAlias, processedWhereClause ).toStatementString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Queryable getTargetedQueryable() {
|
||||
return targetedPersister;
|
||||
}
|
||||
|
||||
protected Dialect dialect() {
|
||||
return factory().getServiceRegistry().getService( JdbcServices.class ).getDialect();
|
||||
}
|
||||
|
||||
protected JDBCException convert(
|
||||
SQLException e,
|
||||
String message,
|
||||
String sql) {
|
||||
throw factory().getServiceRegistry().getService( JdbcServices.class ).getSqlExceptionHelper().convert( e, message, sql );
|
||||
}
|
||||
|
||||
protected List<Object[]> selectIds(
|
||||
SharedSessionContractImplementor session,
|
||||
QueryParameters queryParameters) {
|
||||
List<Object[]> ids = new ArrayList<>();
|
||||
try {
|
||||
try (PreparedStatement ps = session.getJdbcCoordinator()
|
||||
.getStatementPreparer()
|
||||
.prepareStatement( idSelect, false )) {
|
||||
int position = 1;
|
||||
for ( ParameterSpecification parameterSpecification : idSelectParameterSpecifications ) {
|
||||
position += parameterSpecification.bind( ps, queryParameters, session, position );
|
||||
}
|
||||
|
||||
ResultSet rs = session
|
||||
.getJdbcCoordinator()
|
||||
.getResultSetReturn()
|
||||
.extract( ps );
|
||||
while ( rs.next() ) {
|
||||
Object[] result = new Object[targetedPersister.getIdentifierColumnNames().length];
|
||||
for ( String columnName : targetedPersister.getIdentifierColumnNames() ) {
|
||||
Object column = rs.getObject( columnName );
|
||||
result[rs.findColumn( columnName ) - 1] = column;
|
||||
}
|
||||
ids.add( result );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( SQLException e ) {
|
||||
throw convert( e, "could not select ids for bulk operation", idSelect );
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
}
|
|
@ -127,6 +127,22 @@ public abstract class AbstractTableBasedBulkIdHandler {
|
|||
IdTableInfo idTableInfo,
|
||||
ProcessedWhereClause whereClause) {
|
||||
|
||||
final Dialect dialect = sessionFactory.getJdbcServices().getJdbcEnvironment().getDialect();
|
||||
final Select select = generateIdSelect( tableAlias, whereClause );
|
||||
|
||||
InsertSelect insert = new InsertSelect( dialect );
|
||||
if ( sessionFactory.getSessionFactoryOptions().isCommentsEnabled() ) {
|
||||
insert.setComment( "insert-select for " + getTargetedQueryable().getEntityName() + " ids" );
|
||||
}
|
||||
insert.setTableName( idTableInfo.getQualifiedIdTableName() );
|
||||
insert.setSelect( select );
|
||||
return insert.toStatementString();
|
||||
}
|
||||
|
||||
protected Select generateIdSelect(
|
||||
String tableAlias,
|
||||
ProcessedWhereClause whereClause) {
|
||||
|
||||
final Dialect dialect = sessionFactory.getJdbcServices().getJdbcEnvironment().getDialect();
|
||||
|
||||
final Select select = new Select( dialect );
|
||||
|
@ -160,14 +176,7 @@ public abstract class AbstractTableBasedBulkIdHandler {
|
|||
}
|
||||
}
|
||||
select.setWhereClause( whereJoinFragment + whereClause.getUserWhereClauseFragment() );
|
||||
|
||||
InsertSelect insert = new InsertSelect( dialect );
|
||||
if ( sessionFactory.getSessionFactoryOptions().isCommentsEnabled() ) {
|
||||
insert.setComment( "insert-select for " + getTargetedQueryable().getEntityName() + " ids" );
|
||||
}
|
||||
insert.setTableName( idTableInfo.getQualifiedIdTableName() );
|
||||
insert.setSelect( select );
|
||||
return insert.toStatementString();
|
||||
return select;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -114,10 +114,10 @@ public class TableBasedDeleteHandlerImpl
|
|||
try {
|
||||
try {
|
||||
ps = session.getJdbcCoordinator().getStatementPreparer().prepareStatement( idInsertSelect, false );
|
||||
int pos = 1;
|
||||
pos += handlePrependedParametersOnIdSelection( ps, session, pos );
|
||||
int position = 1;
|
||||
position += handlePrependedParametersOnIdSelection( ps, session, position );
|
||||
for ( ParameterSpecification parameterSpecification : idSelectParameterSpecifications ) {
|
||||
pos += parameterSpecification.bind( ps, queryParameters, session, pos );
|
||||
position += parameterSpecification.bind( ps, queryParameters, session, position );
|
||||
}
|
||||
resultCount = session.getJdbcCoordinator().getResultSetReturn().executeUpdate( ps );
|
||||
}
|
||||
|
|
|
@ -116,10 +116,10 @@ public class TableBasedUpdateHandlerImpl
|
|||
try {
|
||||
try {
|
||||
ps = session.getJdbcCoordinator().getStatementPreparer().prepareStatement( idInsertSelect, false );
|
||||
int sum = 1;
|
||||
sum += handlePrependedParametersOnIdSelection( ps, session, sum );
|
||||
int position = 1;
|
||||
position += handlePrependedParametersOnIdSelection( ps, session, position );
|
||||
for ( ParameterSpecification parameterSpecification : idSelectParameterSpecifications ) {
|
||||
sum += parameterSpecification.bind( ps, queryParameters, session, sum );
|
||||
position += parameterSpecification.bind( ps, queryParameters, session, position );
|
||||
}
|
||||
resultCount = session.getJdbcCoordinator().getResultSetReturn().executeUpdate( ps );
|
||||
}
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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.hql.spi.id.cte;
|
||||
|
||||
import org.hibernate.boot.model.naming.Identifier;
|
||||
import org.hibernate.boot.model.relational.QualifiedTableName;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
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.AbstractIdsBulkIdHandler;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
|
||||
/**
|
||||
* Defines how identifier values are selected from the updatable/deletable tables.
|
||||
*
|
||||
* @author Evandro Pires da Silva
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public abstract class AbstractCteValuesListBulkIdHandler extends
|
||||
AbstractIdsBulkIdHandler {
|
||||
|
||||
private final String catalog;
|
||||
private final String schema;
|
||||
|
||||
private final JdbcEnvironment jdbcEnvironment;
|
||||
|
||||
public AbstractCteValuesListBulkIdHandler(
|
||||
SessionFactoryImplementor sessionFactory, HqlSqlWalker walker,
|
||||
String catalog, String schema) {
|
||||
super( sessionFactory, walker );
|
||||
Dialect dialect = sessionFactory.getServiceRegistry().getService( JdbcServices.class ).getDialect();
|
||||
if ( !dialect.supportsNonQueryWithCTE() ) {
|
||||
throw new UnsupportedOperationException(
|
||||
"The " + getClass().getSimpleName() +
|
||||
" can only be used with Dialects that support CTE that can take UPDATE or DELETE statements as well!"
|
||||
);
|
||||
}
|
||||
if ( !dialect.supportsValuesList() ) {
|
||||
throw new UnsupportedOperationException(
|
||||
"The " + getClass().getSimpleName() +
|
||||
" can only be used with Dialects that support VALUES lists!"
|
||||
);
|
||||
}
|
||||
if ( !dialect.supportsRowValueConstructorSyntaxInInList() ) {
|
||||
throw new UnsupportedOperationException(
|
||||
"The " + getClass().getSimpleName() +
|
||||
" can only be used with Dialects that support IN clause row-value expressions (for composite identifiers)!"
|
||||
);
|
||||
}
|
||||
|
||||
this.jdbcEnvironment = sessionFactory.getServiceRegistry().getService(
|
||||
JdbcServices.class ).getJdbcEnvironment();
|
||||
this.catalog = catalog;
|
||||
this.schema = schema;
|
||||
}
|
||||
|
||||
protected String determineIdTableName(Queryable persister) {
|
||||
return jdbcEnvironment.getQualifiedObjectNameFormatter().format(
|
||||
new QualifiedTableName(
|
||||
Identifier.toIdentifier( catalog ),
|
||||
Identifier.toIdentifier( schema ),
|
||||
Identifier.toIdentifier( "HT_" + persister.getTableName() )
|
||||
),
|
||||
jdbcEnvironment.getDialect()
|
||||
);
|
||||
}
|
||||
|
||||
protected String generateIdSubselect(Queryable persister) {
|
||||
return new StringBuilder()
|
||||
.append( "select " )
|
||||
.append( String.join(
|
||||
", ",
|
||||
(CharSequence[]) persister.getIdentifierColumnNames()
|
||||
) )
|
||||
.append( " from " )
|
||||
.append( determineIdTableName( persister ) )
|
||||
.toString();
|
||||
}
|
||||
|
||||
protected CteValuesListBuilder prepareCteStatement(
|
||||
SharedSessionContractImplementor session,
|
||||
QueryParameters queryParameters) {
|
||||
|
||||
return new CteValuesListBuilder(
|
||||
determineIdTableName( getTargetedQueryable() ),
|
||||
getTargetedQueryable().getIdentifierColumnNames(),
|
||||
selectIds( session, queryParameters )
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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.hql.spi.id.cte;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Builds the CTE with VALUES list clause that wraps the identifiers to be updated/deleted.
|
||||
*
|
||||
* @author Evandro Pires da Silva
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class CteValuesListBuilder {
|
||||
|
||||
private final String tableName;
|
||||
|
||||
private final String[] columns;
|
||||
|
||||
private final List<Object[]> ids;
|
||||
|
||||
private String cteStatement;
|
||||
|
||||
public CteValuesListBuilder(
|
||||
String tableName,
|
||||
String[] columns,
|
||||
List<Object[]> ids) {
|
||||
this.tableName = tableName;
|
||||
this.columns = columns;
|
||||
this.ids = ids;
|
||||
|
||||
this.cteStatement = buildStatement();
|
||||
}
|
||||
|
||||
public List<Object[]> getIds() {
|
||||
return ids;
|
||||
}
|
||||
|
||||
public String toStatement(String statement) {
|
||||
return cteStatement + statement;
|
||||
}
|
||||
|
||||
private String buildStatement() {
|
||||
String columnNames = String.join(",", columns);
|
||||
|
||||
String singleIdValuesParam = '(' + String.join( ",", Collections.nCopies( columns.length, "?")) + ')';
|
||||
String parameters = String.join(",", Collections.nCopies(ids.size(), singleIdValuesParam));
|
||||
|
||||
return new StringBuilder()
|
||||
.append( "with " )
|
||||
.append( tableName )
|
||||
.append( " (" )
|
||||
.append( columnNames )
|
||||
.append( " ) as ( select " )
|
||||
.append( columnNames )
|
||||
.append( " from ( values " )
|
||||
.append( parameters )
|
||||
.append( ") as HT " )
|
||||
.append( "(" )
|
||||
.append( columnNames )
|
||||
.append( ") ) " )
|
||||
.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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.hql.spi.id.cte;
|
||||
|
||||
import org.hibernate.boot.spi.MetadataImplementor;
|
||||
import org.hibernate.boot.spi.SessionFactoryOptions;
|
||||
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
|
||||
/**
|
||||
* This bulk-id strategy uses a CTE with a VALUE list to hold the identifiers,
|
||||
* which are later used by the update or delete statement:
|
||||
*
|
||||
* <pre>
|
||||
* with HT_Person (id ) as (
|
||||
* select
|
||||
* id
|
||||
* from (
|
||||
* values
|
||||
* (?),
|
||||
* (?),
|
||||
* (?)
|
||||
* (?)
|
||||
* ) as HT (id)
|
||||
* )
|
||||
* delete
|
||||
* from
|
||||
* Person
|
||||
* where
|
||||
* ( id ) in (
|
||||
* select
|
||||
* id
|
||||
* from
|
||||
* HT_Person
|
||||
* )
|
||||
* </pre>
|
||||
*
|
||||
* @author Evandro Pires da Silva
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class CteValuesListBulkIdStrategy
|
||||
implements MultiTableBulkIdStrategy {
|
||||
|
||||
public static final CteValuesListBulkIdStrategy INSTANCE = new CteValuesListBulkIdStrategy();
|
||||
|
||||
@Override
|
||||
public void prepare(
|
||||
JdbcServices jdbcServices,
|
||||
JdbcConnectionAccess jdbcConnectionAccess,
|
||||
MetadataImplementor metadataImplementor,
|
||||
SessionFactoryOptions sessionFactoryOptions) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(
|
||||
JdbcServices jdbcServices,
|
||||
JdbcConnectionAccess connectionAccess) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateHandler buildUpdateHandler(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
return new CteValuesListUpdateHandlerImpl( factory, walker );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeleteHandler buildDeleteHandler(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
return new CteValuesListDeleteHandlerImpl( factory, walker );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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.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<String> 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()] );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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.hql.spi.id.cte;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
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.internal.ast.tree.AssignmentSpecification;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.param.ParameterSpecification;
|
||||
import org.hibernate.sql.Update;
|
||||
|
||||
/**
|
||||
* Bulk-id update handler that uses CTE and VALUES lists.
|
||||
*
|
||||
* @author Evandro Pires da Silva
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class CteValuesListUpdateHandlerImpl
|
||||
extends AbstractCteValuesListBulkIdHandler
|
||||
implements MultiTableBulkIdStrategy.UpdateHandler {
|
||||
|
||||
private final String[] updates;
|
||||
private final ParameterSpecification[][] assignmentParameterSpecifications;
|
||||
|
||||
public CteValuesListUpdateHandlerImpl(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
this( factory, walker, null, null );
|
||||
}
|
||||
|
||||
public CteValuesListUpdateHandlerImpl(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker,
|
||||
String catalog,
|
||||
String schema) {
|
||||
super( factory, walker, catalog, schema );
|
||||
|
||||
String[] tableNames = getTargetedQueryable().getConstraintOrderedTableNameClosure();
|
||||
String[][] columnNames = getTargetedQueryable().getContraintOrderedTableKeyColumnClosure();
|
||||
String idSubselect = generateIdSubselect( getTargetedQueryable() );
|
||||
|
||||
updates = new String[tableNames.length];
|
||||
assignmentParameterSpecifications = new ParameterSpecification[tableNames.length][];
|
||||
for ( int tableIndex = 0; tableIndex < tableNames.length; tableIndex++ ) {
|
||||
boolean affected = false;
|
||||
final List<ParameterSpecification> parameterList = new ArrayList<>();
|
||||
final Update update = new Update( factory.getServiceRegistry().getService( JdbcServices.class ).getDialect() )
|
||||
.setTableName( tableNames[tableIndex] )
|
||||
.setWhere( "(" + String.join( ", ", (CharSequence[]) columnNames[tableIndex] ) + ") in (" + idSubselect + ")" );
|
||||
if ( factory().getSessionFactoryOptions().isCommentsEnabled() ) {
|
||||
update.setComment( "bulk update" );
|
||||
}
|
||||
final List<AssignmentSpecification> assignmentSpecifications = walker.getAssignmentSpecifications();
|
||||
for ( AssignmentSpecification assignmentSpecification : assignmentSpecifications ) {
|
||||
if ( assignmentSpecification.affectsTable( tableNames[tableIndex] ) ) {
|
||||
affected = true;
|
||||
update.appendAssignmentFragment( assignmentSpecification.getSqlAssignmentFragment() );
|
||||
if ( assignmentSpecification.getParameters() != null ) {
|
||||
Collections.addAll( parameterList, assignmentSpecification.getParameters() );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( affected ) {
|
||||
updates[tableIndex] = update.toStatementString();
|
||||
assignmentParameterSpecifications[tableIndex] = parameterList.toArray( new ParameterSpecification[parameterList.size()] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSqlStatements() {
|
||||
return updates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int execute(
|
||||
SharedSessionContractImplementor session,
|
||||
QueryParameters queryParameters) {
|
||||
|
||||
CteValuesListBuilder values = prepareCteStatement( session, queryParameters );
|
||||
|
||||
if ( !values.getIds().isEmpty() ) {
|
||||
|
||||
// Start performing the updates
|
||||
for ( int i = 0; i < updates.length; i++ ) {
|
||||
String updateSuffix = updates[i];
|
||||
if ( updateSuffix == null) {
|
||||
continue;
|
||||
}
|
||||
String update = values.toStatement( updateSuffix );
|
||||
try {
|
||||
try (PreparedStatement ps = session
|
||||
.getJdbcCoordinator().getStatementPreparer()
|
||||
.prepareStatement( update, false )) {
|
||||
int position = 1; // jdbc params are 1-based
|
||||
for ( Object[] result : values.getIds() ) {
|
||||
for ( Object column : result ) {
|
||||
ps.setObject( position++, column );
|
||||
}
|
||||
}
|
||||
if ( assignmentParameterSpecifications[i] != null ) {
|
||||
for ( int x = 0; x < assignmentParameterSpecifications[i].length; x++ ) {
|
||||
position += assignmentParameterSpecifications[i][x]
|
||||
.bind( ps, queryParameters, session,
|
||||
position
|
||||
);
|
||||
}
|
||||
}
|
||||
session
|
||||
.getJdbcCoordinator().getResultSetReturn()
|
||||
.executeUpdate( ps );
|
||||
}
|
||||
}
|
||||
catch ( SQLException e ) {
|
||||
throw convert(
|
||||
e,
|
||||
"error performing bulk update",
|
||||
update
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return values.getIds().size();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.hql.spi.id.inline;
|
||||
|
||||
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.AbstractIdsBulkIdHandler;
|
||||
|
||||
/**
|
||||
* Base class for all bulk-id strategy handlers that inline the identifiers of the updatable/deletable rows.
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public abstract class AbstractInlineIdsBulkIdHandler
|
||||
extends AbstractIdsBulkIdHandler {
|
||||
|
||||
public AbstractInlineIdsBulkIdHandler(
|
||||
SessionFactoryImplementor sessionFactory,
|
||||
HqlSqlWalker walker) {
|
||||
super( sessionFactory, walker );
|
||||
}
|
||||
|
||||
protected IdsClauseBuilder prepareInlineStatement(
|
||||
SharedSessionContractImplementor session,
|
||||
QueryParameters queryParameters) {
|
||||
return newIdsClauseBuilder( selectIds( session, queryParameters ) );
|
||||
}
|
||||
|
||||
protected abstract IdsClauseBuilder newIdsClauseBuilder(List<Object[]> ids);
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* 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.hql.spi.id.inline;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Inline bulk-id delete handler that selects the identifiers of the rows to be updated.
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public abstract class AbstractInlineIdsDeleteHandlerImpl
|
||||
extends AbstractInlineIdsBulkIdHandler
|
||||
implements MultiTableBulkIdStrategy.DeleteHandler {
|
||||
|
||||
private final List<String> deletes = new ArrayList<>();
|
||||
|
||||
public AbstractInlineIdsDeleteHandlerImpl(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
super( factory, walker );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSqlStatements() {
|
||||
return deletes.toArray( new String[deletes.size()] );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int execute(
|
||||
SharedSessionContractImplementor session,
|
||||
QueryParameters queryParameters) {
|
||||
|
||||
IdsClauseBuilder values = prepareInlineStatement( session, queryParameters );
|
||||
|
||||
if ( !values.getIds().isEmpty() ) {
|
||||
final String idSubselect = values.toStatement();
|
||||
|
||||
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"
|
||||
).toStatementString() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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" ).toStatementString() );
|
||||
}
|
||||
|
||||
// Start performing the deletes
|
||||
for ( String delete : deletes ) {
|
||||
if ( delete == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
try ( PreparedStatement ps = session
|
||||
.getJdbcCoordinator().getStatementPreparer()
|
||||
.prepareStatement( delete, false ) ) {
|
||||
session
|
||||
.getJdbcCoordinator().getResultSetReturn()
|
||||
.executeUpdate( ps );
|
||||
}
|
||||
}
|
||||
catch ( SQLException e ) {
|
||||
throw convert( e, "error performing bulk delete", delete );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return values.getIds().size();
|
||||
}
|
||||
|
||||
protected Delete 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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.hql.spi.id.inline;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
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.internal.ast.tree.AssignmentSpecification;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.param.ParameterSpecification;
|
||||
import org.hibernate.sql.Update;
|
||||
|
||||
/**
|
||||
* Inline bulk-id delete handler that selects the identifiers of the rows to be deleted.
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public abstract class AbstractInlineIdsUpdateHandlerImpl
|
||||
extends AbstractInlineIdsBulkIdHandler
|
||||
implements MultiTableBulkIdStrategy.UpdateHandler {
|
||||
|
||||
private final Map<Integer, String> updates = new LinkedHashMap<>();
|
||||
|
||||
private ParameterSpecification[][] assignmentParameterSpecifications;
|
||||
|
||||
public AbstractInlineIdsUpdateHandlerImpl(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
super( factory, walker );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSqlStatements() {
|
||||
return updates.values().toArray( new String[updates.values().size()] );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int execute(
|
||||
SharedSessionContractImplementor session,
|
||||
QueryParameters queryParameters) {
|
||||
|
||||
IdsClauseBuilder values = prepareInlineStatement( session, queryParameters );
|
||||
|
||||
if ( !values.getIds().isEmpty() ) {
|
||||
|
||||
String[] tableNames = getTargetedQueryable().getConstraintOrderedTableNameClosure();
|
||||
String[][] columnNames = getTargetedQueryable().getContraintOrderedTableKeyColumnClosure();
|
||||
|
||||
String idSubselect = values.toStatement();
|
||||
|
||||
assignmentParameterSpecifications = new ParameterSpecification[tableNames.length][];
|
||||
for ( int tableIndex = 0; tableIndex < tableNames.length; tableIndex++ ) {
|
||||
boolean affected = false;
|
||||
final List<ParameterSpecification> parameterList = new ArrayList<>();
|
||||
|
||||
Update update = generateUpdate( tableNames[tableIndex], columnNames[tableIndex], idSubselect, "bulk update" );
|
||||
|
||||
final List<AssignmentSpecification> assignmentSpecifications = walker().getAssignmentSpecifications();
|
||||
for ( AssignmentSpecification assignmentSpecification : assignmentSpecifications ) {
|
||||
if ( assignmentSpecification.affectsTable( tableNames[tableIndex] ) ) {
|
||||
affected = true;
|
||||
update.appendAssignmentFragment( assignmentSpecification.getSqlAssignmentFragment() );
|
||||
if ( assignmentSpecification.getParameters() != null ) {
|
||||
Collections.addAll( parameterList, assignmentSpecification.getParameters() );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( affected ) {
|
||||
updates.put( tableIndex, update.toStatementString() );
|
||||
assignmentParameterSpecifications[tableIndex] = parameterList.toArray( new ParameterSpecification[parameterList.size()] );
|
||||
}
|
||||
}
|
||||
|
||||
// Start performing the updates
|
||||
for ( Map.Entry<Integer, String> updateEntry: updates.entrySet()) {
|
||||
int i = updateEntry.getKey();
|
||||
String update = updateEntry.getValue();
|
||||
|
||||
if ( update == null) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
try (PreparedStatement ps = session
|
||||
.getJdbcCoordinator().getStatementPreparer()
|
||||
.prepareStatement( update, false )) {
|
||||
int position = 1; // jdbc params are 1-based
|
||||
if ( assignmentParameterSpecifications[i] != null ) {
|
||||
for ( int x = 0; x < assignmentParameterSpecifications[i].length; x++ ) {
|
||||
position += assignmentParameterSpecifications[i][x]
|
||||
.bind( ps, queryParameters, session,
|
||||
position
|
||||
);
|
||||
}
|
||||
}
|
||||
session
|
||||
.getJdbcCoordinator().getResultSetReturn()
|
||||
.executeUpdate( ps );
|
||||
}
|
||||
}
|
||||
catch ( SQLException e ) {
|
||||
throw convert(
|
||||
e,
|
||||
"error performing bulk update",
|
||||
update
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return values.getIds().size();
|
||||
}
|
||||
|
||||
protected Update generateUpdate(
|
||||
String tableName,
|
||||
String[] columnNames,
|
||||
String idSubselect,
|
||||
String comment) {
|
||||
final Update update = new Update( factory().getServiceRegistry().getService( JdbcServices.class ).getDialect() )
|
||||
.setTableName( tableName )
|
||||
.setWhere( "(" + String.join( ", ", (CharSequence[]) columnNames ) + ") in (" + idSubselect + ")" );
|
||||
if ( factory().getSessionFactoryOptions().isCommentsEnabled() ) {
|
||||
update.setComment( comment );
|
||||
}
|
||||
return update;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package org.hibernate.hql.spi.id.inline;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.type.CompositeType;
|
||||
import org.hibernate.type.LiteralType;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.type.TypeResolver;
|
||||
|
||||
/**
|
||||
* Builds the where clause that wraps the identifiers to be updated/deleted.
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public abstract class IdsClauseBuilder {
|
||||
|
||||
private final Dialect dialect;
|
||||
|
||||
private final Type identifierType;
|
||||
|
||||
private final TypeResolver typeResolver;
|
||||
|
||||
private final String[] columns;
|
||||
|
||||
private final List<Object[]> ids;
|
||||
|
||||
protected IdsClauseBuilder(
|
||||
Dialect dialect,
|
||||
Type identifierType,
|
||||
TypeResolver typeResolver,
|
||||
String[] columns,
|
||||
List<Object[]> ids) {
|
||||
this.dialect = dialect;
|
||||
this.identifierType = identifierType;
|
||||
this.typeResolver = typeResolver;
|
||||
this.columns = columns;
|
||||
this.ids = ids;
|
||||
}
|
||||
|
||||
public Type getIdentifierType() {
|
||||
return identifierType;
|
||||
}
|
||||
|
||||
public TypeResolver getTypeResolver() {
|
||||
return typeResolver;
|
||||
}
|
||||
|
||||
protected String[] getColumns() {
|
||||
return columns;
|
||||
}
|
||||
|
||||
public List<Object[]> getIds() {
|
||||
return ids;
|
||||
}
|
||||
|
||||
public abstract String toStatement();
|
||||
|
||||
protected String quoteIdentifier(Object... value) {
|
||||
if ( value.length == 1 ) {
|
||||
return quoteIdentifier( value[0], identifierType );
|
||||
}
|
||||
else {
|
||||
if ( identifierType instanceof CompositeType ) {
|
||||
CompositeType compositeType = (CompositeType) identifierType;
|
||||
List<String> quotedIdentifiers = new ArrayList<>();
|
||||
|
||||
for ( int i = 0; i < value.length; i++ ) {
|
||||
quotedIdentifiers.add(quoteIdentifier( value[i], compositeType.getSubtypes()[i] ));
|
||||
}
|
||||
return String.join( ",", quotedIdentifiers );
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Composite identifier does not implement CompositeType");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String quoteIdentifier(Object value, Type type) {
|
||||
Type resolvedType = ( !type.getReturnedClass().equals( value.getClass() ) ) ?
|
||||
typeResolver.heuristicType( value.getClass().getName() ) : type;
|
||||
|
||||
if ( resolvedType instanceof LiteralType ) {
|
||||
LiteralType literalType = (LiteralType) resolvedType;
|
||||
try {
|
||||
return literalType.objectToSQLString( value, dialect );
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
throw new IllegalArgumentException( e );
|
||||
}
|
||||
}
|
||||
return String.valueOf( value );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.hql.spi.id.inline;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
|
||||
/**
|
||||
* Inline bulk-id delete handler that uses an IN clause.
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineIdsIdsInClauseDeleteHandlerImpl
|
||||
extends AbstractInlineIdsDeleteHandlerImpl
|
||||
implements MultiTableBulkIdStrategy.DeleteHandler {
|
||||
|
||||
public InlineIdsIdsInClauseDeleteHandlerImpl(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
super( factory, walker );
|
||||
Dialect dialect = factory.getServiceRegistry().getService( JdbcServices.class ).getDialect();
|
||||
if ( !dialect.supportsRowValueConstructorSyntaxInInList() ) {
|
||||
throw new UnsupportedOperationException(
|
||||
"The " + getClass().getSimpleName() +
|
||||
" can only be used with Dialects that support IN clause row-value expressions (for composite identifiers)!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IdsClauseBuilder newIdsClauseBuilder(List<Object[]> ids) {
|
||||
return new InlineIdsInClauseBuilder(
|
||||
dialect(),
|
||||
getTargetedQueryable().getIdentifierType(),
|
||||
factory().getTypeResolver(),
|
||||
getTargetedQueryable().getIdentifierColumnNames(),
|
||||
ids
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.hql.spi.id.inline;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.sql.Delete;
|
||||
|
||||
/**
|
||||
* Inline bulk-id delete handler that uses multiple identifier OR clauses.
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineIdsIdsOrClauseDeleteHandlerImpl
|
||||
extends AbstractInlineIdsDeleteHandlerImpl
|
||||
implements MultiTableBulkIdStrategy.DeleteHandler {
|
||||
|
||||
public InlineIdsIdsOrClauseDeleteHandlerImpl(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
super( factory, walker );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IdsClauseBuilder newIdsClauseBuilder(List<Object[]> ids) {
|
||||
return new InlineIdsOrClauseBuilder(
|
||||
dialect(),
|
||||
getTargetedQueryable().getIdentifierType(),
|
||||
factory().getTypeResolver(),
|
||||
getTargetedQueryable().getIdentifierColumnNames(),
|
||||
ids
|
||||
);
|
||||
}
|
||||
|
||||
protected Delete generateDelete(
|
||||
String tableName,
|
||||
String[] columnNames,
|
||||
String idSubselect,
|
||||
String comment) {
|
||||
final Delete delete = new Delete()
|
||||
.setTableName( tableName )
|
||||
.setWhere(idSubselect);
|
||||
if ( factory().getSessionFactoryOptions().isCommentsEnabled() ) {
|
||||
delete.setComment( comment );
|
||||
}
|
||||
return delete;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.hql.spi.id.inline;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.type.TypeResolver;
|
||||
|
||||
/**
|
||||
* Builds the where IN clause that wraps the identifiers to be updated/deleted.
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineIdsInClauseBuilder extends IdsClauseBuilder {
|
||||
|
||||
private final int chunkLimit;
|
||||
|
||||
public InlineIdsInClauseBuilder(
|
||||
Dialect dialect, Type identifierType, TypeResolver typeResolver, String[] columns, List<Object[]> ids) {
|
||||
super( dialect, identifierType, typeResolver, columns, ids );
|
||||
this.chunkLimit = dialect.getInExpressionCountLimit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStatement() {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
String columnNames = String.join( ",", (CharSequence[]) getColumns() );
|
||||
|
||||
for ( int i = 0; i < getIds().size(); i++ ) {
|
||||
Object[] idTokens = getIds().get( i );
|
||||
if ( i > 0 ) {
|
||||
if( chunkLimit > 0 && i % chunkLimit == 0 ) {
|
||||
buffer.append( " ) or ( " );
|
||||
buffer.append( columnNames );
|
||||
buffer.append( " ) in (" );
|
||||
}
|
||||
else {
|
||||
buffer.append( "," );
|
||||
}
|
||||
}
|
||||
buffer.append( "(" );
|
||||
buffer.append( quoteIdentifier( idTokens ) );
|
||||
buffer.append( ")" );
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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.hql.spi.id.inline;
|
||||
|
||||
import org.hibernate.boot.spi.MetadataImplementor;
|
||||
import org.hibernate.boot.spi.SessionFactoryOptions;
|
||||
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
|
||||
/**
|
||||
* This bulk-id strategy inlines identifiers of the rows that need to be updated or deleted using an IN clause:
|
||||
*
|
||||
* <pre>
|
||||
* delete
|
||||
* from
|
||||
* Doctor
|
||||
* where
|
||||
* ( id ) in (
|
||||
* ( 1 ),
|
||||
* ( 2 ),
|
||||
* ( 3 ),
|
||||
* ( 4 )
|
||||
* )
|
||||
* </pre>
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineIdsInClauseBulkIdStrategy
|
||||
implements MultiTableBulkIdStrategy {
|
||||
|
||||
public static final InlineIdsInClauseBulkIdStrategy INSTANCE =
|
||||
new InlineIdsInClauseBulkIdStrategy();
|
||||
|
||||
@Override
|
||||
public void prepare(
|
||||
JdbcServices jdbcServices,
|
||||
JdbcConnectionAccess jdbcConnectionAccess,
|
||||
MetadataImplementor metadataImplementor,
|
||||
SessionFactoryOptions sessionFactoryOptions) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(
|
||||
JdbcServices jdbcServices,
|
||||
JdbcConnectionAccess connectionAccess) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateHandler buildUpdateHandler(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
return new InlineIdsInClauseUpdateHandlerImpl( factory, walker );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeleteHandler buildDeleteHandler(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
return new InlineIdsIdsInClauseDeleteHandlerImpl( factory, walker );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.hql.spi.id.inline;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
|
||||
/**
|
||||
* Inline bulk-id update handler that uses an IN clause.
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineIdsInClauseUpdateHandlerImpl
|
||||
extends AbstractInlineIdsUpdateHandlerImpl
|
||||
implements MultiTableBulkIdStrategy.UpdateHandler {
|
||||
|
||||
public InlineIdsInClauseUpdateHandlerImpl(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
super( factory, walker );
|
||||
Dialect dialect = factory.getServiceRegistry().getService( JdbcServices.class ).getDialect();
|
||||
if ( !dialect.supportsRowValueConstructorSyntaxInInList() ) {
|
||||
throw new UnsupportedOperationException(
|
||||
"The " + getClass().getSimpleName() +
|
||||
" can only be used with Dialects that support IN clause row-value expressions (for composite identifiers)!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IdsClauseBuilder newIdsClauseBuilder(List<Object[]> ids) {
|
||||
return new InlineIdsInClauseBuilder(
|
||||
dialect(),
|
||||
getTargetedQueryable().getIdentifierType(),
|
||||
factory().getTypeResolver(),
|
||||
getTargetedQueryable().getIdentifierColumnNames(),
|
||||
ids
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.hql.spi.id.inline;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.type.TypeResolver;
|
||||
|
||||
/**
|
||||
* Builds the where clause using OR expressions for the identifiers to be updated/deleted.
|
||||
* This is useful for Dialects that do no support IN clause row value expressions.
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineIdsOrClauseBuilder extends IdsClauseBuilder {
|
||||
|
||||
public InlineIdsOrClauseBuilder(
|
||||
Dialect dialect, Type identifierType, TypeResolver typeResolver, String[] columns, List<Object[]> ids) {
|
||||
super( dialect, identifierType, typeResolver, columns, ids );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStatement() {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
for ( int i = 0; i < getIds().size(); i++ ) {
|
||||
Object[] idTokens = getIds().get( i );
|
||||
|
||||
if ( i > 0 ) {
|
||||
buffer.append( " or " );
|
||||
}
|
||||
|
||||
buffer.append( "(" );
|
||||
|
||||
for ( int j = 0; j < getColumns().length; j++ ) {
|
||||
if ( j > 0 ) {
|
||||
buffer.append( " and " );
|
||||
}
|
||||
buffer.append( getColumns()[j] );
|
||||
buffer.append( " = " );
|
||||
buffer.append( quoteIdentifier( idTokens[j] ) );
|
||||
|
||||
}
|
||||
buffer.append( ")" );
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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.hql.spi.id.inline;
|
||||
|
||||
import org.hibernate.boot.spi.MetadataImplementor;
|
||||
import org.hibernate.boot.spi.SessionFactoryOptions;
|
||||
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
|
||||
/**
|
||||
* This bulk-id strategy inlines identifiers of the rows that need to be updated or deleted using multiple identifier OR clauses.
|
||||
*
|
||||
* <pre>
|
||||
* delete
|
||||
* from
|
||||
* Engineer
|
||||
* where
|
||||
* (
|
||||
* id = 0
|
||||
* and companyName = 'Red Hat Europe'
|
||||
* )
|
||||
* or (
|
||||
* id = 1
|
||||
* and companyName = 'Red Hat USA'
|
||||
* )
|
||||
* </pre>
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineIdsOrClauseBulkIdStrategy
|
||||
implements MultiTableBulkIdStrategy {
|
||||
|
||||
public static final InlineIdsOrClauseBulkIdStrategy INSTANCE =
|
||||
new InlineIdsOrClauseBulkIdStrategy();
|
||||
|
||||
@Override
|
||||
public void prepare(
|
||||
JdbcServices jdbcServices,
|
||||
JdbcConnectionAccess jdbcConnectionAccess,
|
||||
MetadataImplementor metadataImplementor,
|
||||
SessionFactoryOptions sessionFactoryOptions) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(
|
||||
JdbcServices jdbcServices,
|
||||
JdbcConnectionAccess connectionAccess) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateHandler buildUpdateHandler(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
return new InlineIdsOrClauseUpdateHandlerImpl( factory, walker );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeleteHandler buildDeleteHandler(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
return new InlineIdsIdsOrClauseDeleteHandlerImpl( factory, walker );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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.hql.spi.id.inline;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.sql.Update;
|
||||
|
||||
/**
|
||||
* Inline bulk-id update handler that uses multiple identifier OR clauses.
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineIdsOrClauseUpdateHandlerImpl
|
||||
extends AbstractInlineIdsUpdateHandlerImpl
|
||||
implements MultiTableBulkIdStrategy.UpdateHandler {
|
||||
|
||||
public InlineIdsOrClauseUpdateHandlerImpl(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
super( factory, walker );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IdsClauseBuilder newIdsClauseBuilder(List<Object[]> ids) {
|
||||
return new InlineIdsOrClauseBuilder(
|
||||
dialect(),
|
||||
getTargetedQueryable().getIdentifierType(),
|
||||
factory().getTypeResolver(),
|
||||
getTargetedQueryable().getIdentifierColumnNames(),
|
||||
ids
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Update generateUpdate(
|
||||
String tableName,
|
||||
String[] columnNames,
|
||||
String idSubselect,
|
||||
String comment) {
|
||||
final Update update = new Update( factory().getServiceRegistry().getService( JdbcServices.class ).getDialect() )
|
||||
.setTableName( tableName )
|
||||
.setWhere( idSubselect );
|
||||
if ( factory().getSessionFactoryOptions().isCommentsEnabled() ) {
|
||||
update.setComment( comment );
|
||||
}
|
||||
return update;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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.hql.spi.id.inline;
|
||||
|
||||
import org.hibernate.boot.spi.MetadataImplementor;
|
||||
import org.hibernate.boot.spi.SessionFactoryOptions;
|
||||
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
|
||||
/**
|
||||
* This bulk-id strategy inlines identifiers of the rows that need to be updated or deleted in a subselect using a VALUES list:
|
||||
*
|
||||
* <pre>
|
||||
* delete
|
||||
* from
|
||||
* Person
|
||||
* where
|
||||
* ( id ) in (
|
||||
* select
|
||||
* id
|
||||
* from (
|
||||
* values
|
||||
* ( 1 ),
|
||||
* ( 2 ),
|
||||
* ( 3 ),
|
||||
* ( 4 )
|
||||
* ) as HT (id)
|
||||
* )
|
||||
* </pre>
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineIdsSubSelectValueListBulkIdStrategy
|
||||
implements MultiTableBulkIdStrategy {
|
||||
|
||||
public static final InlineIdsSubSelectValueListBulkIdStrategy INSTANCE =
|
||||
new InlineIdsSubSelectValueListBulkIdStrategy();
|
||||
|
||||
@Override
|
||||
public void prepare(
|
||||
JdbcServices jdbcServices,
|
||||
JdbcConnectionAccess jdbcConnectionAccess,
|
||||
MetadataImplementor metadataImplementor,
|
||||
SessionFactoryOptions sessionFactoryOptions) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(
|
||||
JdbcServices jdbcServices,
|
||||
JdbcConnectionAccess connectionAccess) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateHandler buildUpdateHandler(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
return new InlineIdsSubSelectValuesListUpdateHandlerImpl(
|
||||
factory,
|
||||
walker
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeleteHandler buildDeleteHandler(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
return new InlineIdsSubSelectValuesListDeleteHandlerImpl(
|
||||
factory,
|
||||
walker
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.hql.spi.id.inline;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.type.TypeResolver;
|
||||
|
||||
/**
|
||||
* Builds the where SELECT FROM VALUES clause that wraps the identifiers to be updated/deleted.
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineIdsSubSelectValuesListBuilder extends IdsClauseBuilder {
|
||||
|
||||
public InlineIdsSubSelectValuesListBuilder(
|
||||
Dialect dialect, Type identifierType, TypeResolver typeResolver, String[] columns, List<Object[]> ids) {
|
||||
super( dialect, identifierType, typeResolver, columns, ids );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStatement() {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
String columnNames = String.join( ",", (CharSequence[]) getColumns() );
|
||||
|
||||
buffer.append( "select " ).append( columnNames ).append(
|
||||
" from ( values " );
|
||||
|
||||
for ( int i = 0; i < getIds().size(); i++ ) {
|
||||
Object[] idTokens = getIds().get( i );
|
||||
if ( i > 0 ) {
|
||||
buffer.append( "," );
|
||||
}
|
||||
buffer.append( "(" );
|
||||
buffer.append( quoteIdentifier( idTokens ) );
|
||||
buffer.append( ")" );
|
||||
}
|
||||
buffer.append( ") as HT (" ).append( columnNames ).append( ") " );
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.hql.spi.id.inline;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
|
||||
/**
|
||||
* Inline bulk-id delete handler that uses a subselect with a VALUES clause.
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineIdsSubSelectValuesListDeleteHandlerImpl
|
||||
extends AbstractInlineIdsDeleteHandlerImpl
|
||||
implements MultiTableBulkIdStrategy.DeleteHandler {
|
||||
|
||||
public InlineIdsSubSelectValuesListDeleteHandlerImpl(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
super( factory, walker );
|
||||
|
||||
Dialect dialect = factory().getServiceRegistry().getService( JdbcServices.class ).getDialect();
|
||||
if ( !dialect.supportsRowValueConstructorSyntaxInInList() ) {
|
||||
throw new UnsupportedOperationException(
|
||||
"The " + getClass().getSimpleName() +
|
||||
" can only be used with Dialects that support IN clause row-value expressions (for composite identifiers)!"
|
||||
);
|
||||
}
|
||||
if ( !dialect.supportsValuesList() ) {
|
||||
throw new UnsupportedOperationException(
|
||||
"The " + getClass().getSimpleName() +
|
||||
" can only be used with Dialects that support VALUES lists!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IdsClauseBuilder newIdsClauseBuilder(List<Object[]> ids) {
|
||||
return new InlineIdsSubSelectValuesListBuilder(
|
||||
dialect(),
|
||||
getTargetedQueryable().getIdentifierType(),
|
||||
factory().getTypeResolver(),
|
||||
getTargetedQueryable().getIdentifierColumnNames(),
|
||||
ids
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.hql.spi.id.inline;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
|
||||
/**
|
||||
* Inline bulk-id update handler that uses a subselect with a VALUES clause.
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineIdsSubSelectValuesListUpdateHandlerImpl
|
||||
extends AbstractInlineIdsUpdateHandlerImpl
|
||||
implements MultiTableBulkIdStrategy.DeleteHandler {
|
||||
|
||||
public InlineIdsSubSelectValuesListUpdateHandlerImpl(
|
||||
SessionFactoryImplementor factory,
|
||||
HqlSqlWalker walker) {
|
||||
super( factory, walker );
|
||||
|
||||
Dialect dialect = factory().getServiceRegistry().getService( JdbcServices.class ).getDialect();
|
||||
if ( !dialect.supportsRowValueConstructorSyntaxInInList() ) {
|
||||
throw new UnsupportedOperationException(
|
||||
"The " + getClass().getSimpleName() +
|
||||
" can only be used with Dialects that support IN clause row-value expressions (for composite identifiers)!"
|
||||
);
|
||||
}
|
||||
if ( !dialect.supportsValuesList() ) {
|
||||
throw new UnsupportedOperationException(
|
||||
"The " + getClass().getSimpleName() +
|
||||
" can only be used with Dialects that support VALUES lists!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IdsClauseBuilder newIdsClauseBuilder(List<Object[]> ids) {
|
||||
return new InlineIdsSubSelectValuesListBuilder(
|
||||
dialect(),
|
||||
getTargetedQueryable().getIdentifierType(),
|
||||
factory().getTypeResolver(),
|
||||
getTargetedQueryable().getIdentifierColumnNames(),
|
||||
ids
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public abstract class AbstractBulkCompositeIdTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Person.class,
|
||||
Doctor.class,
|
||||
Engineer.class
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Configuration constructConfiguration() {
|
||||
Configuration configuration = super.constructConfiguration();
|
||||
configuration.setProperty( AvailableSettings.HQL_BULK_ID_STRATEGY, getMultiTableBulkIdStrategyClass().getName() );
|
||||
return configuration;
|
||||
}
|
||||
|
||||
protected abstract Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass();
|
||||
|
||||
@Override
|
||||
protected boolean isCleanupTestDataRequired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCleanupTestDataUsingBulkDelete() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
for ( int i = 0; i < entityCount(); i++ ) {
|
||||
Doctor doctor = new Doctor();
|
||||
doctor.setId( i );
|
||||
doctor.setCompanyName( "Red Hat USA" );
|
||||
doctor.setEmployed( ( i % 2 ) == 0 );
|
||||
session.persist( doctor );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < entityCount(); i++ ) {
|
||||
Engineer engineer = new Engineer();
|
||||
engineer.setId( i );
|
||||
engineer.setCompanyName( "Red Hat Europe" );
|
||||
engineer.setEmployed( ( i % 2 ) == 0 );
|
||||
engineer.setFellow( ( i % 2 ) == 1 );
|
||||
session.persist( engineer );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected int entityCount() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
int updateCount = session.createQuery( "update Person set name = :name where employed = :employed" )
|
||||
.setParameter( "name", "John Doe" )
|
||||
.setParameter( "employed", true )
|
||||
.executeUpdate();
|
||||
|
||||
assertEquals(entityCount(), updateCount);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteFromPerson() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
int updateCount = session.createQuery( "delete from Person where employed = :employed" )
|
||||
.setParameter( "employed", false )
|
||||
.executeUpdate();
|
||||
assertEquals( entityCount(), updateCount );
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteFromEngineer() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
int updateCount = session.createQuery( "delete from Engineer where fellow = :fellow" )
|
||||
.setParameter( "fellow", true )
|
||||
.executeUpdate();
|
||||
assertEquals( entityCount() / 2, updateCount );
|
||||
});
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
@Inheritance(strategy = InheritanceType.JOINED)
|
||||
public static class Person implements Serializable {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
@Id
|
||||
private String companyName;
|
||||
|
||||
private String name;
|
||||
|
||||
private boolean employed;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCompanyName() {
|
||||
return companyName;
|
||||
}
|
||||
|
||||
public void setCompanyName(String companyName) {
|
||||
this.companyName = companyName;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isEmployed() {
|
||||
return employed;
|
||||
}
|
||||
|
||||
public void setEmployed(boolean employed) {
|
||||
this.employed = employed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( !( o instanceof Person ) ) {
|
||||
return false;
|
||||
}
|
||||
Person person = (Person) o;
|
||||
return Objects.equals( getId(), person.getId() ) &&
|
||||
Objects.equals( getCompanyName(), person.getCompanyName() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash( getId(), getCompanyName() );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Doctor")
|
||||
public static class Doctor extends Person {
|
||||
}
|
||||
|
||||
@Entity(name = "Engineer")
|
||||
public static class Engineer extends Person {
|
||||
|
||||
private boolean fellow;
|
||||
|
||||
public boolean isFellow() {
|
||||
return fellow;
|
||||
}
|
||||
|
||||
public void setFellow(boolean fellow) {
|
||||
this.fellow = fellow;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public abstract class AbstractBulkIdTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Person.class,
|
||||
Doctor.class,
|
||||
Engineer.class
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Configuration constructConfiguration() {
|
||||
Configuration configuration = super.constructConfiguration();
|
||||
configuration.setProperty( AvailableSettings.HQL_BULK_ID_STRATEGY, getMultiTableBulkIdStrategyClass().getName() );
|
||||
return configuration;
|
||||
}
|
||||
|
||||
protected abstract Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass();
|
||||
|
||||
@Override
|
||||
protected boolean isCleanupTestDataRequired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCleanupTestDataUsingBulkDelete() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
for ( int i = 0; i < entityCount(); i++ ) {
|
||||
Doctor doctor = new Doctor();
|
||||
doctor.setEmployed( ( i % 2 ) == 0 );
|
||||
session.persist( doctor );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < entityCount(); i++ ) {
|
||||
Engineer engineer = new Engineer();
|
||||
engineer.setEmployed( ( i % 2 ) == 0 );
|
||||
engineer.setFellow( ( i % 2 ) == 1 );
|
||||
session.persist( engineer );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected int entityCount() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
int updateCount = session.createQuery( "update Person set name = :name where employed = :employed" )
|
||||
.setParameter( "name", "John Doe" )
|
||||
.setParameter( "employed", true )
|
||||
.executeUpdate();
|
||||
|
||||
assertEquals(entityCount(), updateCount);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteFromPerson() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
int updateCount = session.createQuery( "delete from Person where employed = :employed" )
|
||||
.setParameter( "employed", false )
|
||||
.executeUpdate();
|
||||
assertEquals( entityCount(), updateCount );
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteFromEngineer() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
int updateCount = session.createQuery( "delete from Engineer where fellow = :fellow" )
|
||||
.setParameter( "fellow", true )
|
||||
.executeUpdate();
|
||||
assertEquals( entityCount() / 2, updateCount );
|
||||
});
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
@Inheritance(strategy = InheritanceType.JOINED)
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
private boolean employed;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isEmployed() {
|
||||
return employed;
|
||||
}
|
||||
|
||||
public void setEmployed(boolean employed) {
|
||||
this.employed = employed;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Doctor")
|
||||
public static class Doctor extends Person {
|
||||
}
|
||||
|
||||
@Entity(name = "Engineer")
|
||||
public static class Engineer extends Person {
|
||||
|
||||
private boolean fellow;
|
||||
|
||||
public boolean isFellow() {
|
||||
return fellow;
|
||||
}
|
||||
|
||||
public void setFellow(boolean fellow) {
|
||||
this.fellow = fellow;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.cte.CteValuesListBulkIdStrategy;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialectFeature(DialectChecks.SupportNonQueryValuesListWithCTE.class)
|
||||
public class CteValuesListBulkCompositeIdTest extends AbstractBulkCompositeIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return CteValuesListBulkIdStrategy.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.cte.CteValuesListBulkIdStrategy;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialectFeature(DialectChecks.SupportNonQueryValuesListWithCTE.class)
|
||||
public class CteValuesListBulkIdTest extends AbstractBulkIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return CteValuesListBulkIdStrategy.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.inline.InlineIdsInClauseBulkIdStrategy;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialectFeature(DialectChecks.SupportRowValueConstructorSyntaxInInList.class)
|
||||
public class InlineIdsInClauseBulkCompositeIdTest extends
|
||||
AbstractBulkCompositeIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return InlineIdsInClauseBulkIdStrategy.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.inline.InlineIdsInClauseBulkIdStrategy;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialectFeature(DialectChecks.SupportRowValueConstructorSyntaxInInList.class)
|
||||
public class InlineIdsInClauseBulkIdTest extends AbstractBulkIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return InlineIdsInClauseBulkIdStrategy.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.inline.InlineIdsOrClauseBulkIdStrategy;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineIdsOrClauseBulkCompositeIdTest extends
|
||||
AbstractBulkCompositeIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return InlineIdsOrClauseBulkIdStrategy.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.inline.InlineIdsOrClauseBulkIdStrategy;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineIdsOrClauseBulkIdTest extends AbstractBulkIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return InlineIdsOrClauseBulkIdStrategy.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.inline.InlineIdsSubSelectValueListBulkIdStrategy;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialectFeature(DialectChecks.SupportValuesListAndRowValueConstructorSyntaxInInList.class)
|
||||
public class InlineIdsSubSelectValuesListBulkCompositeIdTest extends AbstractBulkCompositeIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return InlineIdsSubSelectValueListBulkIdStrategy.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.inline.InlineIdsSubSelectValueListBulkIdStrategy;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialectFeature(DialectChecks.SupportValuesListAndRowValueConstructorSyntaxInInList.class)
|
||||
public class InlineIdsSubSelectValuesListBulkIdTest extends AbstractBulkIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return InlineIdsSubSelectValueListBulkIdStrategy.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.dialect.Oracle8iDialect;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(Oracle8iDialect.class)
|
||||
public class OracleInlineIdsInClauseBulkIdTest extends InlineIdsInClauseBulkIdTest {
|
||||
|
||||
@Override
|
||||
protected int entityCount() {
|
||||
return 1100;
|
||||
}
|
||||
}
|
|
@ -201,4 +201,26 @@ abstract public class DialectChecks {
|
|||
return dialect.supportsPartitionBy();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SupportNonQueryValuesListWithCTE implements DialectCheck {
|
||||
public boolean isMatch(Dialect dialect) {
|
||||
return dialect.supportsValuesList() &&
|
||||
dialect.supportsNonQueryWithCTE() &&
|
||||
dialect.supportsRowValueConstructorSyntaxInInList();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SupportValuesListAndRowValueConstructorSyntaxInInList
|
||||
implements DialectCheck {
|
||||
public boolean isMatch(Dialect dialect) {
|
||||
return dialect.supportsValuesList() &&
|
||||
dialect.supportsRowValueConstructorSyntaxInInList();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SupportRowValueConstructorSyntaxInInList implements DialectCheck {
|
||||
public boolean isMatch(Dialect dialect) {
|
||||
return dialect.supportsRowValueConstructorSyntaxInInList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue