HHH-6920 - HQL insert queries and identifier handling

This commit is contained in:
Steve Ebersole 2011-12-27 23:16:29 -06:00
parent d4e8e9973f
commit 1e591f09ac
11 changed files with 195 additions and 103 deletions

View File

@ -85,6 +85,7 @@ import org.hibernate.hql.internal.ast.util.NodeTraverser;
import org.hibernate.hql.internal.ast.util.SessionFactoryHelper;
import org.hibernate.hql.internal.ast.util.SyntheticAndFactory;
import org.hibernate.hql.spi.QueryTranslator;
import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PostInsertIdentifierGenerator;
import org.hibernate.id.SequenceGenerator;
@ -724,11 +725,6 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
postProcessDML( ( DeleteStatement ) delete );
}
public static boolean supportsIdGenWithBulkInsertion(IdentifierGenerator generator) {
return SequenceGenerator.class.isAssignableFrom( generator.getClass() )
|| PostInsertIdentifierGenerator.class.isAssignableFrom( generator.getClass() );
}
@Override
protected void postProcessInsert(AST insert) throws SemanticException, QueryException {
InsertStatement insertStatement = ( InsertStatement ) insert;
@ -738,37 +734,37 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
Queryable persister = insertStatement.getIntoClause().getQueryable();
if ( !insertStatement.getIntoClause().isExplicitIdInsertion() ) {
// We need to generate ids as part of this bulk insert.
//
// Note that this is only supported for sequence-style generators and
// post-insert-style generators; basically, only in-db generators
IdentifierGenerator generator = persister.getIdentifierGenerator();
if ( !supportsIdGenWithBulkInsertion( generator ) ) {
throw new QueryException( "can only generate ids as part of bulk insert with either sequence or post-insert style generators" );
// the insert did not explicitly reference the id. See if
// 1) that is allowed
// 2) whether we need to alter the SQL tree to account for id
final IdentifierGenerator generator = persister.getIdentifierGenerator();
if ( !BulkInsertionCapableIdentifierGenerator.class.isInstance( generator ) ) {
throw new QueryException(
"Invalid identifier generator encountered for implicit id handling as part of bulk insertions"
);
}
final BulkInsertionCapableIdentifierGenerator capableGenerator =
BulkInsertionCapableIdentifierGenerator.class.cast( generator );
if ( ! capableGenerator.supportsBulkInsertionIdentifierGeneration() ) {
throw new QueryException(
"Identifier generator reported it does not support implicit id handling as part of bulk insertions"
);
}
AST idSelectExprNode = null;
if ( SequenceGenerator.class.isAssignableFrom( generator.getClass() ) ) {
String seqName = ( String ) ( ( SequenceGenerator ) generator ).generatorKey();
String nextval = sessionFactoryHelper.getFactory().getDialect().getSelectSequenceNextValString( seqName );
idSelectExprNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, nextval );
}
else {
//Don't need this, because we should never ever be selecting no columns in an insert ... select...
//and because it causes a bug on DB2
/*String idInsertString = sessionFactoryHelper.getFactory().getDialect().getIdentityInsertString();
if ( idInsertString != null ) {
idSelectExprNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, idInsertString );
}*/
}
if ( idSelectExprNode != null ) {
AST currentFirstSelectExprNode = selectClause.getFirstChild();
selectClause.setFirstChild( idSelectExprNode );
idSelectExprNode.setNextSibling( currentFirstSelectExprNode );
insertStatement.getIntoClause().prependIdColumnSpec();
final String fragment = capableGenerator.determineBulkInsertionIdentifierGenerationSelectFragment(
sessionFactoryHelper.getFactory().getDialect()
);
if ( fragment != null ) {
// we got a fragment from the generator, so alter the sql tree...
//
// first, wrap the fragment as a node
AST fragmentNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, fragment );
// next, rearrange the SQL tree to add the fragment node as the first select expression
AST originalFirstSelectExprNode = selectClause.getFirstChild();
selectClause.setFirstChild( fragmentNode );
fragmentNode.setNextSibling( originalFirstSelectExprNode );
// finally, prepend the id column name(s) to the insert-spec
insertStatement.getIntoClause().prependIdColumnSpec();
}
}

View File

@ -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-2011, 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,24 +20,33 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.id;
import java.io.Serializable;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionImplementor;
/**
* Basic implementation of the {@link PostInsertIdentifierGenerator}
* contract.
* Basic implementation of the {@link PostInsertIdentifierGenerator} contract.
*
* @author Gavin King
*/
public abstract class AbstractPostInsertGenerator implements PostInsertIdentifierGenerator {
/**
* {@inheritDoc}
*/
public abstract class AbstractPostInsertGenerator
implements PostInsertIdentifierGenerator, BulkInsertionCapableIdentifierGenerator {
@Override
public Serializable generate(SessionImplementor s, Object obj) {
return IdentifierGeneratorHelper.POST_INSERT_INDICATOR;
}
@Override
public boolean supportsBulkInsertionIdentifierGeneration() {
return true;
}
@Override
public String determineBulkInsertionIdentifierGenerationSelectFragment(Dialect dialect) {
return null;
}
}

View File

@ -0,0 +1,53 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, 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.id;
import org.hibernate.dialect.Dialect;
/**
* Specialized contract for {@link IdentifierGenerator} implementations capable of being used in conjunction
* with HQL insert statements.
*
* @author Steve Ebersole
*/
public interface BulkInsertionCapableIdentifierGenerator extends IdentifierGenerator {
/**
* Given the configuration of this generator, is identifier generation as part of bulk insertion supported?
* <p/>
* IMPL NOTE : Mainly here to allow stuff like SequenceStyleGenerator which *can* support this based on
* configuration
*
* @return {@code true} if bulk insertions are supported; {@code false} otherwise.
*/
public boolean supportsBulkInsertionIdentifierGeneration();
/**
* Return the select expression fragment, if any, that generates the identifier values.
*
* @param dialect The dialect against which the insert will be performed.
*
* @return The identifier value generation fragment (SQL). {@code null} indicates that no fragment is needed.
*/
public String determineBulkInsertionIdentifierGenerationSelectFragment(Dialect dialect);
}

View File

@ -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-2011, 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,9 +20,9 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.id;
import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.id;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@ -35,7 +36,6 @@ import org.hibernate.MappingException;
import org.hibernate.cfg.ObjectNameNormalizer;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.mapping.Table;
import org.hibernate.type.Type;
@ -52,9 +52,10 @@ import org.hibernate.type.Type;
* @see TableHiLoGenerator
* @author Gavin King
*/
public class SequenceGenerator implements PersistentIdentifierGenerator, Configurable {
public class SequenceGenerator
implements PersistentIdentifierGenerator, BulkInsertionCapableIdentifierGenerator, Configurable {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, SequenceGenerator.class.getName());
private static final Logger LOG = Logger.getLogger( SequenceGenerator.class.getName() );
/**
* The sequence parameter
@ -76,6 +77,15 @@ public class SequenceGenerator implements PersistentIdentifierGenerator, Configu
return identifierType;
}
public Object generatorKey() {
return getSequenceName();
}
public String getSequenceName() {
return sequenceName;
}
@Override
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
ObjectNameNormalizer normalizer = ( ObjectNameNormalizer ) params.get( IDENTIFIER_NORMALIZER );
sequenceName = normalizer.normalizeIdentifierQuoting(
@ -101,6 +111,7 @@ public class SequenceGenerator implements PersistentIdentifierGenerator, Configu
sql = dialect.getSequenceNextValString( sequenceName );
}
@Override
public Serializable generate(SessionImplementor session, Object obj) {
return generateHolder( session ).makeValue();
}
@ -139,23 +150,28 @@ public class SequenceGenerator implements PersistentIdentifierGenerator, Configu
return IdentifierGeneratorHelper.getIntegralDataTypeHolder( identifierType.getReturnedClass() );
}
@Override
@SuppressWarnings( {"deprecation"})
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
String[] ddl = dialect.getCreateSequenceStrings(sequenceName);
String[] ddl = dialect.getCreateSequenceStrings( sequenceName );
if ( parameters != null ) {
ddl[ddl.length - 1] += ' ' + parameters;
}
return ddl;
}
@Override
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
return dialect.getDropSequenceStrings(sequenceName);
}
public Object generatorKey() {
return sequenceName;
@Override
public boolean supportsBulkInsertionIdentifierGeneration() {
return true;
}
public String getSequenceName() {
return sequenceName;
@Override
public String determineBulkInsertionIdentifierGenerationSelectFragment(Dialect dialect) {
return dialect.getSelectSequenceNextValString( getSequenceName() );
}
}

View File

@ -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-2011, 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,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.id;
@ -55,11 +54,14 @@ import org.hibernate.type.Type;
*
* @author Steve Ebersole
*/
public class SequenceIdentityGenerator extends SequenceGenerator
public class SequenceIdentityGenerator
extends SequenceGenerator
implements PostInsertIdentifierGenerator {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class,
SequenceIdentityGenerator.class.getName());
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class,
SequenceIdentityGenerator.class.getName()
);
@Override
public Serializable generate(SessionImplementor s, Object obj) {

View File

@ -87,4 +87,11 @@ public interface DatabaseStructure {
* @return The drop commands.
*/
public String[] sqlDropStrings(Dialect dialect);
/**
* Is the structure physically a sequence?
*
* @return {@code true} if the actual database structure is a sequence; {@code false} otherwise.
*/
public boolean isPhysicalSequence();
}

View File

@ -66,39 +66,30 @@ public class SequenceStructure implements DatabaseStructure {
sql = dialect.getSequenceNextValString( sequenceName );
}
/**
* {@inheritDoc}
*/
@Override
public String getName() {
return sequenceName;
}
/**
* {@inheritDoc}
*/
@Override
public int getIncrementSize() {
return incrementSize;
}
/**
* {@inheritDoc}
*/
@Override
public int getTimesAccessed() {
return accessCounter;
}
/**
* {@inheritDoc}
*/
@Override
public int getInitialValue() {
return initialValue;
}
/**
* {@inheritDoc}
*/
@Override
public AccessCallback buildCallback(final SessionImplementor session) {
return new AccessCallback() {
@Override
public IntegralDataTypeHolder getNextValue() {
accessCounter++;
try {
@ -139,25 +130,24 @@ public class SequenceStructure implements DatabaseStructure {
};
}
/**
* {@inheritDoc}
*/
@Override
public void prepare(Optimizer optimizer) {
applyIncrementSizeToSourceValues = optimizer.applyIncrementSizeToSourceValues();
}
/**
* {@inheritDoc}
*/
@Override
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
int sourceIncrementSize = applyIncrementSizeToSourceValues ? incrementSize : 1;
return dialect.getCreateSequenceStrings( sequenceName, initialValue, sourceIncrementSize );
}
/**
* {@inheritDoc}
*/
@Override
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
return dialect.getDropSequenceStrings( sequenceName );
}
@Override
public boolean isPhysicalSequence() {
return true;
}
}

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.id.enhanced;
import java.io.Serializable;
import java.util.Properties;
@ -33,6 +34,7 @@ import org.hibernate.cfg.Environment;
import org.hibernate.cfg.ObjectNameNormalizer;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
import org.hibernate.id.Configurable;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.internal.CoreMessageLogger;
@ -95,10 +97,13 @@ import org.hibernate.type.Type;
*
* @author Steve Ebersole
*/
public class SequenceStyleGenerator implements PersistentIdentifierGenerator, Configurable {
public class SequenceStyleGenerator
implements PersistentIdentifierGenerator, BulkInsertionCapableIdentifierGenerator, Configurable {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class,
SequenceStyleGenerator.class.getName());
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class,
SequenceStyleGenerator.class.getName()
);
// general purpose parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public static final String SEQUENCE_PARAM = "sequence_name";
@ -155,9 +160,7 @@ public class SequenceStyleGenerator implements PersistentIdentifierGenerator, Co
// Configurable implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* {@inheritDoc}
*/
@Override
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
this.identifierType = type;
boolean forceTableUse = ConfigurationHelper.getBoolean( FORCE_TBL_PARAM, params, false );
@ -338,9 +341,7 @@ public class SequenceStyleGenerator implements PersistentIdentifierGenerator, Co
// IdentifierGenerator implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* {@inheritDoc}
*/
@Override
public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
return optimizer.generate( databaseStructure.buildCallback( session ) );
}
@ -348,24 +349,35 @@ public class SequenceStyleGenerator implements PersistentIdentifierGenerator, Co
// PersistentIdentifierGenerator implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* {@inheritDoc}
*/
@Override
public Object generatorKey() {
return databaseStructure.getName();
}
/**
* {@inheritDoc}
*/
@Override
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
return databaseStructure.sqlCreateStrings( dialect );
}
/**
* {@inheritDoc}
*/
@Override
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
return databaseStructure.sqlDropStrings( dialect );
}
// BulkInsertionCapableIdentifierGenerator implementation ~~~~~~~~~~~~~~~~~
@Override
public boolean supportsBulkInsertionIdentifierGeneration() {
// it does, as long as
// 1) there is no (non-noop) optimizer in use
// 2) the underlying structure is a sequence
return OptimizerFactory.NoopOptimizer.class.isInstance( getOptimizer() )
&& getDatabaseStructure().isPhysicalSequence();
}
@Override
public String determineBulkInsertionIdentifierGenerationSelectFragment(Dialect dialect) {
return dialect.getSelectSequenceNextValString( getDatabaseStructure().getName() );
}
}

View File

@ -197,4 +197,9 @@ public class TableStructure implements DatabaseStructure {
}
return new String[] { sqlDropString.toString() };
}
@Override
public boolean isPhysicalSequence() {
return false;
}
}

View File

@ -36,6 +36,7 @@ import org.hibernate.Transaction;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.hql.internal.ast.HqlSqlWalker;
import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.DialectChecks;
@ -345,7 +346,8 @@ public class BulkManipulationTest extends BaseCoreFunctionalTestCase {
protected boolean supportsBulkInsertIdGeneration(Class entityClass) {
EntityPersister persister = sessionFactory().getEntityPersister( entityClass.getName() );
IdentifierGenerator generator = persister.getIdentifierGenerator();
return HqlSqlWalker.supportsIdGenWithBulkInsertion( generator );
return BulkInsertionCapableIdentifierGenerator.class.isInstance( generator )
&& BulkInsertionCapableIdentifierGenerator.class.cast( generator ).supportsBulkInsertionIdentifierGeneration();
}
@Test