HHH-7725 - Make handling multi-table bulk HQL operations more pluggable
This commit is contained in:
parent
3305af5fe8
commit
3e69b7bd53
|
@ -72,7 +72,7 @@ public class BulkOperationCleanupAction implements Executable, Serializable {
|
|||
* @param session The session to which this request is tied.
|
||||
* @param affectedQueryables The affected entity persisters.
|
||||
*/
|
||||
public BulkOperationCleanupAction(SessionImplementor session, Queryable[] affectedQueryables) {
|
||||
public BulkOperationCleanupAction(SessionImplementor session, Queryable... affectedQueryables) {
|
||||
SessionFactoryImplementor factory = session.getFactory();
|
||||
LinkedHashSet<String> spacesList = new LinkedHashSet<String>();
|
||||
for ( Queryable persister : affectedQueryables ) {
|
||||
|
|
|
@ -577,4 +577,6 @@ public interface AvailableSettings {
|
|||
public static final String FORCE_DISCRIMINATOR_IN_SELECTS_BY_DEFAULT = "hibernate.discriminator.force_in_select";
|
||||
|
||||
public static final String ENABLE_LAZY_LOAD_NO_TRANS = "hibernate.enable_lazy_load_no_trans";
|
||||
|
||||
public static final String HQL_BULK_ID_STRATEGY = "hibernate.hql.bulk_id_strategy";
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.hibernate.EntityMode;
|
|||
import org.hibernate.MultiTenancyStrategy;
|
||||
import org.hibernate.cache.spi.QueryCacheFactory;
|
||||
import org.hibernate.cache.spi.RegionFactory;
|
||||
import org.hibernate.hql.spi.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.QueryTranslatorFactory;
|
||||
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
|
||||
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
||||
|
@ -89,6 +90,9 @@ public final class Settings {
|
|||
|
||||
private JtaPlatform jtaPlatform;
|
||||
|
||||
private MultiTableBulkIdStrategy multiTableBulkIdStrategy;
|
||||
|
||||
|
||||
/**
|
||||
* Package protected constructor
|
||||
*/
|
||||
|
@ -468,4 +472,12 @@ public final class Settings {
|
|||
void setInitializeLazyStateOutsideTransactions(boolean initializeLazyStateOutsideTransactions) {
|
||||
this.initializeLazyStateOutsideTransactions = initializeLazyStateOutsideTransactions;
|
||||
}
|
||||
|
||||
public MultiTableBulkIdStrategy getMultiTableBulkIdStrategy() {
|
||||
return multiTableBulkIdStrategy;
|
||||
}
|
||||
|
||||
void setMultiTableBulkIdStrategy(MultiTableBulkIdStrategy multiTableBulkIdStrategy) {
|
||||
this.multiTableBulkIdStrategy = multiTableBulkIdStrategy;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.hibernate.ConnectionReleaseMode;
|
|||
import org.hibernate.EntityMode;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MultiTenancyStrategy;
|
||||
import org.hibernate.boot.registry.selector.spi.StrategySelector;
|
||||
import org.hibernate.cache.internal.NoCachingRegionFactory;
|
||||
import org.hibernate.cache.internal.RegionFactoryInitiator;
|
||||
import org.hibernate.cache.internal.StandardQueryCacheFactory;
|
||||
|
@ -41,7 +42,10 @@ import org.hibernate.cache.spi.RegionFactory;
|
|||
import org.hibernate.engine.jdbc.spi.ExtractedDatabaseMetaData;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.transaction.spi.TransactionFactory;
|
||||
import org.hibernate.hql.spi.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.PersistentTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.QueryTranslatorFactory;
|
||||
import org.hibernate.hql.spi.TemporaryTableBulkIdStrategy;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||
|
@ -97,6 +101,18 @@ public class SettingsFactory implements Serializable {
|
|||
// Transaction settings:
|
||||
settings.setJtaPlatform( serviceRegistry.getService( JtaPlatform.class ) );
|
||||
|
||||
MultiTableBulkIdStrategy multiTableBulkIdStrategy = serviceRegistry.getService( StrategySelector.class )
|
||||
.resolveStrategy(
|
||||
MultiTableBulkIdStrategy.class,
|
||||
properties.getProperty( AvailableSettings.HQL_BULK_ID_STRATEGY )
|
||||
);
|
||||
if ( multiTableBulkIdStrategy == null ) {
|
||||
multiTableBulkIdStrategy = jdbcServices.getDialect().supportsTemporaryTables()
|
||||
? TemporaryTableBulkIdStrategy.INSTANCE
|
||||
: new PersistentTableBulkIdStrategy();
|
||||
}
|
||||
settings.setMultiTableBulkIdStrategy( multiTableBulkIdStrategy );
|
||||
|
||||
boolean flushBeforeCompletion = ConfigurationHelper.getBoolean(AvailableSettings.FLUSH_BEFORE_COMPLETION, properties);
|
||||
if ( debugEnabled ) {
|
||||
LOG.debugf( "Automatic flush during beforeCompletion(): %s", enabledDisabled(flushBeforeCompletion) );
|
||||
|
|
|
@ -31,9 +31,12 @@ import java.util.List;
|
|||
import antlr.RecognitionException;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.action.internal.BulkOperationCleanupAction;
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
import org.hibernate.engine.spi.RowSelection;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.internal.ast.QuerySyntaxException;
|
||||
import org.hibernate.hql.internal.ast.SqlGenerator;
|
||||
|
@ -45,17 +48,17 @@ import org.hibernate.persister.entity.Queryable;
|
|||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class BasicExecutor extends AbstractStatementExecutor {
|
||||
|
||||
public class BasicExecutor implements StatementExecutor {
|
||||
private final SessionFactoryImplementor factory;
|
||||
private final Queryable persister;
|
||||
private final String sql;
|
||||
private final List parameterSpecifications;
|
||||
|
||||
public BasicExecutor(HqlSqlWalker walker, Queryable persister) {
|
||||
super(walker, null);
|
||||
this.factory = walker.getSessionFactoryHelper().getFactory();
|
||||
this.persister = persister;
|
||||
try {
|
||||
SqlGenerator gen = new SqlGenerator( getFactory() );
|
||||
SqlGenerator gen = new SqlGenerator( factory );
|
||||
gen.statement( walker.getAST() );
|
||||
sql = gen.getSQL();
|
||||
gen.getParseErrorHandler().throwQueryException();
|
||||
|
@ -71,8 +74,13 @@ public class BasicExecutor extends AbstractStatementExecutor {
|
|||
}
|
||||
|
||||
public int execute(QueryParameters parameters, SessionImplementor session) throws HibernateException {
|
||||
|
||||
coordinateSharedCacheCleanup( session );
|
||||
BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, persister );
|
||||
if ( session.isEventSource() ) {
|
||||
( (EventSource) session ).getActionQueue().addAction( action );
|
||||
}
|
||||
else {
|
||||
action.getAfterTransactionCompletionProcess().doAfterTransactionCompletion( true, session );
|
||||
}
|
||||
|
||||
PreparedStatement st = null;
|
||||
RowSelection selection = parameters.getRowSelection();
|
||||
|
@ -101,16 +109,7 @@ public class BasicExecutor extends AbstractStatementExecutor {
|
|||
}
|
||||
}
|
||||
catch( SQLException sqle ) {
|
||||
throw getFactory().getSQLExceptionHelper().convert(
|
||||
sqle,
|
||||
"could not execute update query",
|
||||
sql
|
||||
);
|
||||
throw factory.getSQLExceptionHelper().convert( sqle, "could not execute update query", sql );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Queryable[] getAffectedQueryables() {
|
||||
return new Queryable[] { persister };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
|
||||
* Copyright (c) 2008, 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Middleware LLC.
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
|
@ -20,147 +20,46 @@
|
|||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
package org.hibernate.hql.internal.ast.exec;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.action.internal.BulkOperationCleanupAction;
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.internal.ast.tree.DeleteStatement;
|
||||
import org.hibernate.hql.internal.ast.tree.FromElement;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.param.ParameterSpecification;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
import org.hibernate.sql.Delete;
|
||||
import org.hibernate.hql.spi.MultiTableBulkIdStrategy;
|
||||
|
||||
/**
|
||||
* Implementation of MultiTableDeleteExecutor.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class MultiTableDeleteExecutor extends AbstractStatementExecutor {
|
||||
|
||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class,
|
||||
MultiTableDeleteExecutor.class.getName());
|
||||
|
||||
private final Queryable persister;
|
||||
private final String idInsertSelect;
|
||||
private final String[] deletes;
|
||||
public class MultiTableDeleteExecutor implements StatementExecutor {
|
||||
private final MultiTableBulkIdStrategy.DeleteHandler deleteHandler;
|
||||
|
||||
public MultiTableDeleteExecutor(HqlSqlWalker walker) {
|
||||
super(walker, null);
|
||||
|
||||
if ( !walker.getSessionFactoryHelper().getFactory().getDialect().supportsTemporaryTables() ) {
|
||||
throw new HibernateException( "cannot doAfterTransactionCompletion multi-table deletes using dialect not supporting temp tables" );
|
||||
}
|
||||
|
||||
DeleteStatement deleteStatement = ( DeleteStatement ) walker.getAST();
|
||||
FromElement fromElement = deleteStatement.getFromClause().getFromElement();
|
||||
String bulkTargetAlias = fromElement.getTableAlias();
|
||||
this.persister = fromElement.getQueryable();
|
||||
|
||||
this.idInsertSelect = generateIdInsertSelect( persister, bulkTargetAlias, deleteStatement.getWhereClause() );
|
||||
LOG.tracev( "Generated ID-INSERT-SELECT SQL (multi-table delete) : {0}", idInsertSelect );
|
||||
|
||||
String[] tableNames = persister.getConstraintOrderedTableNameClosure();
|
||||
String[][] columnNames = persister.getContraintOrderedTableKeyColumnClosure();
|
||||
String idSubselect = generateIdSubselect( persister );
|
||||
|
||||
deletes = new String[tableNames.length];
|
||||
for ( int i = tableNames.length - 1; i >= 0; 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
|
||||
final Delete delete = new Delete()
|
||||
.setTableName( tableNames[i] )
|
||||
.setWhere( "(" + StringHelper.join( ", ", columnNames[i] ) + ") IN (" + idSubselect + ")" );
|
||||
if ( getFactory().getSettings().isCommentsEnabled() ) {
|
||||
delete.setComment( "bulk delete" );
|
||||
}
|
||||
|
||||
deletes[i] = delete.toStatementString();
|
||||
}
|
||||
MultiTableBulkIdStrategy strategy = walker.getSessionFactoryHelper()
|
||||
.getFactory()
|
||||
.getSettings()
|
||||
.getMultiTableBulkIdStrategy();
|
||||
this.deleteHandler = strategy.buildDeleteHandler( walker.getSessionFactoryHelper().getFactory(), walker );
|
||||
}
|
||||
|
||||
public String[] getSqlStatements() {
|
||||
return deletes;
|
||||
return deleteHandler.getSqlStatements();
|
||||
}
|
||||
|
||||
public int execute(QueryParameters parameters, SessionImplementor session) throws HibernateException {
|
||||
coordinateSharedCacheCleanup( session );
|
||||
|
||||
createTemporaryTableIfNecessary( persister, session );
|
||||
|
||||
try {
|
||||
// First, save off the pertinent ids, saving the number of pertinent ids for return
|
||||
PreparedStatement ps = null;
|
||||
int resultCount = 0;
|
||||
try {
|
||||
try {
|
||||
ps = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( idInsertSelect, false );
|
||||
Iterator paramSpecifications = getIdSelectParameterSpecifications().iterator();
|
||||
int pos = 1;
|
||||
while ( paramSpecifications.hasNext() ) {
|
||||
final ParameterSpecification paramSpec = ( ParameterSpecification ) paramSpecifications.next();
|
||||
pos += paramSpec.bind( ps, parameters, session, pos );
|
||||
}
|
||||
resultCount = ps.executeUpdate();
|
||||
}
|
||||
finally {
|
||||
if ( ps != null ) {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( SQLException e ) {
|
||||
throw getFactory().getSQLExceptionHelper().convert(
|
||||
e,
|
||||
"could not insert/select ids for bulk delete",
|
||||
idInsertSelect
|
||||
);
|
||||
}
|
||||
|
||||
// Start performing the deletes
|
||||
for ( int i = 0; i < deletes.length; i++ ) {
|
||||
try {
|
||||
try {
|
||||
ps = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( deletes[i], false );
|
||||
ps.executeUpdate();
|
||||
}
|
||||
finally {
|
||||
if ( ps != null ) {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( SQLException e ) {
|
||||
throw getFactory().getSQLExceptionHelper().convert(
|
||||
e,
|
||||
"error performing bulk delete",
|
||||
deletes[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return resultCount;
|
||||
BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, deleteHandler.getTargetedQueryable() );
|
||||
if ( session.isEventSource() ) {
|
||||
( (EventSource) session ).getActionQueue().addAction( action );
|
||||
}
|
||||
finally {
|
||||
dropTemporaryTableIfNecessary( persister, session );
|
||||
else {
|
||||
action.getAfterTransactionCompletionProcess().doAfterTransactionCompletion( true, session );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Queryable[] getAffectedQueryables() {
|
||||
return new Queryable[] { persister };
|
||||
return deleteHandler.execute( session, parameters );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,178 +24,44 @@
|
|||
*/
|
||||
package org.hibernate.hql.internal.ast.exec;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.action.internal.BulkOperationCleanupAction;
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.internal.ast.tree.AssignmentSpecification;
|
||||
import org.hibernate.hql.internal.ast.tree.FromElement;
|
||||
import org.hibernate.hql.internal.ast.tree.UpdateStatement;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.param.ParameterSpecification;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
import org.hibernate.sql.Update;
|
||||
import org.hibernate.hql.spi.MultiTableBulkIdStrategy;
|
||||
|
||||
/**
|
||||
* Implementation of MultiTableUpdateExecutor.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class MultiTableUpdateExecutor extends AbstractStatementExecutor {
|
||||
|
||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class,
|
||||
MultiTableUpdateExecutor.class.getName());
|
||||
|
||||
private final Queryable persister;
|
||||
private final String idInsertSelect;
|
||||
private final String[] updates;
|
||||
private final ParameterSpecification[][] hqlParameters;
|
||||
public class MultiTableUpdateExecutor implements StatementExecutor {
|
||||
private final MultiTableBulkIdStrategy.UpdateHandler updateHandler;
|
||||
|
||||
public MultiTableUpdateExecutor(HqlSqlWalker walker) {
|
||||
super(walker, null);
|
||||
|
||||
if ( !walker.getSessionFactoryHelper().getFactory().getDialect().supportsTemporaryTables() ) {
|
||||
throw new HibernateException( "cannot doAfterTransactionCompletion multi-table updates using dialect not supporting temp tables" );
|
||||
}
|
||||
|
||||
UpdateStatement updateStatement = ( UpdateStatement ) walker.getAST();
|
||||
FromElement fromElement = updateStatement.getFromClause().getFromElement();
|
||||
String bulkTargetAlias = fromElement.getTableAlias();
|
||||
this.persister = fromElement.getQueryable();
|
||||
|
||||
this.idInsertSelect = generateIdInsertSelect( persister, bulkTargetAlias, updateStatement.getWhereClause() );
|
||||
LOG.tracev( "Generated ID-INSERT-SELECT SQL (multi-table update) : {0}", idInsertSelect );
|
||||
|
||||
String[] tableNames = persister.getConstraintOrderedTableNameClosure();
|
||||
String[][] columnNames = persister.getContraintOrderedTableKeyColumnClosure();
|
||||
|
||||
String idSubselect = generateIdSubselect( persister );
|
||||
List assignmentSpecifications = walker.getAssignmentSpecifications();
|
||||
|
||||
updates = new String[tableNames.length];
|
||||
hqlParameters = new ParameterSpecification[tableNames.length][];
|
||||
for ( int tableIndex = 0; tableIndex < tableNames.length; tableIndex++ ) {
|
||||
boolean affected = false;
|
||||
List parameterList = new ArrayList();
|
||||
Update update = new Update( getFactory().getDialect() )
|
||||
.setTableName( tableNames[tableIndex] )
|
||||
.setWhere( "(" + StringHelper.join( ", ", columnNames[tableIndex] ) + ") IN (" + idSubselect + ")" );
|
||||
if ( getFactory().getSettings().isCommentsEnabled() ) {
|
||||
update.setComment( "bulk update" );
|
||||
}
|
||||
final Iterator itr = assignmentSpecifications.iterator();
|
||||
while ( itr.hasNext() ) {
|
||||
final AssignmentSpecification specification = ( AssignmentSpecification ) itr.next();
|
||||
if ( specification.affectsTable( tableNames[tableIndex] ) ) {
|
||||
affected = true;
|
||||
update.appendAssignmentFragment( specification.getSqlAssignmentFragment() );
|
||||
if ( specification.getParameters() != null ) {
|
||||
for ( int paramIndex = 0; paramIndex < specification.getParameters().length; paramIndex++ ) {
|
||||
parameterList.add( specification.getParameters()[paramIndex] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( affected ) {
|
||||
updates[tableIndex] = update.toStatementString();
|
||||
hqlParameters[tableIndex] = ( ParameterSpecification[] ) parameterList.toArray( new ParameterSpecification[0] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Queryable getAffectedQueryable() {
|
||||
return persister;
|
||||
MultiTableBulkIdStrategy strategy = walker.getSessionFactoryHelper()
|
||||
.getFactory()
|
||||
.getSettings()
|
||||
.getMultiTableBulkIdStrategy();
|
||||
this.updateHandler = strategy.buildUpdateHandler( walker.getSessionFactoryHelper().getFactory(), walker );
|
||||
}
|
||||
|
||||
public String[] getSqlStatements() {
|
||||
return updates;
|
||||
return updateHandler.getSqlStatements();
|
||||
}
|
||||
|
||||
public int execute(QueryParameters parameters, SessionImplementor session) throws HibernateException {
|
||||
coordinateSharedCacheCleanup( session );
|
||||
BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, updateHandler.getTargetedQueryable() );
|
||||
|
||||
createTemporaryTableIfNecessary( persister, session );
|
||||
|
||||
try {
|
||||
// First, save off the pertinent ids, as the return value
|
||||
PreparedStatement ps = null;
|
||||
int resultCount = 0;
|
||||
try {
|
||||
try {
|
||||
ps = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( idInsertSelect, false );
|
||||
// int parameterStart = getWalker().getNumberOfParametersInSetClause();
|
||||
// List allParams = getIdSelectParameterSpecifications();
|
||||
// Iterator whereParams = allParams.subList( parameterStart, allParams.size() ).iterator();
|
||||
Iterator whereParams = getIdSelectParameterSpecifications().iterator();
|
||||
int sum = 1; // jdbc params are 1-based
|
||||
while ( whereParams.hasNext() ) {
|
||||
sum += ( ( ParameterSpecification ) whereParams.next() ).bind( ps, parameters, session, sum );
|
||||
}
|
||||
resultCount = ps.executeUpdate();
|
||||
}
|
||||
finally {
|
||||
if ( ps != null ) {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( SQLException e ) {
|
||||
throw getFactory().getSQLExceptionHelper().convert(
|
||||
e,
|
||||
"could not insert/select ids for bulk update",
|
||||
idInsertSelect
|
||||
);
|
||||
}
|
||||
|
||||
// Start performing the updates
|
||||
for ( int i = 0; i < updates.length; i++ ) {
|
||||
if ( updates[i] == null ) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
try {
|
||||
ps = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( updates[i], false );
|
||||
if ( hqlParameters[i] != null ) {
|
||||
int position = 1; // jdbc params are 1-based
|
||||
for ( int x = 0; x < hqlParameters[i].length; x++ ) {
|
||||
position += hqlParameters[i][x].bind( ps, parameters, session, position );
|
||||
}
|
||||
}
|
||||
ps.executeUpdate();
|
||||
}
|
||||
finally {
|
||||
if ( ps != null ) {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( SQLException e ) {
|
||||
throw getFactory().getSQLExceptionHelper().convert(
|
||||
e,
|
||||
"error performing bulk update",
|
||||
updates[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return resultCount;
|
||||
if ( session.isEventSource() ) {
|
||||
( (EventSource) session ).getActionQueue().addAction( action );
|
||||
}
|
||||
finally {
|
||||
dropTemporaryTableIfNecessary( persister, session );
|
||||
else {
|
||||
action.getAfterTransactionCompletionProcess().doAfterTransactionCompletion( true, session );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Queryable[] getAffectedQueryables() {
|
||||
return new Queryable[] { persister };
|
||||
return updateHandler.execute( session, parameters );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.hql.spi;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import antlr.RecognitionException;
|
||||
import antlr.collections.AST;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.JDBCException;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.internal.ast.SqlGenerator;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.param.ParameterSpecification;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
import org.hibernate.sql.InsertSelect;
|
||||
import org.hibernate.sql.Select;
|
||||
import org.hibernate.sql.SelectFragment;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class AbstractTableBasedBulkIdHandler {
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
private final HqlSqlWalker walker;
|
||||
|
||||
public AbstractTableBasedBulkIdHandler(SessionFactoryImplementor sessionFactory, HqlSqlWalker walker) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
this.walker = walker;
|
||||
}
|
||||
|
||||
protected SessionFactoryImplementor factory() {
|
||||
return sessionFactory;
|
||||
}
|
||||
|
||||
protected HqlSqlWalker walker() {
|
||||
return walker;
|
||||
}
|
||||
|
||||
protected JDBCException convert(SQLException e, String message, String sql) {
|
||||
throw factory().getSQLExceptionHelper().convert( e, message, sql );
|
||||
}
|
||||
|
||||
protected static class ProcessedWhereClause {
|
||||
public static final ProcessedWhereClause NO_WHERE_CLAUSE = new ProcessedWhereClause();
|
||||
|
||||
private final String userWhereClauseFragment;
|
||||
private final List<ParameterSpecification> idSelectParameterSpecifications;
|
||||
|
||||
private ProcessedWhereClause() {
|
||||
this( "", Collections.<ParameterSpecification>emptyList() );
|
||||
}
|
||||
|
||||
public ProcessedWhereClause(String userWhereClauseFragment, List<ParameterSpecification> idSelectParameterSpecifications) {
|
||||
this.userWhereClauseFragment = userWhereClauseFragment;
|
||||
this.idSelectParameterSpecifications = idSelectParameterSpecifications;
|
||||
}
|
||||
|
||||
public String getUserWhereClauseFragment() {
|
||||
return userWhereClauseFragment;
|
||||
}
|
||||
|
||||
public List<ParameterSpecification> getIdSelectParameterSpecifications() {
|
||||
return idSelectParameterSpecifications;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ProcessedWhereClause processWhereClause(AST whereClause) {
|
||||
if ( whereClause.getNumberOfChildren() != 0 ) {
|
||||
// If a where clause was specified in the update/delete query, use it to limit the
|
||||
// returned ids here...
|
||||
try {
|
||||
SqlGenerator sqlGenerator = new SqlGenerator( sessionFactory );
|
||||
sqlGenerator.whereClause( whereClause );
|
||||
String userWhereClause = sqlGenerator.getSQL().substring( 7 ); // strip the " where "
|
||||
List<ParameterSpecification> idSelectParameterSpecifications = sqlGenerator.getCollectedParameters();
|
||||
|
||||
return new ProcessedWhereClause( userWhereClause, idSelectParameterSpecifications );
|
||||
}
|
||||
catch ( RecognitionException e ) {
|
||||
throw new HibernateException( "Unable to generate id select for DML operation", e );
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ProcessedWhereClause.NO_WHERE_CLAUSE;
|
||||
}
|
||||
}
|
||||
|
||||
protected String generateIdInsertSelect(Queryable persister, String tableAlias, ProcessedWhereClause whereClause) {
|
||||
Select select = new Select( sessionFactory.getDialect() );
|
||||
SelectFragment selectFragment = new SelectFragment()
|
||||
.addColumns( tableAlias, persister.getIdentifierColumnNames(), persister.getIdentifierColumnNames() );
|
||||
select.setSelectClause( selectFragment.toFragmentString().substring( 2 ) + extraIdSelectValues() );
|
||||
|
||||
String rootTableName = persister.getTableName();
|
||||
String fromJoinFragment = persister.fromJoinFragment( tableAlias, true, false );
|
||||
String whereJoinFragment = persister.whereJoinFragment( tableAlias, true, false );
|
||||
|
||||
select.setFromClause( rootTableName + ' ' + tableAlias + fromJoinFragment );
|
||||
|
||||
if ( whereJoinFragment == null ) {
|
||||
whereJoinFragment = "";
|
||||
}
|
||||
else {
|
||||
whereJoinFragment = whereJoinFragment.trim();
|
||||
if ( whereJoinFragment.startsWith( "and" ) ) {
|
||||
whereJoinFragment = whereJoinFragment.substring( 4 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( whereClause.userWhereClauseFragment.length() > 0 ) {
|
||||
if ( whereJoinFragment.length() > 0 ) {
|
||||
whereJoinFragment += " and ";
|
||||
}
|
||||
}
|
||||
|
||||
select.setWhereClause( whereJoinFragment + whereClause.userWhereClauseFragment );
|
||||
|
||||
InsertSelect insert = new InsertSelect( sessionFactory.getDialect() );
|
||||
if ( sessionFactory.getSettings().isCommentsEnabled() ) {
|
||||
insert.setComment( "insert-select for " + persister.getEntityName() + " ids" );
|
||||
}
|
||||
insert.setTableName( determineIdTableName( persister ) );
|
||||
insert.setSelect( select );
|
||||
return insert.toStatementString();
|
||||
}
|
||||
|
||||
protected String extraIdSelectValues() {
|
||||
return "";
|
||||
}
|
||||
|
||||
protected String determineIdTableName(Queryable persister) {
|
||||
return persister.getTemporaryIdTableName();
|
||||
}
|
||||
|
||||
protected String generateIdSubselect(Queryable persister) {
|
||||
return "select " + StringHelper.join( ", ", persister.getIdentifierColumnNames() ) +
|
||||
" from " + determineIdTableName( persister );
|
||||
}
|
||||
|
||||
protected void prepareForUse(Queryable persister, SessionImplementor session) {
|
||||
}
|
||||
|
||||
protected void releaseFromUse(Queryable persister, SessionImplementor session) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.hql.spi;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.cfg.Mappings;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
|
||||
import org.hibernate.engine.spi.Mapping;
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
|
||||
/**
|
||||
* Generalized strategy contract for handling multi-table bulk HQL operations.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface MultiTableBulkIdStrategy {
|
||||
/**
|
||||
* Prepare the strategy. Called as the SessionFactory is being built. Intended patterns here include:<ul>
|
||||
* <li>Adding tables to the passed Mappings, to be picked by by "schema management tools"</li>
|
||||
* <li>Manually creating the tables immediately through the passed JDBC Connection access</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param dialect The dialect
|
||||
* @param connectionAccess Access to the JDBC Connection
|
||||
* @param mappings The Hibernate Mappings object, for access to O/RM mapping information
|
||||
* @param mapping The Hibernate Mapping contract, mainly for use in DDL generation
|
||||
* @param settings Configuration settings
|
||||
*/
|
||||
public void prepare(Dialect dialect, JdbcConnectionAccess connectionAccess, Mappings mappings, Mapping mapping, Map settings);
|
||||
|
||||
/**
|
||||
* Release the strategy. Called as the SessionFactory is being shut down.
|
||||
*
|
||||
* @param dialect The dialect
|
||||
* @param connectionAccess Access to the JDBC Connection
|
||||
*/
|
||||
public void release(Dialect dialect, JdbcConnectionAccess connectionAccess);
|
||||
|
||||
/**
|
||||
* Handler for dealing with multi-table HQL bulk update statements.
|
||||
*/
|
||||
public static interface UpdateHandler {
|
||||
public Queryable getTargetedQueryable();
|
||||
public String[] getSqlStatements();
|
||||
|
||||
public int execute(SessionImplementor session, QueryParameters queryParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a handler capable of handling the bulk update indicated by the given walker.
|
||||
*
|
||||
* @param factory The SessionFactory
|
||||
* @param walker The AST walker, representing the update query
|
||||
*
|
||||
* @return The handler
|
||||
*/
|
||||
public UpdateHandler buildUpdateHandler(SessionFactoryImplementor factory, HqlSqlWalker walker);
|
||||
|
||||
/**
|
||||
* Handler for dealing with multi-table HQL bulk delete statements.
|
||||
*/
|
||||
public static interface DeleteHandler {
|
||||
public Queryable getTargetedQueryable();
|
||||
public String[] getSqlStatements();
|
||||
|
||||
public int execute(SessionImplementor session, QueryParameters queryParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a handler capable of handling the bulk delete indicated by the given walker.
|
||||
*
|
||||
* @param factory The SessionFactory
|
||||
* @param walker The AST walker, representing the delete query
|
||||
*
|
||||
* @return The handler
|
||||
*/
|
||||
public DeleteHandler buildDeleteHandler(SessionFactoryImplementor factory, HqlSqlWalker walker);
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.hql.spi;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.cfg.Mappings;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
|
||||
import org.hibernate.engine.spi.Mapping;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.internal.AbstractSessionImpl;
|
||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
import org.hibernate.type.UUIDCharType;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class PersistentTableBulkIdStrategy implements MultiTableBulkIdStrategy {
|
||||
private static final Logger log = Logger.getLogger( PersistentTableBulkIdStrategy.class );
|
||||
|
||||
public static final String CLEAN_UP_ID_TABLES = "hibernate.hql.bulk_id_strategy.persistent.clean_up";
|
||||
|
||||
private boolean cleanUpTables;
|
||||
private List<String> tableCleanUpDdl;
|
||||
|
||||
@Override
|
||||
public void prepare(
|
||||
Dialect dialect,
|
||||
JdbcConnectionAccess connectionAccess,
|
||||
Mappings mappings,
|
||||
Mapping mapping,
|
||||
Map settings) {
|
||||
cleanUpTables = ConfigurationHelper.getBoolean( CLEAN_UP_ID_TABLES, settings, false );
|
||||
final Iterator<PersistentClass> entityMappings = mappings.iterateClasses();
|
||||
final List<Table> idTableDefinitions = new ArrayList<Table>();
|
||||
while ( entityMappings.hasNext() ) {
|
||||
final PersistentClass entityMapping = entityMappings.next();
|
||||
final Table idTableDefinition = generateIdTableDefinition( entityMapping );
|
||||
idTableDefinitions.add( idTableDefinition );
|
||||
}
|
||||
exportTableDefinitions( idTableDefinitions, dialect, connectionAccess, mappings, mapping );
|
||||
}
|
||||
|
||||
protected Table generateIdTableDefinition(PersistentClass entityMapping) {
|
||||
Table idTable = new Table( entityMapping.getTemporaryIdTableName() );
|
||||
Iterator itr = entityMapping.getIdentityTable().getPrimaryKey().getColumnIterator();
|
||||
while( itr.hasNext() ) {
|
||||
Column column = (Column) itr.next();
|
||||
idTable.addColumn( column.clone() );
|
||||
}
|
||||
Column sessionIdColumn = new Column( "hib_sess_id" );
|
||||
sessionIdColumn.setSqlType( "CHAR(36)" );
|
||||
sessionIdColumn.setComment( "Used to hold the Hibernate Session identifier" );
|
||||
idTable.addColumn( sessionIdColumn );
|
||||
|
||||
idTable.setComment( "Used to hold id values for the " + entityMapping.getEntityName() + " class" );
|
||||
return idTable;
|
||||
}
|
||||
|
||||
protected void exportTableDefinitions(
|
||||
List<Table> idTableDefinitions,
|
||||
Dialect dialect,
|
||||
JdbcConnectionAccess connectionAccess,
|
||||
Mappings mappings,
|
||||
Mapping mapping) {
|
||||
try {
|
||||
Connection connection = connectionAccess.obtainConnection();
|
||||
|
||||
try {
|
||||
Statement statement = connection.createStatement();
|
||||
|
||||
for ( Table idTableDefinition : idTableDefinitions ) {
|
||||
if ( cleanUpTables ) {
|
||||
if ( tableCleanUpDdl == null ) {
|
||||
tableCleanUpDdl = new ArrayList<String>();
|
||||
}
|
||||
tableCleanUpDdl.add( idTableDefinition.sqlDropString( dialect, null, null ) );
|
||||
}
|
||||
try {
|
||||
statement.execute( idTableDefinition.sqlCreateString( dialect, mapping, null, null ) );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
log.debugf( "Error attempting to export id-table [%s] : %s", idTableDefinition.getName(), e.getMessage() );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
log.error( "Unable to use JDBC Connection to create Statement", e );
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
connectionAccess.releaseConnection( connection );
|
||||
}
|
||||
catch (SQLException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
log.error( "Unable obtain JDBC Connection", e );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(Dialect dialect, JdbcConnectionAccess connectionAccess) {
|
||||
if ( ! cleanUpTables ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Connection connection = connectionAccess.obtainConnection();
|
||||
|
||||
try {
|
||||
Statement statement = connection.createStatement();
|
||||
|
||||
for ( String cleanupDdl : tableCleanUpDdl ) {
|
||||
try {
|
||||
statement.execute( cleanupDdl );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
log.debugf( "Error attempting to cleanup id-table : [%s]", e.getMessage() );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
log.error( "Unable to use JDBC Connection to create Statement", e );
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
connectionAccess.releaseConnection( connection );
|
||||
}
|
||||
catch (SQLException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
log.error( "Unable obtain JDBC Connection", e );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateHandler buildUpdateHandler(SessionFactoryImplementor factory, HqlSqlWalker walker) {
|
||||
return new TableBasedUpdateHandlerImpl( factory, walker ) {
|
||||
@Override
|
||||
protected String extraIdSelectValues() {
|
||||
return "cast(? as char)";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String generateIdSubselect(Queryable persister) {
|
||||
return super.generateIdSubselect( persister ) + " where hib_sess_id=?";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int handlePrependedParametersOnIdSelection(PreparedStatement ps, SessionImplementor session, int pos) throws SQLException {
|
||||
if ( ! AbstractSessionImpl.class.isInstance( session ) ) {
|
||||
throw new HibernateException( "Only available on SessionImpl instances" );
|
||||
}
|
||||
UUIDCharType.INSTANCE.set( ps, ( (AbstractSessionImpl) session ).getSessionIdentifier(), pos, session );
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleAddedParametersOnUpdate(PreparedStatement ps, SessionImplementor session, int position) throws SQLException {
|
||||
if ( ! AbstractSessionImpl.class.isInstance( session ) ) {
|
||||
throw new HibernateException( "Only available on SessionImpl instances" );
|
||||
}
|
||||
UUIDCharType.INSTANCE.set( ps, ( (AbstractSessionImpl) session ).getSessionIdentifier(), position, session );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeleteHandler buildDeleteHandler(SessionFactoryImplementor factory, HqlSqlWalker walker) {
|
||||
return new TableBasedDeleteHandlerImpl( factory, walker ) {
|
||||
@Override
|
||||
protected String extraIdSelectValues() {
|
||||
return "cast(? as char)";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String generateIdSubselect(Queryable persister) {
|
||||
return super.generateIdSubselect( persister ) + " where hib_sess_id=?";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int handlePrependedParametersOnIdSelection(PreparedStatement ps, SessionImplementor session, int pos) throws SQLException {
|
||||
if ( ! AbstractSessionImpl.class.isInstance( session ) ) {
|
||||
throw new HibernateException( "Only available on SessionImpl instances" );
|
||||
}
|
||||
UUIDCharType.INSTANCE.set( ps, ( (AbstractSessionImpl) session ).getSessionIdentifier(), pos, session );
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleAddedParametersOnDelete(PreparedStatement ps, SessionImplementor session) throws SQLException {
|
||||
if ( ! AbstractSessionImpl.class.isInstance( session ) ) {
|
||||
throw new HibernateException( "Only available on SessionImpl instances" );
|
||||
}
|
||||
UUIDCharType.INSTANCE.set( ps, ( (AbstractSessionImpl) session ).getSessionIdentifier(), 1, session );
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.hql.spi;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.internal.ast.tree.DeleteStatement;
|
||||
import org.hibernate.hql.internal.ast.tree.FromElement;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.param.ParameterSpecification;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
import org.hibernate.sql.Delete;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
class TableBasedDeleteHandlerImpl
|
||||
extends AbstractTableBasedBulkIdHandler
|
||||
implements MultiTableBulkIdStrategy.DeleteHandler {
|
||||
private static final Logger log = Logger.getLogger( TableBasedDeleteHandlerImpl.class );
|
||||
|
||||
private final Queryable targetedPersister;
|
||||
|
||||
private final String idInsertSelect;
|
||||
private final List<ParameterSpecification> idSelectParameterSpecifications;
|
||||
private final String[] deletes;
|
||||
|
||||
TableBasedDeleteHandlerImpl(SessionFactoryImplementor factory, HqlSqlWalker walker) {
|
||||
super( factory, walker );
|
||||
|
||||
DeleteStatement deleteStatement = ( DeleteStatement ) walker.getAST();
|
||||
FromElement fromElement = deleteStatement.getFromClause().getFromElement();
|
||||
|
||||
this.targetedPersister = fromElement.getQueryable();
|
||||
final String bulkTargetAlias = fromElement.getTableAlias();
|
||||
|
||||
final ProcessedWhereClause processedWhereClause = processWhereClause( deleteStatement.getWhereClause() );
|
||||
this.idSelectParameterSpecifications = processedWhereClause.getIdSelectParameterSpecifications();
|
||||
this.idInsertSelect = generateIdInsertSelect( targetedPersister, bulkTargetAlias, processedWhereClause );
|
||||
log.tracev( "Generated ID-INSERT-SELECT SQL (multi-table delete) : {0}", idInsertSelect );
|
||||
|
||||
String[] tableNames = targetedPersister.getConstraintOrderedTableNameClosure();
|
||||
String[][] columnNames = targetedPersister.getContraintOrderedTableKeyColumnClosure();
|
||||
String idSubselect = generateIdSubselect( targetedPersister );
|
||||
|
||||
deletes = new String[tableNames.length];
|
||||
for ( int i = tableNames.length - 1; i >= 0; 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
|
||||
final Delete delete = new Delete()
|
||||
.setTableName( tableNames[i] )
|
||||
.setWhere( "(" + StringHelper.join( ", ", columnNames[i] ) + ") IN (" + idSubselect + ")" );
|
||||
if ( factory().getSettings().isCommentsEnabled() ) {
|
||||
delete.setComment( "bulk delete" );
|
||||
}
|
||||
|
||||
deletes[i] = delete.toStatementString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Queryable getTargetedQueryable() {
|
||||
return targetedPersister;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSqlStatements() {
|
||||
return deletes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int execute(SessionImplementor session, QueryParameters queryParameters) {
|
||||
prepareForUse( targetedPersister, session );
|
||||
try {
|
||||
PreparedStatement ps = null;
|
||||
int resultCount = 0;
|
||||
try {
|
||||
try {
|
||||
ps = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( idInsertSelect, false );
|
||||
int pos = 1;
|
||||
pos += handlePrependedParametersOnIdSelection( ps, session, pos );
|
||||
for ( ParameterSpecification parameterSpecification : idSelectParameterSpecifications ) {
|
||||
pos += parameterSpecification.bind( ps, queryParameters, session, pos );
|
||||
}
|
||||
resultCount = ps.executeUpdate();
|
||||
}
|
||||
finally {
|
||||
if ( ps != null ) {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( SQLException e ) {
|
||||
throw convert( e, "could not insert/select ids for bulk delete", idInsertSelect );
|
||||
}
|
||||
|
||||
// Start performing the deletes
|
||||
for ( String delete : deletes ) {
|
||||
try {
|
||||
try {
|
||||
ps = session.getTransactionCoordinator()
|
||||
.getJdbcCoordinator()
|
||||
.getStatementPreparer()
|
||||
.prepareStatement( delete, false );
|
||||
handleAddedParametersOnDelete( ps, session );
|
||||
ps.executeUpdate();
|
||||
}
|
||||
finally {
|
||||
if ( ps != null ) {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw convert( e, "error performing bulk delete", delete );
|
||||
}
|
||||
}
|
||||
|
||||
return resultCount;
|
||||
|
||||
}
|
||||
finally {
|
||||
releaseFromUse( targetedPersister, session );
|
||||
}
|
||||
}
|
||||
|
||||
protected int handlePrependedParametersOnIdSelection(PreparedStatement ps, SessionImplementor session, int pos) throws SQLException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected void handleAddedParametersOnDelete(PreparedStatement ps, SessionImplementor session) throws SQLException {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.hql.spi;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.internal.ast.tree.AssignmentSpecification;
|
||||
import org.hibernate.hql.internal.ast.tree.FromElement;
|
||||
import org.hibernate.hql.internal.ast.tree.UpdateStatement;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.param.ParameterSpecification;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
import org.hibernate.sql.Update;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class TableBasedUpdateHandlerImpl
|
||||
extends AbstractTableBasedBulkIdHandler
|
||||
implements MultiTableBulkIdStrategy.UpdateHandler {
|
||||
|
||||
private static final Logger log = Logger.getLogger( TableBasedUpdateHandlerImpl.class );
|
||||
|
||||
private final Queryable targetedPersister;
|
||||
|
||||
private final String idInsertSelect;
|
||||
private final List<ParameterSpecification> idSelectParameterSpecifications;
|
||||
|
||||
private final String[] updates;
|
||||
private final ParameterSpecification[][] assignmentParameterSpecifications;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
TableBasedUpdateHandlerImpl(SessionFactoryImplementor factory, HqlSqlWalker walker) {
|
||||
super( factory, walker );
|
||||
|
||||
UpdateStatement updateStatement = ( UpdateStatement ) walker.getAST();
|
||||
FromElement fromElement = updateStatement.getFromClause().getFromElement();
|
||||
|
||||
this.targetedPersister = fromElement.getQueryable();
|
||||
final String bulkTargetAlias = fromElement.getTableAlias();
|
||||
|
||||
final ProcessedWhereClause processedWhereClause = processWhereClause( updateStatement.getWhereClause() );
|
||||
this.idSelectParameterSpecifications = processedWhereClause.getIdSelectParameterSpecifications();
|
||||
this.idInsertSelect = generateIdInsertSelect( targetedPersister, bulkTargetAlias, processedWhereClause );
|
||||
log.tracev( "Generated ID-INSERT-SELECT SQL (multi-table update) : {0}", idInsertSelect );
|
||||
|
||||
String[] tableNames = targetedPersister.getConstraintOrderedTableNameClosure();
|
||||
String[][] columnNames = targetedPersister.getContraintOrderedTableKeyColumnClosure();
|
||||
String idSubselect = generateIdSubselect( targetedPersister );
|
||||
|
||||
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<ParameterSpecification>();
|
||||
final Update update = new Update( factory().getDialect() )
|
||||
.setTableName( tableNames[tableIndex] )
|
||||
.setWhere( "(" + StringHelper.join( ", ", columnNames[tableIndex] ) + ") IN (" + idSubselect + ")" );
|
||||
if ( factory().getSettings().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 ) {
|
||||
for ( int paramIndex = 0; paramIndex < assignmentSpecification.getParameters().length; paramIndex++ ) {
|
||||
parameterList.add( assignmentSpecification.getParameters()[paramIndex] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( affected ) {
|
||||
updates[tableIndex] = update.toStatementString();
|
||||
assignmentParameterSpecifications[tableIndex] = parameterList.toArray( new ParameterSpecification[parameterList.size()] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Queryable getTargetedQueryable() {
|
||||
return targetedPersister;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSqlStatements() {
|
||||
return updates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int execute(SessionImplementor session, QueryParameters queryParameters) {
|
||||
prepareForUse( targetedPersister, session );
|
||||
try {
|
||||
// First, save off the pertinent ids, as the return value
|
||||
PreparedStatement ps = null;
|
||||
int resultCount = 0;
|
||||
try {
|
||||
try {
|
||||
ps = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( idInsertSelect, false );
|
||||
int sum = 1;
|
||||
sum += handlePrependedParametersOnIdSelection( ps, session, sum );
|
||||
for ( ParameterSpecification parameterSpecification : idSelectParameterSpecifications ) {
|
||||
sum += parameterSpecification.bind( ps, queryParameters, session, sum );
|
||||
}
|
||||
resultCount = ps.executeUpdate();
|
||||
}
|
||||
finally {
|
||||
if ( ps != null ) {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( SQLException e ) {
|
||||
throw convert( e, "could not insert/select ids for bulk update", idInsertSelect );
|
||||
}
|
||||
|
||||
// Start performing the updates
|
||||
for ( int i = 0; i < updates.length; i++ ) {
|
||||
if ( updates[i] == null ) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
try {
|
||||
ps = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( updates[i], false );
|
||||
if ( assignmentParameterSpecifications[i] != null ) {
|
||||
int position = 1; // jdbc params are 1-based
|
||||
for ( int x = 0; x < assignmentParameterSpecifications[i].length; x++ ) {
|
||||
position += assignmentParameterSpecifications[i][x].bind( ps, queryParameters, session, position );
|
||||
}
|
||||
handleAddedParametersOnUpdate( ps, session, position );
|
||||
}
|
||||
ps.executeUpdate();
|
||||
}
|
||||
finally {
|
||||
if ( ps != null ) {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( SQLException e ) {
|
||||
throw convert( e, "error performing bulk update", updates[i] );
|
||||
}
|
||||
}
|
||||
|
||||
return resultCount;
|
||||
}
|
||||
finally {
|
||||
releaseFromUse( targetedPersister, session );
|
||||
}
|
||||
}
|
||||
|
||||
protected int handlePrependedParametersOnIdSelection(PreparedStatement ps, SessionImplementor session, int pos) throws SQLException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected void handleAddedParametersOnUpdate(PreparedStatement ps, SessionImplementor session, int position) throws SQLException {
|
||||
//To change body of created methods use File | Settings | File Templates.
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
|
@ -21,122 +21,155 @@
|
|||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.hql.internal.ast.exec;
|
||||
package org.hibernate.hql.spi;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLWarning;
|
||||
import java.sql.Statement;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import antlr.RecognitionException;
|
||||
import antlr.collections.AST;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.action.internal.BulkOperationCleanupAction;
|
||||
import org.hibernate.cfg.Mappings;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
|
||||
import org.hibernate.engine.spi.Mapping;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.internal.ast.SqlGenerator;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.jdbc.AbstractWork;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
import org.hibernate.sql.InsertSelect;
|
||||
import org.hibernate.sql.Select;
|
||||
import org.hibernate.sql.SelectFragment;
|
||||
|
||||
/**
|
||||
* Implementation of AbstractStatementExecutor.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractStatementExecutor implements StatementExecutor {
|
||||
public class TemporaryTableBulkIdStrategy implements MultiTableBulkIdStrategy {
|
||||
public static final TemporaryTableBulkIdStrategy INSTANCE = new TemporaryTableBulkIdStrategy();
|
||||
|
||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class,
|
||||
AbstractStatementExecutor.class.getName());
|
||||
private static final CoreMessageLogger log = Logger.getMessageLogger(
|
||||
CoreMessageLogger.class,
|
||||
TemporaryTableBulkIdStrategy.class.getName()
|
||||
);
|
||||
|
||||
private final HqlSqlWalker walker;
|
||||
private List idSelectParameterSpecifications = Collections.EMPTY_LIST;
|
||||
|
||||
public AbstractStatementExecutor( HqlSqlWalker walker,
|
||||
CoreMessageLogger log ) {
|
||||
this.walker = walker;
|
||||
@Override
|
||||
public void prepare(Dialect dialect, JdbcConnectionAccess connectionAccess, Mappings mappings, Mapping mapping) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
protected HqlSqlWalker getWalker() {
|
||||
return walker;
|
||||
@Override
|
||||
public void release(Dialect dialect, JdbcConnectionAccess connectionAccess) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
protected SessionFactoryImplementor getFactory() {
|
||||
return walker.getSessionFactoryHelper().getFactory();
|
||||
@Override
|
||||
public UpdateHandler buildUpdateHandler(SessionFactoryImplementor factory, HqlSqlWalker walker) {
|
||||
return new TableBasedUpdateHandlerImpl( factory, walker ) {
|
||||
@Override
|
||||
protected void prepareForUse(Queryable persister, SessionImplementor session) {
|
||||
createTempTable( persister, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void releaseFromUse(Queryable persister, SessionImplementor session) {
|
||||
releaseTempTable( persister, session );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected List getIdSelectParameterSpecifications() {
|
||||
return idSelectParameterSpecifications;
|
||||
@Override
|
||||
public DeleteHandler buildDeleteHandler(SessionFactoryImplementor factory, HqlSqlWalker walker) {
|
||||
return new TableBasedDeleteHandlerImpl( factory, walker ) {
|
||||
@Override
|
||||
protected void prepareForUse(Queryable persister, SessionImplementor session) {
|
||||
createTempTable( persister, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void releaseFromUse(Queryable persister, SessionImplementor session) {
|
||||
releaseTempTable( persister, session );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected abstract Queryable[] getAffectedQueryables();
|
||||
|
||||
protected String generateIdInsertSelect(Queryable persister, String tableAlias, AST whereClause) {
|
||||
Select select = new Select( getFactory().getDialect() );
|
||||
SelectFragment selectFragment = new SelectFragment()
|
||||
.addColumns( tableAlias, persister.getIdentifierColumnNames(), persister.getIdentifierColumnNames() );
|
||||
select.setSelectClause( selectFragment.toFragmentString().substring( 2 ) );
|
||||
|
||||
String rootTableName = persister.getTableName();
|
||||
String fromJoinFragment = persister.fromJoinFragment( tableAlias, true, false );
|
||||
String whereJoinFragment = persister.whereJoinFragment( tableAlias, true, false );
|
||||
|
||||
select.setFromClause( rootTableName + ' ' + tableAlias + fromJoinFragment );
|
||||
|
||||
if ( whereJoinFragment == null ) {
|
||||
whereJoinFragment = "";
|
||||
protected void createTempTable(Queryable persister, SessionImplementor session) {
|
||||
// Don't really know all the codes required to adequately decipher returned jdbc exceptions here.
|
||||
// simply allow the failure to be eaten and the subsequent insert-selects/deletes should fail
|
||||
TemporaryTableCreationWork work = new TemporaryTableCreationWork( persister );
|
||||
if ( shouldIsolateTemporaryTableDDL( session ) ) {
|
||||
session.getTransactionCoordinator()
|
||||
.getTransaction()
|
||||
.createIsolationDelegate()
|
||||
.delegateWork( work, session.getFactory().getSettings().isDataDefinitionInTransactionSupported() );
|
||||
}
|
||||
else {
|
||||
whereJoinFragment = whereJoinFragment.trim();
|
||||
if ( whereJoinFragment.startsWith( "and" ) ) {
|
||||
whereJoinFragment = whereJoinFragment.substring( 4 );
|
||||
}
|
||||
final Connection connection = session.getTransactionCoordinator()
|
||||
.getJdbcCoordinator()
|
||||
.getLogicalConnection()
|
||||
.getShareableConnectionProxy();
|
||||
work.execute( connection );
|
||||
session.getTransactionCoordinator()
|
||||
.getJdbcCoordinator()
|
||||
.getLogicalConnection()
|
||||
.afterStatementExecution();
|
||||
}
|
||||
|
||||
String userWhereClause = "";
|
||||
if ( whereClause.getNumberOfChildren() != 0 ) {
|
||||
// If a where clause was specified in the update/delete query, use it to limit the
|
||||
// returned ids here...
|
||||
try {
|
||||
SqlGenerator sqlGenerator = new SqlGenerator( getFactory() );
|
||||
sqlGenerator.whereClause( whereClause );
|
||||
userWhereClause = sqlGenerator.getSQL().substring( 7 ); // strip the " where "
|
||||
idSelectParameterSpecifications = sqlGenerator.getCollectedParameters();
|
||||
}
|
||||
catch ( RecognitionException e ) {
|
||||
throw new HibernateException( "Unable to generate id select for DML operation", e );
|
||||
}
|
||||
if ( whereJoinFragment.length() > 0 ) {
|
||||
whereJoinFragment += " and ";
|
||||
}
|
||||
}
|
||||
|
||||
select.setWhereClause( whereJoinFragment + userWhereClause );
|
||||
|
||||
InsertSelect insert = new InsertSelect( getFactory().getDialect() );
|
||||
if ( getFactory().getSettings().isCommentsEnabled() ) {
|
||||
insert.setComment( "insert-select for " + persister.getEntityName() + " ids" );
|
||||
}
|
||||
insert.setTableName( persister.getTemporaryIdTableName() );
|
||||
insert.setSelect( select );
|
||||
return insert.toStatementString();
|
||||
}
|
||||
|
||||
protected String generateIdSubselect(Queryable persister) {
|
||||
return "select " + StringHelper.join( ", ", persister.getIdentifierColumnNames() ) +
|
||||
" from " + persister.getTemporaryIdTableName();
|
||||
protected void releaseTempTable(Queryable persister, SessionImplementor session) {
|
||||
if ( session.getFactory().getDialect().dropTemporaryTableAfterUse() ) {
|
||||
TemporaryTableDropWork work = new TemporaryTableDropWork( persister, session );
|
||||
if ( shouldIsolateTemporaryTableDDL( session ) ) {
|
||||
session.getTransactionCoordinator()
|
||||
.getTransaction()
|
||||
.createIsolationDelegate()
|
||||
.delegateWork( work, session.getFactory().getSettings().isDataDefinitionInTransactionSupported() );
|
||||
}
|
||||
else {
|
||||
final Connection connection = session.getTransactionCoordinator()
|
||||
.getJdbcCoordinator()
|
||||
.getLogicalConnection()
|
||||
.getShareableConnectionProxy();
|
||||
work.execute( connection );
|
||||
session.getTransactionCoordinator()
|
||||
.getJdbcCoordinator()
|
||||
.getLogicalConnection()
|
||||
.afterStatementExecution();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// at the very least cleanup the data :)
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
final String sql = "delete from " + persister.getTemporaryIdTableName();
|
||||
ps = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( sql, false );
|
||||
ps.executeUpdate();
|
||||
}
|
||||
catch( Throwable t ) {
|
||||
log.unableToCleanupTemporaryIdTable(t);
|
||||
}
|
||||
finally {
|
||||
if ( ps != null ) {
|
||||
try {
|
||||
ps.close();
|
||||
}
|
||||
catch( Throwable ignore ) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "UnnecessaryUnboxing" })
|
||||
protected boolean shouldIsolateTemporaryTableDDL(SessionImplementor session) {
|
||||
Boolean dialectVote = session.getFactory().getDialect().performTemporaryTableDDLInIsolation();
|
||||
if ( dialectVote != null ) {
|
||||
return dialectVote.booleanValue();
|
||||
}
|
||||
return session.getFactory().getSettings().isDataDefinitionImplicitCommit();
|
||||
}
|
||||
|
||||
private static class TemporaryTableCreationWork extends AbstractWork {
|
||||
|
@ -168,46 +201,24 @@ public abstract class AbstractStatementExecutor implements StatementExecutor {
|
|||
}
|
||||
}
|
||||
catch( Exception e ) {
|
||||
LOG.debug( "unable to create temporary id table [" + e.getMessage() + "]" );
|
||||
log.debug( "unable to create temporary id table [" + e.getMessage() + "]" );
|
||||
}
|
||||
}
|
||||
}
|
||||
protected void createTemporaryTableIfNecessary(final Queryable persister, final SessionImplementor session) {
|
||||
// Don't really know all the codes required to adequately decipher returned jdbc exceptions here.
|
||||
// simply allow the failure to be eaten and the subsequent insert-selects/deletes should fail
|
||||
TemporaryTableCreationWork work = new TemporaryTableCreationWork( persister );
|
||||
if ( shouldIsolateTemporaryTableDDL() ) {
|
||||
session.getTransactionCoordinator()
|
||||
.getTransaction()
|
||||
.createIsolationDelegate()
|
||||
.delegateWork( work, getFactory().getSettings().isDataDefinitionInTransactionSupported() );
|
||||
}
|
||||
else {
|
||||
final Connection connection = session.getTransactionCoordinator()
|
||||
.getJdbcCoordinator()
|
||||
.getLogicalConnection()
|
||||
.getShareableConnectionProxy();
|
||||
work.execute( connection );
|
||||
session.getTransactionCoordinator()
|
||||
.getJdbcCoordinator()
|
||||
.getLogicalConnection()
|
||||
.afterStatementExecution();
|
||||
}
|
||||
}
|
||||
|
||||
private static SqlExceptionHelper.WarningHandler CREATION_WARNING_HANDLER = new SqlExceptionHelper.WarningHandlerLoggingSupport() {
|
||||
public boolean doProcess() {
|
||||
return LOG.isDebugEnabled();
|
||||
return log.isDebugEnabled();
|
||||
}
|
||||
|
||||
public void prepare(SQLWarning warning) {
|
||||
LOG.warningsCreatingTempTable( warning );
|
||||
log.warningsCreatingTempTable( warning );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logWarning(String description, String message) {
|
||||
LOG.debug( description );
|
||||
LOG.debug( message );
|
||||
log.debug( description );
|
||||
log.debug( message );
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -240,71 +251,9 @@ public abstract class AbstractStatementExecutor implements StatementExecutor {
|
|||
}
|
||||
}
|
||||
catch( Exception e ) {
|
||||
LOG.warn( "unable to drop temporary id table after use [" + e.getMessage() + "]" );
|
||||
log.warn( "unable to drop temporary id table after use [" + e.getMessage() + "]" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void dropTemporaryTableIfNecessary(final Queryable persister, final SessionImplementor session) {
|
||||
if ( getFactory().getDialect().dropTemporaryTableAfterUse() ) {
|
||||
TemporaryTableDropWork work = new TemporaryTableDropWork( persister, session );
|
||||
if ( shouldIsolateTemporaryTableDDL() ) {
|
||||
session.getTransactionCoordinator()
|
||||
.getTransaction()
|
||||
.createIsolationDelegate()
|
||||
.delegateWork( work, getFactory().getSettings().isDataDefinitionInTransactionSupported() );
|
||||
}
|
||||
else {
|
||||
final Connection connection = session.getTransactionCoordinator()
|
||||
.getJdbcCoordinator()
|
||||
.getLogicalConnection()
|
||||
.getShareableConnectionProxy();
|
||||
work.execute( connection );
|
||||
session.getTransactionCoordinator()
|
||||
.getJdbcCoordinator()
|
||||
.getLogicalConnection()
|
||||
.afterStatementExecution();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// at the very least cleanup the data :)
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
final String sql = "delete from " + persister.getTemporaryIdTableName();
|
||||
ps = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( sql, false );
|
||||
ps.executeUpdate();
|
||||
}
|
||||
catch( Throwable t ) {
|
||||
LOG.unableToCleanupTemporaryIdTable(t);
|
||||
}
|
||||
finally {
|
||||
if ( ps != null ) {
|
||||
try {
|
||||
ps.close();
|
||||
}
|
||||
catch( Throwable ignore ) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void coordinateSharedCacheCleanup(SessionImplementor session) {
|
||||
BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, getAffectedQueryables() );
|
||||
|
||||
if ( session.isEventSource() ) {
|
||||
( ( EventSource ) session ).getActionQueue().addAction( action );
|
||||
}
|
||||
else {
|
||||
action.getAfterTransactionCompletionProcess().doAfterTransactionCompletion( true, session );
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "UnnecessaryUnboxing" })
|
||||
protected boolean shouldIsolateTemporaryTableDDL() {
|
||||
Boolean dialectVote = getFactory().getDialect().performTemporaryTableDDLInIsolation();
|
||||
if (dialectVote != null) return dialectVote.booleanValue();
|
||||
return getFactory().getSettings().isDataDefinitionImplicitCommit();
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ import java.io.Serializable;
|
|||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
|
@ -52,6 +53,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.engine.transaction.spi.TransactionContext;
|
||||
import org.hibernate.engine.transaction.spi.TransactionEnvironment;
|
||||
import org.hibernate.id.uuid.StandardRandomStrategy;
|
||||
import org.hibernate.jdbc.WorkExecutor;
|
||||
import org.hibernate.jdbc.WorkExecutorVisitable;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
@ -317,6 +319,15 @@ public abstract class AbstractSessionImpl implements Serializable, SharedSession
|
|||
return jdbcConnectionAccess;
|
||||
}
|
||||
|
||||
private UUID sessionIdentifier;
|
||||
|
||||
public UUID getSessionIdentifier() {
|
||||
if ( sessionIdentifier == null ) {
|
||||
sessionIdentifier = StandardRandomStrategy.INSTANCE.generateUUID( this );
|
||||
}
|
||||
return sessionIdentifier;
|
||||
}
|
||||
|
||||
private static class NonContextualJdbcConnectionAccess implements JdbcConnectionAccess, Serializable {
|
||||
private final ConnectionProvider connectionProvider;
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.io.ObjectInputStream;
|
|||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -52,6 +53,7 @@ import org.hibernate.EntityNameResolver;
|
|||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Interceptor;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.MultiTenancyStrategy;
|
||||
import org.hibernate.ObjectNotFoundException;
|
||||
import org.hibernate.QueryException;
|
||||
import org.hibernate.Session;
|
||||
|
@ -88,6 +90,8 @@ import org.hibernate.dialect.Dialect;
|
|||
import org.hibernate.dialect.function.SQLFunction;
|
||||
import org.hibernate.dialect.function.SQLFunctionRegistry;
|
||||
import org.hibernate.engine.ResultSetMappingDefinition;
|
||||
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
|
||||
import org.hibernate.engine.profile.Association;
|
||||
|
@ -481,6 +485,15 @@ public final class SessionFactoryImpl
|
|||
|
||||
LOG.debug( "Instantiated session factory" );
|
||||
|
||||
settings.getMultiTableBulkIdStrategy().prepare(
|
||||
dialect,
|
||||
buildLocalConnectionAccess(),
|
||||
cfg.createMappings(),
|
||||
cfg.buildMapping(),
|
||||
properties
|
||||
);
|
||||
|
||||
|
||||
if ( settings.isAutoCreateSchema() ) {
|
||||
new SchemaExport( serviceRegistry, cfg )
|
||||
.setImportSqlCommandExtractor( serviceRegistry.getService( ImportSqlCommandExtractor.class ) )
|
||||
|
@ -556,6 +569,32 @@ public final class SessionFactoryImpl
|
|||
this.observer.sessionFactoryCreated( this );
|
||||
}
|
||||
|
||||
private JdbcConnectionAccess buildLocalConnectionAccess() {
|
||||
return new JdbcConnectionAccess() {
|
||||
@Override
|
||||
public Connection obtainConnection() throws SQLException {
|
||||
return settings.getMultiTenancyStrategy() == MultiTenancyStrategy.NONE
|
||||
? serviceRegistry.getService( ConnectionProvider.class ).getConnection()
|
||||
: serviceRegistry.getService( MultiTenantConnectionProvider.class ).getAnyConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseConnection(Connection connection) throws SQLException {
|
||||
if ( settings.getMultiTenancyStrategy() == MultiTenancyStrategy.NONE ) {
|
||||
serviceRegistry.getService( ConnectionProvider.class ).closeConnection( connection );
|
||||
}
|
||||
else {
|
||||
serviceRegistry.getService( MultiTenantConnectionProvider.class ).releaseAnyConnection( connection );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAggressiveRelease() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
private CustomEntityDirtinessStrategy determineCustomEntityDirtinessStrategy() {
|
||||
CustomEntityDirtinessStrategy defaultValue = new CustomEntityDirtinessStrategy() {
|
||||
|
@ -1344,6 +1383,8 @@ public final class SessionFactoryImpl
|
|||
|
||||
isClosed = true;
|
||||
|
||||
settings.getMultiTableBulkIdStrategy().release( dialect, buildLocalConnectionAccess() );
|
||||
|
||||
Iterator iter = entityPersisters.values().iterator();
|
||||
while ( iter.hasNext() ) {
|
||||
EntityPersister p = (EntityPersister) iter.next();
|
||||
|
|
|
@ -343,7 +343,8 @@ public class Column implements Selectable, Serializable, Cloneable {
|
|||
/**
|
||||
* Shallow copy, the value is not copied
|
||||
*/
|
||||
protected Object clone() {
|
||||
@Override
|
||||
public Column clone() {
|
||||
Column copy = new Column();
|
||||
copy.setLength( length );
|
||||
copy.setScale( scale );
|
||||
|
|
|
@ -768,14 +768,14 @@ public abstract class PersistentClass implements Serializable, Filterable, MetaA
|
|||
}
|
||||
|
||||
public void prepareTemporaryTables(Mapping mapping, Dialect dialect) {
|
||||
temporaryIdTableName = dialect.generateTemporaryTableName( getTable().getName() );
|
||||
if ( dialect.supportsTemporaryTables() ) {
|
||||
temporaryIdTableName = dialect.generateTemporaryTableName( getTable().getName() );
|
||||
Table table = new Table();
|
||||
table.setName( temporaryIdTableName );
|
||||
Iterator itr = getTable().getPrimaryKey().getColumnIterator();
|
||||
while( itr.hasNext() ) {
|
||||
Column column = (Column) itr.next();
|
||||
table.addColumn( (Column) column.clone() );
|
||||
table.addColumn( column.clone() );
|
||||
}
|
||||
temporaryIdTableDDL = table.sqlTemporaryTableCreateString( dialect, mapping );
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue