/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2010, 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.internal.ast.exec; 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 org.hibernate.HibernateException; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.action.internal.BulkOperationCleanupAction; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; import org.hibernate.event.spi.EventSource; import org.hibernate.hql.internal.ast.HqlSqlWalker; import org.hibernate.hql.internal.ast.SqlGenerator; 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; import org.jboss.logging.Logger; import antlr.RecognitionException; import antlr.collections.AST; /** * Implementation of AbstractStatementExecutor. * * @author Steve Ebersole */ public abstract class AbstractStatementExecutor implements StatementExecutor { private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, AbstractStatementExecutor.class.getName()); private final HqlSqlWalker walker; private List idSelectParameterSpecifications = Collections.EMPTY_LIST; public AbstractStatementExecutor( HqlSqlWalker walker, CoreMessageLogger log ) { this.walker = walker; } protected HqlSqlWalker getWalker() { return walker; } protected SessionFactoryImplementor getFactory() { return walker.getSessionFactoryHelper().getFactory(); } protected List getIdSelectParameterSpecifications() { return idSelectParameterSpecifications; } 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 = ""; } else { whereJoinFragment = whereJoinFragment.trim(); if ( whereJoinFragment.startsWith( "and" ) ) { whereJoinFragment = whereJoinFragment.substring( 4 ); } } 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(); } private static class TemporaryTableCreationWork extends AbstractWork { private final Queryable persister; private TemporaryTableCreationWork(Queryable persister) { this.persister = persister; } @Override public void execute(Connection connection) { try { Statement statement = connection.createStatement(); try { statement.executeUpdate( persister.getTemporaryIdTableDDL() ); persister.getFactory() .getServiceRegistry() .getService( JdbcServices.class ) .getSqlExceptionHelper() .handleAndClearWarnings( statement, CREATION_WARNING_HANDLER ); } finally { try { statement.close(); } catch( Throwable ignore ) { // ignore } } } catch( Exception e ) { 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(); } public void prepare(SQLWarning warning) { LOG.warningsCreatingTempTable(warning); } @Override protected void logWarning(String description, String message) { LOG.debugf(description); LOG.debugf(message); } }; private static class TemporaryTableDropWork extends AbstractWork { private final Queryable persister; private final SessionImplementor session; private TemporaryTableDropWork(Queryable persister, SessionImplementor session) { this.persister = persister; this.session = session; } @Override public void execute(Connection connection) { final String command = session.getFactory().getDialect().getDropTemporaryTableString() + ' ' + persister.getTemporaryIdTableName(); try { Statement statement = connection.createStatement(); try { statement = connection.createStatement(); statement.executeUpdate( command ); } finally { try { statement.close(); } catch( Throwable ignore ) { // ignore } } } catch( Exception e ) { 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(); } }