HHH-7725 - Make handling multi-table bulk HQL operations more pluggable
This commit is contained in:
parent
9f0bbe10a6
commit
0ab36bed8c
|
@ -178,6 +178,10 @@ subprojects { subProject ->
|
|||
)
|
||||
|
||||
test {
|
||||
// pass along command line defined system props (-D) to the test
|
||||
// pretty sure I used to have this limited to just certain prefixes, but that is no longer here
|
||||
// and I no longer remember that groovy-magic needed to accomplish that
|
||||
systemProperties = System.properties
|
||||
systemProperties['hibernate.test.validatefailureexpected'] = true
|
||||
maxHeapSize = "1024m"
|
||||
}
|
||||
|
|
|
@ -32,16 +32,18 @@ import antlr.collections.AST;
|
|||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.JDBCException;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
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.mapping.Table;
|
||||
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;
|
||||
import org.hibernate.sql.SelectValues;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
|
@ -50,9 +52,22 @@ public class AbstractTableBasedBulkIdHandler {
|
|||
private final SessionFactoryImplementor sessionFactory;
|
||||
private final HqlSqlWalker walker;
|
||||
|
||||
private final String catalog;
|
||||
private final String schema;
|
||||
|
||||
public AbstractTableBasedBulkIdHandler(SessionFactoryImplementor sessionFactory, HqlSqlWalker walker) {
|
||||
this( sessionFactory, walker, null, null );
|
||||
}
|
||||
|
||||
public AbstractTableBasedBulkIdHandler(
|
||||
SessionFactoryImplementor sessionFactory,
|
||||
HqlSqlWalker walker,
|
||||
String catalog,
|
||||
String schema) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
this.walker = walker;
|
||||
this.catalog = catalog;
|
||||
this.schema = schema;
|
||||
}
|
||||
|
||||
protected SessionFactoryImplementor factory() {
|
||||
|
@ -115,9 +130,10 @@ public class AbstractTableBasedBulkIdHandler {
|
|||
|
||||
protected String generateIdInsertSelect(Queryable persister, String tableAlias, ProcessedWhereClause whereClause) {
|
||||
Select select = new Select( sessionFactory.getDialect() );
|
||||
SelectFragment selectFragment = new SelectFragment()
|
||||
SelectValues selectClause = new SelectValues( sessionFactory.getDialect() )
|
||||
.addColumns( tableAlias, persister.getIdentifierColumnNames(), persister.getIdentifierColumnNames() );
|
||||
select.setSelectClause( selectFragment.toFragmentString().substring( 2 ) + extraIdSelectValues() );
|
||||
addAnyExtraIdSelectValues( selectClause );
|
||||
select.setSelectClause( selectClause.render() );
|
||||
|
||||
String rootTableName = persister.getTableName();
|
||||
String fromJoinFragment = persister.fromJoinFragment( tableAlias, true, false );
|
||||
|
@ -135,13 +151,12 @@ public class AbstractTableBasedBulkIdHandler {
|
|||
}
|
||||
}
|
||||
|
||||
if ( whereClause.userWhereClauseFragment.length() > 0 ) {
|
||||
if ( whereClause.getUserWhereClauseFragment().length() > 0 ) {
|
||||
if ( whereJoinFragment.length() > 0 ) {
|
||||
whereJoinFragment += " and ";
|
||||
}
|
||||
}
|
||||
|
||||
select.setWhereClause( whereJoinFragment + whereClause.userWhereClauseFragment );
|
||||
select.setWhereClause( whereJoinFragment + whereClause.getUserWhereClauseFragment() );
|
||||
|
||||
InsertSelect insert = new InsertSelect( sessionFactory.getDialect() );
|
||||
if ( sessionFactory.getSettings().isCommentsEnabled() ) {
|
||||
|
@ -152,12 +167,12 @@ public class AbstractTableBasedBulkIdHandler {
|
|||
return insert.toStatementString();
|
||||
}
|
||||
|
||||
protected String extraIdSelectValues() {
|
||||
return "";
|
||||
protected void addAnyExtraIdSelectValues(SelectValues selectClause) {
|
||||
}
|
||||
|
||||
protected String determineIdTableName(Queryable persister) {
|
||||
return persister.getTemporaryIdTableName();
|
||||
// todo : use the identifier/name qualifier service once we pull that over to master
|
||||
return Table.qualify( catalog, schema, persister.getTemporaryIdTableName() );
|
||||
}
|
||||
|
||||
protected String generateIdSubselect(Queryable persister) {
|
||||
|
|
|
@ -28,6 +28,7 @@ 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.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.Mapping;
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
|
@ -47,21 +48,21 @@ public interface MultiTableBulkIdStrategy {
|
|||
* <li>Manually creating the tables immediately through the passed JDBC Connection access</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param dialect The dialect
|
||||
* @param jdbcServices The JdbcService object
|
||||
* @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);
|
||||
public void prepare(JdbcServices jdbcServices, JdbcConnectionAccess connectionAccess, Mappings mappings, Mapping mapping, Map settings);
|
||||
|
||||
/**
|
||||
* Release the strategy. Called as the SessionFactory is being shut down.
|
||||
*
|
||||
* @param dialect The dialect
|
||||
* @param jdbcServices The JdbcService object
|
||||
* @param connectionAccess Access to the JDBC Connection
|
||||
*/
|
||||
public void release(Dialect dialect, JdbcConnectionAccess connectionAccess);
|
||||
public void release(JdbcServices jdbcServices, JdbcConnectionAccess connectionAccess);
|
||||
|
||||
/**
|
||||
* Handler for dealing with multi-table HQL bulk update statements.
|
||||
|
|
|
@ -36,27 +36,33 @@ import java.util.Map;
|
|||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.JDBCException;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
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.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.CoreMessageLogger;
|
||||
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.sql.SelectValues;
|
||||
import org.hibernate.type.UUIDCharType;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class PersistentTableBulkIdStrategy implements MultiTableBulkIdStrategy {
|
||||
private static final Logger log = Logger.getLogger( PersistentTableBulkIdStrategy.class );
|
||||
private static final CoreMessageLogger log = Logger.getMessageLogger(
|
||||
CoreMessageLogger.class,
|
||||
PersistentTableBulkIdStrategy.class.getName()
|
||||
);
|
||||
|
||||
public static final String CLEAN_UP_ID_TABLES = "hibernate.hql.bulk_id_strategy.persistent.clean_up";
|
||||
public static final String SCHEMA = "hibernate.hql.bulk_id_strategy.persistent.schema";
|
||||
|
@ -69,7 +75,7 @@ public class PersistentTableBulkIdStrategy implements MultiTableBulkIdStrategy {
|
|||
|
||||
@Override
|
||||
public void prepare(
|
||||
Dialect dialect,
|
||||
JdbcServices jdbcServices,
|
||||
JdbcConnectionAccess connectionAccess,
|
||||
Mappings mappings,
|
||||
Mapping mapping,
|
||||
|
@ -93,7 +99,7 @@ public class PersistentTableBulkIdStrategy implements MultiTableBulkIdStrategy {
|
|||
final Table idTableDefinition = generateIdTableDefinition( entityMapping );
|
||||
idTableDefinitions.add( idTableDefinition );
|
||||
}
|
||||
exportTableDefinitions( idTableDefinitions, dialect, connectionAccess, mappings, mapping );
|
||||
exportTableDefinitions( idTableDefinitions, jdbcServices, connectionAccess, mappings, mapping );
|
||||
}
|
||||
|
||||
protected Table generateIdTableDefinition(PersistentClass entityMapping) {
|
||||
|
@ -104,7 +110,7 @@ public class PersistentTableBulkIdStrategy implements MultiTableBulkIdStrategy {
|
|||
if ( schema != null ) {
|
||||
idTable.setSchema( schema );
|
||||
}
|
||||
Iterator itr = entityMapping.getIdentityTable().getPrimaryKey().getColumnIterator();
|
||||
Iterator itr = entityMapping.getTable().getPrimaryKey().getColumnIterator();
|
||||
while( itr.hasNext() ) {
|
||||
Column column = (Column) itr.next();
|
||||
idTable.addColumn( column.clone() );
|
||||
|
@ -120,12 +126,20 @@ public class PersistentTableBulkIdStrategy implements MultiTableBulkIdStrategy {
|
|||
|
||||
protected void exportTableDefinitions(
|
||||
List<Table> idTableDefinitions,
|
||||
Dialect dialect,
|
||||
JdbcServices jdbcServices,
|
||||
JdbcConnectionAccess connectionAccess,
|
||||
Mappings mappings,
|
||||
Mapping mapping) {
|
||||
try {
|
||||
Connection connection = connectionAccess.obtainConnection();
|
||||
Connection connection;
|
||||
try {
|
||||
connection = connectionAccess.obtainConnection();
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
// assume this comes from org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl
|
||||
log.debug( "Unable to obtain JDBC connection; assuming ID tables already exist or wont be needed" );
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Statement statement = connection.createStatement();
|
||||
|
@ -135,10 +149,12 @@ public class PersistentTableBulkIdStrategy implements MultiTableBulkIdStrategy {
|
|||
if ( tableCleanUpDdl == null ) {
|
||||
tableCleanUpDdl = new ArrayList<String>();
|
||||
}
|
||||
tableCleanUpDdl.add( idTableDefinition.sqlDropString( dialect, null, null ) );
|
||||
tableCleanUpDdl.add( idTableDefinition.sqlDropString( jdbcServices.getDialect(), null, null ) );
|
||||
}
|
||||
try {
|
||||
statement.execute( idTableDefinition.sqlCreateString( dialect, mapping, null, null ) );
|
||||
final String sql = idTableDefinition.sqlCreateString( jdbcServices.getDialect(), mapping, null, null );
|
||||
jdbcServices.getSqlStatementLogger().logStatement( sql );
|
||||
statement.execute( sql );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
log.debugf( "Error attempting to export id-table [%s] : %s", idTableDefinition.getName(), e.getMessage() );
|
||||
|
@ -162,8 +178,8 @@ public class PersistentTableBulkIdStrategy implements MultiTableBulkIdStrategy {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void release(Dialect dialect, JdbcConnectionAccess connectionAccess) {
|
||||
if ( ! cleanUpTables ) {
|
||||
public void release(JdbcServices jdbcServices, JdbcConnectionAccess connectionAccess) {
|
||||
if ( ! cleanUpTables || tableCleanUpDdl == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -175,6 +191,7 @@ public class PersistentTableBulkIdStrategy implements MultiTableBulkIdStrategy {
|
|||
|
||||
for ( String cleanupDdl : tableCleanUpDdl ) {
|
||||
try {
|
||||
jdbcServices.getSqlStatementLogger().logStatement( cleanupDdl );
|
||||
statement.execute( cleanupDdl );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
|
@ -202,8 +219,8 @@ public class PersistentTableBulkIdStrategy implements MultiTableBulkIdStrategy {
|
|||
public UpdateHandler buildUpdateHandler(SessionFactoryImplementor factory, HqlSqlWalker walker) {
|
||||
return new TableBasedUpdateHandlerImpl( factory, walker ) {
|
||||
@Override
|
||||
protected String extraIdSelectValues() {
|
||||
return "cast(? as char)";
|
||||
protected void addAnyExtraIdSelectValues(SelectValues selectClause) {
|
||||
selectClause.addParameter( Types.CHAR, 36 );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -213,32 +230,65 @@ public class PersistentTableBulkIdStrategy implements MultiTableBulkIdStrategy {
|
|||
|
||||
@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 );
|
||||
bindSessionIdentifier( ps, session, pos );
|
||||
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 );
|
||||
bindSessionIdentifier( ps, session, position );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void releaseFromUse(Queryable persister, SessionImplementor session) {
|
||||
// clean up our id-table rows
|
||||
cleanUpRows( determineIdTableName( persister ), session );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void bindSessionIdentifier(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 );
|
||||
}
|
||||
|
||||
private void cleanUpRows(String tableName, SessionImplementor session) {
|
||||
final String sql = "delete from " + tableName + " where hib_sess_id=?";
|
||||
try {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( sql, false );
|
||||
bindSessionIdentifier( ps, session, 1 );
|
||||
ps.executeUpdate();
|
||||
}
|
||||
finally {
|
||||
if ( ps != null ) {
|
||||
try {
|
||||
ps.close();
|
||||
}
|
||||
catch( Throwable ignore ) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw convert( session.getFactory(), e, "Unable to clean up id table [" + tableName + "]", sql );
|
||||
}
|
||||
}
|
||||
|
||||
protected JDBCException convert(SessionFactoryImplementor factory, SQLException e, String message, String sql) {
|
||||
throw factory.getSQLExceptionHelper().convert( e, message, sql );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeleteHandler buildDeleteHandler(SessionFactoryImplementor factory, HqlSqlWalker walker) {
|
||||
return new TableBasedDeleteHandlerImpl( factory, walker ) {
|
||||
@Override
|
||||
protected String extraIdSelectValues() {
|
||||
final Dialect dialect = factory().getDialect();
|
||||
return dialect.requiresCastingOfParametersInSelectClause()
|
||||
? dialect.cast( "?", Types.CHAR, 36 )
|
||||
: "?";
|
||||
protected void addAnyExtraIdSelectValues(SelectValues selectClause) {
|
||||
selectClause.addParameter( Types.CHAR, 36 );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -248,19 +298,19 @@ public class PersistentTableBulkIdStrategy implements MultiTableBulkIdStrategy {
|
|||
|
||||
@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 );
|
||||
bindSessionIdentifier( ps, session, pos );
|
||||
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 );
|
||||
bindSessionIdentifier( ps, session, 1 );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void releaseFromUse(Queryable persister, SessionImplementor session) {
|
||||
// clean up our id-table rows
|
||||
cleanUpRows( determineIdTableName( persister ), session );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ import org.hibernate.sql.Delete;
|
|||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
class TableBasedDeleteHandlerImpl
|
||||
public class TableBasedDeleteHandlerImpl
|
||||
extends AbstractTableBasedBulkIdHandler
|
||||
implements MultiTableBulkIdStrategy.DeleteHandler {
|
||||
private static final Logger log = Logger.getLogger( TableBasedDeleteHandlerImpl.class );
|
||||
|
|
|
@ -32,7 +32,6 @@ import java.util.Map;
|
|||
import org.jboss.logging.Logger;
|
||||
|
||||
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;
|
||||
|
@ -56,12 +55,12 @@ public class TemporaryTableBulkIdStrategy implements MultiTableBulkIdStrategy {
|
|||
);
|
||||
|
||||
@Override
|
||||
public void prepare(Dialect dialect, JdbcConnectionAccess connectionAccess, Mappings mappings, Mapping mapping, Map settings) {
|
||||
public void prepare(JdbcServices jdbcServices, JdbcConnectionAccess connectionAccess, Mappings mappings, Mapping mapping, Map settings) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(Dialect dialect, JdbcConnectionAccess connectionAccess) {
|
||||
public void release(JdbcServices jdbcServices, JdbcConnectionAccess connectionAccess) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
|
|
|
@ -486,7 +486,7 @@ public final class SessionFactoryImpl
|
|||
LOG.debug( "Instantiated session factory" );
|
||||
|
||||
settings.getMultiTableBulkIdStrategy().prepare(
|
||||
dialect,
|
||||
jdbcServices,
|
||||
buildLocalConnectionAccess(),
|
||||
cfg.createMappings(),
|
||||
cfg.buildMapping(),
|
||||
|
@ -1383,7 +1383,7 @@ public final class SessionFactoryImpl
|
|||
|
||||
isClosed = true;
|
||||
|
||||
settings.getMultiTableBulkIdStrategy().release( dialect, buildLocalConnectionAccess() );
|
||||
settings.getMultiTableBulkIdStrategy().release( jdbcServices, buildLocalConnectionAccess() );
|
||||
|
||||
Iterator iter = entityPersisters.values().iterator();
|
||||
while ( iter.hasNext() ) {
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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.sql;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
|
||||
/**
|
||||
* Models a SELECT values lists. Eventually, rather than Strings, pass in the Column/Formula representations (something
|
||||
* like {@link org.hibernate.sql.ordering.antlr.ColumnReference}/{@link org.hibernate.sql.ordering.antlr.FormulaReference}
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SelectValues {
|
||||
private static final Logger log = Logger.getLogger( SelectValues.class );
|
||||
|
||||
private static class SelectValue {
|
||||
private final String qualifier;
|
||||
private final String value;
|
||||
private final String alias;
|
||||
|
||||
private SelectValue(String qualifier, String value, String alias) {
|
||||
this.qualifier = qualifier;
|
||||
this.value = value;
|
||||
this.alias = alias;
|
||||
}
|
||||
}
|
||||
|
||||
private final Dialect dialect;
|
||||
private final ArrayList<SelectValue> selectValueList = new ArrayList<SelectValue>();
|
||||
|
||||
public SelectValues(Dialect dialect) {
|
||||
this.dialect = dialect;
|
||||
}
|
||||
|
||||
public SelectValues addColumns(String qualifier, String[] columnNames, String[] columnAliases) {
|
||||
for ( int i = 0; i < columnNames.length; i++ ) {
|
||||
if ( columnNames[i] != null ) {
|
||||
addColumn( qualifier, columnNames[i], columnAliases[i] );
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public SelectValues addColumn(String qualifier, String columnName, String columnAlias) {
|
||||
selectValueList.add( new SelectValue( qualifier, columnName, columnAlias ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
public SelectValues addParameter(int jdbcTypeCode, int length) {
|
||||
final String selectExpression = dialect.requiresCastingOfParametersInSelectClause()
|
||||
? dialect.cast( "?", jdbcTypeCode, length )
|
||||
: "?";
|
||||
selectValueList.add( new SelectValue( null, selectExpression, null ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
public SelectValues addParameter(int jdbcTypeCode, int precision, int scale) {
|
||||
final String selectExpression = dialect.requiresCastingOfParametersInSelectClause()
|
||||
? dialect.cast( "?", jdbcTypeCode, precision, scale )
|
||||
: "?";
|
||||
selectValueList.add( new SelectValue( null, selectExpression, null ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
public String render() {
|
||||
final StringBuilder buf = new StringBuilder( selectValueList.size() * 10 );
|
||||
final HashSet<String> uniqueAliases = new HashSet<String>();
|
||||
boolean firstExpression = true;
|
||||
for ( SelectValue selectValue : selectValueList ) {
|
||||
if ( selectValue.alias != null ) {
|
||||
if ( ! uniqueAliases.add( selectValue.alias ) ) {
|
||||
log.debug( "Skipping select-value with non-unique alias" );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( firstExpression ) {
|
||||
firstExpression = false;
|
||||
}
|
||||
else {
|
||||
buf.append( ", " );
|
||||
}
|
||||
|
||||
if ( selectValue.qualifier != null ) {
|
||||
buf.append( selectValue.qualifier ).append( '.' );
|
||||
}
|
||||
buf.append( selectValue.value );
|
||||
if ( selectValue.alias != null ) {
|
||||
buf.append( " as " ).append( selectValue.alias );
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue