mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-16 16:15:06 +00:00
HHH-14483 Split insert-values statement if dialect doesn't support values lists
This commit is contained in:
parent
88e07652c9
commit
98e028c51d
@ -225,26 +225,7 @@ private boolean hasGroupingOrDistinct(QuerySpec querySpec) {
|
||||
|
||||
@Override
|
||||
protected void visitValuesList(List<Values> valuesList) {
|
||||
if ( valuesList.size() < 2 ) {
|
||||
super.visitValuesList( valuesList );
|
||||
}
|
||||
else {
|
||||
// Oracle doesn't support a multi-values insert
|
||||
// So we render a select union emulation instead
|
||||
String separator = "";
|
||||
final Stack<Clause> clauseStack = getClauseStack();
|
||||
try {
|
||||
clauseStack.push( Clause.VALUES );
|
||||
for ( Values values : valuesList ) {
|
||||
appendSql( separator );
|
||||
renderExpressionsAsSubquery( values.getExpressions() );
|
||||
separator = " union all ";
|
||||
}
|
||||
}
|
||||
finally {
|
||||
clauseStack.pop();
|
||||
}
|
||||
}
|
||||
visitValuesListEmulateSelectUnion( valuesList );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -546,11 +546,6 @@ public int getMaxIdentifierLength() {
|
||||
return 255;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsValuesListForInsert() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLockTimeouts() {
|
||||
return false;
|
||||
|
@ -33,6 +33,7 @@
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.UnionTableReference;
|
||||
import org.hibernate.sql.ast.tree.insert.Values;
|
||||
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||
@ -253,6 +254,11 @@ public void visitQueryGroup(QueryGroup queryGroup) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitValuesList(List<Values> valuesList) {
|
||||
visitValuesListEmulateSelectUnion( valuesList );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||
assertRowsOnlyFetchClauseType( queryPart );
|
||||
|
@ -1135,11 +1135,6 @@ public boolean supportsNoColumnsInsert() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsValuesListForInsert() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsOrderByInSubquery() {
|
||||
// Seems to work, though I don't know as of which version
|
||||
|
@ -6,6 +6,8 @@
|
||||
*/
|
||||
package org.hibernate.dialect;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.query.sqm.BinaryArithmeticOperator;
|
||||
@ -20,6 +22,7 @@
|
||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||
import org.hibernate.sql.ast.tree.insert.Values;
|
||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||
@ -157,4 +160,9 @@ protected void renderInsertIntoNoColumns(TableInsertStandard tableInsert) {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitValuesList(List<Values> valuesList) {
|
||||
visitValuesListEmulateSelectUnion( valuesList );
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.collections.Stack;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
@ -170,26 +169,7 @@ protected boolean shouldEmulateFetchClause(QueryPart queryPart) {
|
||||
|
||||
@Override
|
||||
protected void visitValuesList(List<Values> valuesList) {
|
||||
if ( valuesList.size() < 2 ) {
|
||||
super.visitValuesList( valuesList );
|
||||
}
|
||||
else {
|
||||
// Oracle doesn't support a multi-values insert
|
||||
// So we render a select union emulation instead
|
||||
String separator = "";
|
||||
final Stack<Clause> clauseStack = getClauseStack();
|
||||
try {
|
||||
clauseStack.push( Clause.VALUES );
|
||||
for ( Values values : valuesList ) {
|
||||
appendSql( separator );
|
||||
renderExpressionsAsSubquery( values.getExpressions() );
|
||||
separator = " union all ";
|
||||
}
|
||||
}
|
||||
finally {
|
||||
clauseStack.pop();
|
||||
}
|
||||
}
|
||||
visitValuesListEmulateSelectUnion( valuesList );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -554,11 +554,6 @@ public int getMaxIdentifierLength() {
|
||||
return 255;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsValuesListForInsert() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLockTimeouts() {
|
||||
return false;
|
||||
|
@ -33,6 +33,7 @@
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.UnionTableReference;
|
||||
import org.hibernate.sql.ast.tree.insert.Values;
|
||||
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||
@ -251,6 +252,11 @@ public void visitQueryGroup(QueryGroup queryGroup) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitValuesList(List<Values> valuesList) {
|
||||
visitValuesListEmulateSelectUnion( valuesList );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||
assertRowsOnlyFetchClauseType( queryPart );
|
||||
|
@ -817,9 +817,30 @@ private NonSelectQueryPlan buildInsertQueryPlan() {
|
||||
}
|
||||
}
|
||||
}
|
||||
return !useMultiTableInsert
|
||||
? new SimpleInsertQueryPlan( sqmInsert, domainParameterXref )
|
||||
: new MultiTableInsertQueryPlan( sqmInsert, domainParameterXref, persister.getSqmMultiTableInsertStrategy() );
|
||||
if ( useMultiTableInsert ) {
|
||||
return new MultiTableInsertQueryPlan(
|
||||
sqmInsert,
|
||||
domainParameterXref,
|
||||
persister.getSqmMultiTableInsertStrategy()
|
||||
);
|
||||
}
|
||||
else if ( sqmInsert instanceof SqmInsertValuesStatement<?>
|
||||
&& ( (SqmInsertValuesStatement<R>) sqmInsert ).getValuesList().size() != 1
|
||||
&& !getSessionFactory().getJdbcServices().getDialect().supportsValuesListForInsert() ) {
|
||||
// Split insert-values queries if the dialect doesn't support values lists
|
||||
final SqmInsertValuesStatement<R> insertValues = (SqmInsertValuesStatement<R>) sqmInsert;
|
||||
final List<SqmValues> valuesList = insertValues.getValuesList();
|
||||
final NonSelectQueryPlan[] planParts = new NonSelectQueryPlan[valuesList.size()];
|
||||
for ( int i = 0; i < valuesList.size(); i++ ) {
|
||||
final SqmInsertValuesStatement<?> subInsert = insertValues.copyWithoutValues( SqmCopyContext.simpleContext() );
|
||||
subInsert.getValuesList().add( valuesList.get( i ) );
|
||||
planParts[i] = new SimpleInsertQueryPlan( subInsert, domainParameterXref );
|
||||
}
|
||||
|
||||
return new AggregatedNonSelectQueryPlanImpl( planParts );
|
||||
}
|
||||
|
||||
return new SimpleInsertQueryPlan( sqmInsert, domainParameterXref );
|
||||
}
|
||||
|
||||
protected boolean hasIdentifierAssigned(SqmInsertStatement<?> sqmInsert, EntityPersister entityDescriptor) {
|
||||
|
@ -69,6 +69,21 @@ public SqmInsertValuesStatement<T> copy(SqmCopyContext context) {
|
||||
);
|
||||
}
|
||||
|
||||
public SqmInsertValuesStatement<T> copyWithoutValues(SqmCopyContext context) {
|
||||
return context.registerCopy(
|
||||
this,
|
||||
new SqmInsertValuesStatement<>(
|
||||
nodeBuilder(),
|
||||
getQuerySource(),
|
||||
copyParameters( context ),
|
||||
copyCteStatements( context ),
|
||||
getTarget().copy( context ),
|
||||
copyInsertionTargetPaths( context ),
|
||||
new ArrayList<>()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public List<SqmValues> getValuesList() {
|
||||
return valuesList;
|
||||
}
|
||||
|
@ -1169,28 +1169,28 @@ private void renderImplicitTargetColumnSpec() {
|
||||
}
|
||||
|
||||
protected void visitValuesList(List<Values> valuesList) {
|
||||
visitValuesListStandard( valuesList );
|
||||
}
|
||||
|
||||
protected final void visitValuesListStandard(List<Values> valuesList) {
|
||||
if ( valuesList.size() != 1 && !dialect.supportsValuesListForInsert() ) {
|
||||
throw new IllegalQueryOperationException( "Dialect does not support values lists for insert statements" );
|
||||
}
|
||||
appendSql("values");
|
||||
boolean firstTuple = true;
|
||||
final Stack<Clause> clauseStack = getClauseStack();
|
||||
try {
|
||||
clauseStack.push( Clause.VALUES );
|
||||
for ( Values values : valuesList ) {
|
||||
if ( firstTuple ) {
|
||||
firstTuple = false;
|
||||
}
|
||||
else {
|
||||
for ( int i = 0; i < valuesList.size(); i++ ) {
|
||||
if ( i != 0 ) {
|
||||
appendSql( COMMA_SEPARATOR_CHAR );
|
||||
}
|
||||
appendSql( " (" );
|
||||
boolean firstExpr = true;
|
||||
for ( Expression expression : values.getExpressions() ) {
|
||||
if ( firstExpr ) {
|
||||
firstExpr = false;
|
||||
}
|
||||
else {
|
||||
final List<Expression> expressions = valuesList.get( i ).getExpressions();
|
||||
for ( int j = 0; j < expressions.size(); j++ ) {
|
||||
if ( j != 0 ) {
|
||||
appendSql( COMMA_SEPARATOR_CHAR );
|
||||
}
|
||||
expression.accept( this );
|
||||
expressions.get( j ).accept( this );
|
||||
}
|
||||
appendSql( ')' );
|
||||
}
|
||||
@ -1200,6 +1200,29 @@ protected void visitValuesList(List<Values> valuesList) {
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitValuesListEmulateSelectUnion(List<Values> valuesList) {
|
||||
if ( valuesList.size() < 2 ) {
|
||||
visitValuesListStandard( valuesList );
|
||||
}
|
||||
else {
|
||||
// Oracle doesn't support a multi-values insert
|
||||
// So we render a select union emulation instead
|
||||
String separator = "";
|
||||
final Stack<Clause> clauseStack = getClauseStack();
|
||||
try {
|
||||
clauseStack.push( Clause.VALUES );
|
||||
for ( int i = 0; i < valuesList.size(); i++ ) {
|
||||
appendSql( separator );
|
||||
renderExpressionsAsSubquery( valuesList.get( i ).getExpressions() );
|
||||
separator = " union all ";
|
||||
}
|
||||
}
|
||||
finally {
|
||||
clauseStack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitForUpdateClause(QuerySpec querySpec) {
|
||||
if ( querySpec.isRoot() ) {
|
||||
if ( forUpdate != null ) {
|
||||
|
@ -51,7 +51,6 @@
|
||||
import org.hibernate.testing.orm.junit.SkipForDialect;
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -252,7 +251,6 @@ public void hql_insert_example() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresDialectFeature(DialectChecks.SupportsValuesListForInsert.class)
|
||||
public void hql_multi_insert_example() {
|
||||
doInJPA(this::entityManagerFactory, entityManager -> {
|
||||
//tag::hql-insert-example[]
|
||||
|
@ -9,9 +9,7 @@
|
||||
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||
import org.hibernate.testing.orm.domain.contacts.Contact;
|
||||
import org.hibernate.testing.orm.domain.contacts.Contact.Name;
|
||||
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
@ -91,7 +89,6 @@ public void testInsertValues(SessionFactoryScope scope) {
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresDialectFeature( feature = DialectFeatureChecks.SupportsValuesListForInsert.class)
|
||||
public void testInsertMultipleValues(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
|
Loading…
x
Reference in New Issue
Block a user