Update the bulk section of the batching documentation chapter for the new mutation strategies. Also implement the missing InlineUpdateHandler
This commit is contained in:
parent
111fe26ccc
commit
4334b46376
|
@ -551,8 +551,8 @@ therefore reusing its execution plan.
|
|||
|
||||
==== Multi-table bulk HQL operations
|
||||
|
||||
`*hibernate.hql.bulk_id_strategy*` (e.g. A fully-qualified class name, an instance, or a `Class` object reference)::
|
||||
Provide a custom https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/hql/spi/id/MultiTableBulkIdStrategy.html[`org.hibernate.hql.spi.id.MultiTableBulkIdStrategy`] implementation for handling multi-table bulk HQL operations.
|
||||
`*hibernate.query.mutation_strategy*` (e.g. A fully-qualified class name, an instance, or a `Class` object reference)::
|
||||
Provide a custom https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/query/sqm/mutation/spi/SqmMultiTableMutationStrategy.html[`org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy`] implementation for handling multi-table bulk HQL operations.
|
||||
|
||||
`*hibernate.hql.bulk_id_strategy.global_temporary.drop_tables*` (e.g. `true` or `false` (default value))::
|
||||
For databases that don't support local tables, but just global ones, this configuration property allows you to DROP the global tables used for multi-table bulk HQL operations when the `SessionFactory` or the `EntityManagerFactory` is closed.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[[batch]]
|
||||
== Batching
|
||||
:sourcedir: ../../../../../test/java/org/hibernate/userguide/batch
|
||||
:bulkid-sourcedir: ../../../../../../../hibernate-core/src/test_legacy/java/org/hibernate/test/bulkid
|
||||
:bulkid-sourcedir: ../../../../../../../hibernate-core/src/test/java/org/hibernate/orm/test/bulkid
|
||||
:extrasdir: extras
|
||||
|
||||
[[batch-jdbcbatch]]
|
||||
|
@ -165,7 +165,7 @@ Both the Hibernate native Query Language and JPQL (Java Persistence Query Langua
|
|||
[[batch-bulk-hql-update-delete-example]]
|
||||
.Pseudo-syntax for UPDATE and DELETE statements using HQL
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
[source, SQL, indent=0]
|
||||
----
|
||||
UPDATE FROM EntityName e WHERE e.name = ?
|
||||
|
||||
|
@ -250,24 +250,32 @@ In the example of joined-subclass, a `DELETE` against one of the subclasses may
|
|||
|
||||
==== HQL syntax for INSERT
|
||||
|
||||
.Pseudo-syntax for INSERT statements
|
||||
.Pseudo-syntax for INSERT-SELECT statements
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
[source, SQL, indent=0]
|
||||
----
|
||||
INSERT INTO EntityName
|
||||
properties_list
|
||||
SELECT properties_list
|
||||
SELECT select_list
|
||||
FROM ...
|
||||
----
|
||||
====
|
||||
|
||||
Only the `INSERT INTO ... SELECT ...` form is supported.
|
||||
You cannot specify explicit values to insert.
|
||||
Alternatively one can also declare individual values
|
||||
|
||||
.Pseudo-syntax for INSERT-VALUES statements
|
||||
====
|
||||
[source, SQL, indent=0]
|
||||
----
|
||||
INSERT INTO EntityName
|
||||
properties_list
|
||||
VALUES values_list
|
||||
----
|
||||
====
|
||||
|
||||
The `properties_list` is analogous to the column specification in the `SQL` `INSERT` statement.
|
||||
For entities involved in mapped inheritance, you can only use properties directly defined on that given class-level in the `properties_list`.
|
||||
Superclass properties are not allowed and subclass properties are irrelevant.
|
||||
In other words, `INSERT` statements are inherently non-polymorphic.
|
||||
Note that `INSERT` statements are inherently non-polymorphic, so it is not possible to use an `EntityName`
|
||||
which is abstract or refer to subclass properties.
|
||||
|
||||
The SELECT statement can be any valid HQL select query, but the return types must match the types expected by the INSERT.
|
||||
Hibernate verifies the return types during query compilation, instead of expecting the database to check it.
|
||||
|
@ -282,7 +290,7 @@ Otherwise, Hibernate throws an exception during parsing. Available in-database
|
|||
|
||||
For properties mapped as either version or timestamp, the insert statement gives you two options.
|
||||
You can either specify the property in the properties_list, in which case its value is taken from the corresponding select expressions or omit it from the properties_list,
|
||||
in which case the seed value defined by the org.hibernate.type.VersionType is used.
|
||||
in which case the seed value defined by the `org.hibernate.type.descriptor.java.VersionJavaType` is used.
|
||||
|
||||
[[batch-bulk-hql-insert-example]]
|
||||
.HQL INSERT statement
|
||||
|
@ -296,10 +304,19 @@ include::{sourcedir}/BatchTest.java[tags=batch-bulk-hql-insert-example]
|
|||
This section is only a brief overview of HQL. For more information, see <<chapters/query/hql/QueryLanguage.adoc#query-language,Hibernate Query Language>>.
|
||||
|
||||
[[batch-bulk-hql-strategies]]
|
||||
==== Bulk-id strategies
|
||||
==== Bulk mutation strategies
|
||||
|
||||
This article is about the https://hibernate.atlassian.net/browse/HHH-11262[HHH-11262] JIRA issue which now allows the bulk-id
|
||||
strategies to work even when you cannot create temporary tables.
|
||||
When a bulk mutation involves multiple tables, Hibernate has to issue individual DML statements to the respective tables.
|
||||
Since the mutation itself could have an effect on the conditions used in the statement, it's generally not possible
|
||||
to simply execute parts of the DML statement against the respective tables. Instead, Hibernate has to temporarily remember
|
||||
which rows will be affected, and execute the DML statements based on these rows.
|
||||
|
||||
Usually, Hibernate will make use of local or global temporary tables to remember the primary keys of the rows.
|
||||
For some databases, currently only PostgreSQL and DB2, a more advanced strategy (`CteMutationStrategy`) is used,
|
||||
which makes use of DML in CTE support to execute the whole operation in one SQL statement.
|
||||
|
||||
The chosen strategy, unless overridden through the `hibernate.query.mutation_strategy` setting, is based on the
|
||||
Dialect support through `org.hibernate.dialect.Dialect.getFallbackSqmMutationStrategy`.
|
||||
|
||||
[[batch-bulk-hql-strategies-class-diagram]]
|
||||
===== Class diagram
|
||||
|
@ -311,22 +328,22 @@ image:images/domain/bulkid/temp_table_class_diagram.png[Entity class diagram]
|
|||
The `Person` entity is the base class of this entity inheritance model, and is mapped as follows:
|
||||
|
||||
[[batch-bulk-hql-temp-table-base-class-example]]
|
||||
.Bulk-id base class entity
|
||||
.Bulk mutation base class entity
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{bulkid-sourcedir}/AbstractBulkCompositeIdTest.java[tags=batch-bulk-hql-temp-table-base-class-example]
|
||||
include::{bulkid-sourcedir}/AbstractMutationStrategyCompositeIdTest.java[tags=batch-bulk-hql-temp-table-base-class-example]
|
||||
----
|
||||
====
|
||||
|
||||
Both the `Doctor` and `Engineer` entity classes extend the `Person` base class:
|
||||
|
||||
[[batch-bulk-hql-temp-table-sub-classes-example]]
|
||||
.Bulk-id subclass entities
|
||||
.Bulk mutation subclass entities
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{bulkid-sourcedir}/AbstractBulkCompositeIdTest.java[tags=batch-bulk-hql-temp-table-sub-classes-example]
|
||||
include::{bulkid-sourcedir}/AbstractMutationStrategyIdTest.java[tags=batch-bulk-hql-temp-table-sub-classes-example]
|
||||
----
|
||||
====
|
||||
|
||||
|
@ -336,11 +353,11 @@ include::{bulkid-sourcedir}/AbstractBulkCompositeIdTest.java[tags=batch-bulk-hql
|
|||
Now, when you try to execute a bulk entity delete query:
|
||||
|
||||
[[batch-bulk-hql-temp-table-delete-query-example]]
|
||||
.Bulk-id delete query example
|
||||
.Bulk mutation delete query example
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{bulkid-sourcedir}/AbstractBulkCompositeIdTest.java[tags=batch-bulk-hql-temp-table-delete-query-example]
|
||||
include::{bulkid-sourcedir}/AbstractMutationStrategyCompositeIdTest.java[tags=batch-bulk-hql-temp-table-delete-query-example]
|
||||
----
|
||||
|
||||
[source, SQL, indent=0]
|
||||
|
@ -349,31 +366,21 @@ include::{extrasdir}/batch-bulk-hql-temp-table-delete-query-example.sql[]
|
|||
----
|
||||
====
|
||||
|
||||
`HT_Person` is a temporary table that Hibernate creates to hold all the entity identifiers that are to be updated or deleted by the bulk id operation.
|
||||
`HT_Person` is a temporary table that Hibernate creates to hold all the entity identifiers that are to be updated or deleted by the bulk operation.
|
||||
The temporary table can be either global or local, depending on the underlying database capabilities.
|
||||
|
||||
[[batch-bulk-hql-strategies-non-temporary-table]]
|
||||
===== Non-temporary table bulk-id strategies
|
||||
===== Non-temporary table bulk mutation strategies
|
||||
|
||||
As the https://hibernate.atlassian.net/browse/HHH-11262[HHH-11262] issue describes, there are use cases when the application developer cannot use temporary tables because
|
||||
the database user lacks this privilege.
|
||||
|
||||
In this case, we defined several options which you can choose depending on your database capabilities:
|
||||
|
||||
- `InlineIdsInClauseBulkIdStrategy`
|
||||
- `InlineIdsSubSelectValueListBulkIdStrategy`
|
||||
- `InlineIdsOrClauseBulkIdStrategy`
|
||||
- `CteValuesListBulkIdStrategy`
|
||||
|
||||
[[batch-bulk-hql-strategies-InlineIdsInClauseBulkIdStrategy]]
|
||||
====== `InlineIdsInClauseBulkIdStrategy`
|
||||
When the temporary table strategy can not be used because the database user lacks privilege to create temporary tables,
|
||||
the `InlineMutationStrategy` must be used.
|
||||
|
||||
To use this strategy, you need to configure the following configuration property:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<property name="hibernate.hql.bulk_id_strategy"
|
||||
value="org.hibernate.hql.spi.id.inline.InlineIdsInClauseBulkIdStrategy"
|
||||
<property name="hibernate.query.mutation_strategy"
|
||||
value="org.hibernate.query.sqm.mutation.internal.inline.InlineMutationStrategy"
|
||||
/>
|
||||
----
|
||||
|
||||
|
@ -390,110 +397,3 @@ include::{extrasdir}/batch-bulk-hql-InlineIdsInClauseBulkIdStrategy-delete-query
|
|||
|
||||
So, the entity identifiers are selected first and used for each particular update or delete statement.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
The IN clause row value expression has long been supported by Oracle, PostgreSQL, and nowadays by MySQL 5.7.
|
||||
However, SQL Server 2014 does not support it, so you'll have to use a different strategy.
|
||||
====
|
||||
|
||||
[[batch-bulk-hql-strategies-InlineIdsSubSelectValueListBulkIdStrategy]]
|
||||
====== `InlineIdsSubSelectValueListBulkIdStrategy`
|
||||
|
||||
To use this strategy, you need to configure the following configuration property:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<property name="hibernate.hql.bulk_id_strategy"
|
||||
value="org.hibernate.hql.spi.id.inline.InlineIdsSubSelectValueListBulkIdStrategy"
|
||||
/>
|
||||
----
|
||||
|
||||
Now, when running the previous test case, Hibernate generates the following SQL statements:
|
||||
|
||||
[[batch-bulk-hql-InlineIdsSubSelectValueListBulkIdStrategy-delete-query-example]]
|
||||
.`InlineIdsSubSelectValueListBulkIdStrategy` delete entity query example
|
||||
====
|
||||
[source, SQL, indent=0]
|
||||
----
|
||||
include::{extrasdir}/batch-bulk-hql-InlineIdsSubSelectValueListBulkIdStrategy-delete-query-example.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
[TIP]
|
||||
====
|
||||
The underlying database must support the `VALUES` list clause, like PostgreSQL or SQL Server 2008.
|
||||
However, this strategy requires the IN-clause row value expression for composite identifiers, and for this reason, you can only use the `InlineIdsSubSelectValueListBulkIdStrategy` strategy with PostgreSQL.
|
||||
====
|
||||
|
||||
[[batch-bulk-hql-strategies-InlineIdsOrClauseBulkIdStrategy]]
|
||||
====== `InlineIdsOrClauseBulkIdStrategy`
|
||||
|
||||
To use this strategy, you need to configure the following configuration property:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<property name="hibernate.hql.bulk_id_strategy"
|
||||
value="org.hibernate.hql.spi.id.inline.InlineIdsOrClauseBulkIdStrategy"
|
||||
/>
|
||||
----
|
||||
|
||||
Now, when running the previous test case, Hibernate generates the following SQL statements:
|
||||
|
||||
[[batch-bulk-hql-InlineIdsOrClauseBulkIdStrategy-delete-query-example]]
|
||||
.`InlineIdsOrClauseBulkIdStrategy` delete entity query example
|
||||
====
|
||||
[source, SQL, indent=0]
|
||||
----
|
||||
include::{extrasdir}/batch-bulk-hql-InlineIdsOrClauseBulkIdStrategy-delete-query-example.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
[TIP]
|
||||
====
|
||||
The `InlineIdsOrClauseBulkIdStrategy` strategy has the advantage of being supported by all the major relational database systems (e.g. Oracle, SQL Server, MySQL, and PostgreSQL).
|
||||
====
|
||||
|
||||
[[batch-bulk-hql-strategies-CteValuesListBulkIdStrategy]]
|
||||
====== `CteValuesListBulkIdStrategy`
|
||||
|
||||
To use this strategy, you need to configure the following configuration property:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<property name="hibernate.hql.bulk_id_strategy"
|
||||
value="org.hibernate.hql.spi.id.inline.CteValuesListBulkIdStrategy"
|
||||
/>
|
||||
----
|
||||
|
||||
Now, when running the previous test case, Hibernate generates the following SQL statements:
|
||||
|
||||
[[batch-bulk-hql-CteValuesListBulkIdStrategy-delete-query-example]]
|
||||
.`CteValuesListBulkIdStrategy` delete entity query example
|
||||
====
|
||||
[source, SQL, indent=0]
|
||||
----
|
||||
include::{extrasdir}/batch-bulk-hql-CteValuesListBulkIdStrategy-delete-query-example.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
[TIP]
|
||||
====
|
||||
The underlying database must support CTE (Common Table Expressions) that can be referenced from non-query statements as well. For instance, PostgreSQL supports this feature since version 9.1 and SQL Server offers support for it since version 2005.
|
||||
|
||||
The underlying database must also support the VALUES list clause, like PostgreSQL or SQL Server 2008.
|
||||
|
||||
However, this strategy requires the IN-clause row value expression for composite identifiers, so you can only use this strategy with PostgreSQL.
|
||||
====
|
||||
|
||||
If you can use temporary tables, that's probably the best choice.
|
||||
However, if you are not allowed to create temporary tables, you must pick one of these four strategies that works with your underlying database.
|
||||
Before making up your mind, you should benchmark which one works best for your current workload.
|
||||
For instance, https://blog.2ndquadrant.com/postgresql-ctes-are-optimization-fences/[CTE are optimization fences in PostgreSQL], so make sure you measure before making a decision.
|
||||
|
||||
If you're using Oracle or MySQL 5.7, you can choose either `InlineIdsOrClauseBulkIdStrategy` or `InlineIdsInClauseBulkIdStrategy`.
|
||||
For older version of MySQL, then you can only use `InlineIdsOrClauseBulkIdStrategy`.
|
||||
|
||||
If you're using SQL Server, `InlineIdsOrClauseBulkIdStrategy` is the only option for you.
|
||||
|
||||
If you're using PostgreSQL, then you have the luxury of choosing any of these four strategies.
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.hibernate.query.sqm.internal.SqmUtil;
|
|||
import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess;
|
||||
import org.hibernate.query.sqm.sql.internal.SqlAstQueryPartProcessingStateImpl;
|
||||
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||
|
@ -47,6 +48,7 @@ import org.hibernate.sql.results.graph.DomainResult;
|
|||
import org.hibernate.sql.results.graph.basic.BasicResult;
|
||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||
import org.hibernate.sql.results.spi.ListResultsConsumer;
|
||||
import org.hibernate.sql.results.spi.RowTransformer;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
|
@ -190,7 +192,7 @@ public class MatchingIdSelectionHelper {
|
|||
* or UPDATE SQM query
|
||||
*/
|
||||
public static List<Object> selectMatchingIds(
|
||||
SqmDeleteOrUpdateStatement sqmMutationStatement,
|
||||
SqmDeleteOrUpdateStatement<?> sqmMutationStatement,
|
||||
DomainParameterXref domainParameterXref,
|
||||
DomainQueryExecutionContext executionContext) {
|
||||
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
|
||||
|
@ -236,6 +238,9 @@ public class MatchingIdSelectionHelper {
|
|||
factory
|
||||
);
|
||||
|
||||
if ( sqmMutationStatement instanceof SqmDeleteStatement<?> ) {
|
||||
// For delete statements we also want to collect FK values to execute collection table cleanups
|
||||
|
||||
sqmConverter.getProcessingStateStack().push(
|
||||
new SqlAstQueryPartProcessingStateImpl(
|
||||
matchingIdSelection.getQuerySpec(),
|
||||
|
@ -276,6 +281,7 @@ public class MatchingIdSelectionHelper {
|
|||
}
|
||||
);
|
||||
sqmConverter.getProcessingStateStack().pop();
|
||||
}
|
||||
|
||||
final JdbcServices jdbcServices = factory.getJdbcServices();
|
||||
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
|
||||
|
@ -318,11 +324,18 @@ public class MatchingIdSelectionHelper {
|
|||
);
|
||||
lockOptions.setLockMode( lockMode );
|
||||
|
||||
final RowTransformer<Object> rowTransformer;
|
||||
if ( matchingIdSelection.getDomainResultDescriptors().size() == 1 ) {
|
||||
rowTransformer = row -> row[0];
|
||||
}
|
||||
else {
|
||||
rowTransformer = row -> row;
|
||||
}
|
||||
return jdbcServices.getJdbcSelectExecutor().list(
|
||||
idSelectJdbcOperation,
|
||||
jdbcParameterBindings,
|
||||
SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( executionContext ),
|
||||
row -> row[0],
|
||||
rowTransformer,
|
||||
ListResultsConsumer.UniqueSemantic.FILTER
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.query.sqm.mutation.internal.temptable;
|
||||
package org.hibernate.query.sqm.mutation.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -17,17 +17,17 @@ import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
|||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
class TableKeyExpressionCollector {
|
||||
private final EntityMappingType entityMappingType;
|
||||
public class TableKeyExpressionCollector {
|
||||
|
||||
TableKeyExpressionCollector(EntityMappingType entityMappingType) {
|
||||
private final EntityMappingType entityMappingType;
|
||||
private Expression firstColumnExpression;
|
||||
private List<Expression> collectedColumnExpressions;
|
||||
|
||||
public TableKeyExpressionCollector(EntityMappingType entityMappingType) {
|
||||
this.entityMappingType = entityMappingType;
|
||||
}
|
||||
|
||||
Expression firstColumnExpression;
|
||||
List<Expression> collectedColumnExpressions;
|
||||
|
||||
void apply(ColumnReference columnReference) {
|
||||
public void apply(ColumnReference columnReference) {
|
||||
if ( firstColumnExpression == null ) {
|
||||
firstColumnExpression = columnReference;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class TableKeyExpressionCollector {
|
|||
}
|
||||
}
|
||||
|
||||
Expression buildKeyExpression() {
|
||||
public Expression buildKeyExpression() {
|
||||
if ( collectedColumnExpressions == null ) {
|
||||
return firstColumnExpression;
|
||||
}
|
|
@ -36,7 +36,7 @@ import org.hibernate.sql.ast.tree.select.SelectStatement;
|
|||
import org.hibernate.sql.results.graph.basic.BasicResult;
|
||||
|
||||
/**
|
||||
* Bulk-id delete handler that uses CTE and VALUES lists.
|
||||
* Bulk mutation delete handler that uses CTE and VALUES lists.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
|
|
|
@ -43,7 +43,7 @@ import org.hibernate.sql.exec.spi.StatementCreatorHelper;
|
|||
*/
|
||||
public class InlineDeleteHandler implements DeleteHandler {
|
||||
private final MatchingIdRestrictionProducer matchingIdsPredicateProducer;
|
||||
private final SqmDeleteStatement sqmDeleteStatement;
|
||||
private final SqmDeleteStatement<?> sqmDeleteStatement;
|
||||
private final DomainParameterXref domainParameterXref;
|
||||
|
||||
private final DomainQueryExecutionContext executionContext;
|
||||
|
@ -54,7 +54,7 @@ public class InlineDeleteHandler implements DeleteHandler {
|
|||
|
||||
protected InlineDeleteHandler(
|
||||
MatchingIdRestrictionProducer matchingIdsPredicateProducer,
|
||||
SqmDeleteStatement sqmDeleteStatement,
|
||||
SqmDeleteStatement<?> sqmDeleteStatement,
|
||||
DomainParameterXref domainParameterXref,
|
||||
DomainQueryExecutionContext context) {
|
||||
this.sqmDeleteStatement = sqmDeleteStatement;
|
||||
|
|
|
@ -26,17 +26,17 @@ import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
|||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class InlineMutationStrategy implements SqmMultiTableMutationStrategy {
|
||||
private final Function<SqmDeleteOrUpdateStatement,MatchingIdRestrictionProducer> matchingIdsStrategy;
|
||||
private final Function<SqmDeleteOrUpdateStatement<?>,MatchingIdRestrictionProducer> matchingIdsStrategy;
|
||||
|
||||
public InlineMutationStrategy(Dialect dialect) {
|
||||
this( determinePredicateProducer( dialect ) );
|
||||
}
|
||||
|
||||
private static Function<SqmDeleteOrUpdateStatement,MatchingIdRestrictionProducer> determinePredicateProducer(Dialect dialect) {
|
||||
private static Function<SqmDeleteOrUpdateStatement<?>,MatchingIdRestrictionProducer> determinePredicateProducer(Dialect dialect) {
|
||||
return statement -> new InPredicateRestrictionProducer();
|
||||
}
|
||||
|
||||
public InlineMutationStrategy(Function<SqmDeleteOrUpdateStatement,MatchingIdRestrictionProducer> matchingIdsStrategy) {
|
||||
public InlineMutationStrategy(Function<SqmDeleteOrUpdateStatement<?>,MatchingIdRestrictionProducer> matchingIdsStrategy) {
|
||||
this.matchingIdsStrategy = matchingIdsStrategy;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,21 +6,85 @@
|
|||
*/
|
||||
package org.hibernate.query.sqm.mutation.internal.inline;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.metamodel.MappingMetamodel;
|
||||
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
||||
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||
import org.hibernate.persister.entity.AbstractEntityPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.Joinable;
|
||||
import org.hibernate.query.SemanticException;
|
||||
import org.hibernate.query.spi.DomainQueryExecutionContext;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter;
|
||||
import org.hibernate.query.sqm.internal.SqmUtil;
|
||||
import org.hibernate.query.sqm.mutation.internal.MatchingIdSelectionHelper;
|
||||
import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter;
|
||||
import org.hibernate.query.sqm.mutation.internal.UpdateHandler;
|
||||
import org.hibernate.query.sqm.mutation.internal.TableKeyExpressionCollector;
|
||||
import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||
import org.hibernate.query.sqm.tree.predicate.SqmWhereClause;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseImpl;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
|
||||
import org.hibernate.sql.ast.tree.from.UnionTableReference;
|
||||
import org.hibernate.sql.ast.tree.from.ValuesTableGroup;
|
||||
import org.hibernate.sql.ast.tree.insert.InsertStatement;
|
||||
import org.hibernate.sql.ast.tree.insert.Values;
|
||||
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.InListPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.PredicateCollector;
|
||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||
import org.hibernate.sql.ast.tree.update.Assignment;
|
||||
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.exec.spi.JdbcInsert;
|
||||
import org.hibernate.sql.exec.spi.JdbcMutationExecutor;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.sql.exec.spi.JdbcUpdate;
|
||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class InlineUpdateHandler implements UpdateHandler {
|
||||
private final SqmUpdateStatement sqmUpdate;
|
||||
private final SqmUpdateStatement<?> sqmUpdate;
|
||||
private final DomainParameterXref domainParameterXref;
|
||||
private final MatchingIdRestrictionProducer matchingIdsPredicateProducer;
|
||||
|
||||
|
@ -32,7 +96,7 @@ public class InlineUpdateHandler implements UpdateHandler {
|
|||
|
||||
public InlineUpdateHandler(
|
||||
MatchingIdRestrictionProducer matchingIdsPredicateProducer,
|
||||
SqmUpdateStatement sqmUpdate,
|
||||
SqmUpdateStatement<?> sqmUpdate,
|
||||
DomainParameterXref domainParameterXref,
|
||||
DomainQueryExecutionContext context) {
|
||||
this.matchingIdsPredicateProducer = matchingIdsPredicateProducer;
|
||||
|
@ -48,6 +112,527 @@ public class InlineUpdateHandler implements UpdateHandler {
|
|||
|
||||
@Override
|
||||
public int execute(DomainQueryExecutionContext executionContext) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
final List<Object> ids = MatchingIdSelectionHelper.selectMatchingIds(
|
||||
sqmUpdate,
|
||||
domainParameterXref,
|
||||
executionContext
|
||||
);
|
||||
|
||||
if ( ids == null || ids.isEmpty() ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
domainParameterXref.clearExpansions();
|
||||
final MappingMetamodel domainModel = sessionFactory.getRuntimeMetamodels().getMappingMetamodel();
|
||||
|
||||
final String mutatingEntityName = sqmUpdate.getTarget().getModel().getHibernateEntityName();
|
||||
final EntityPersister entityDescriptor = domainModel.getEntityDescriptor( mutatingEntityName );
|
||||
|
||||
final String rootEntityName = entityDescriptor.getEntityPersister().getRootEntityName();
|
||||
final EntityPersister rootEntityDescriptor = domainModel.getEntityDescriptor( rootEntityName );
|
||||
|
||||
final String hierarchyRootTableName = ( (Joinable) rootEntityDescriptor ).getTableName();
|
||||
|
||||
final List<Expression> inListExpressions = new ArrayList<>( ids.size() );
|
||||
final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping();
|
||||
if ( identifierMapping instanceof BasicValuedModelPart ) {
|
||||
final BasicValuedModelPart basicValuedModelPart = (BasicValuedModelPart) identifierMapping;
|
||||
for ( int i = 0; i < ids.size(); i++ ) {
|
||||
inListExpressions.add( new QueryLiteral<>( ids.get( i ), basicValuedModelPart ) );
|
||||
}
|
||||
}
|
||||
else {
|
||||
final int jdbcTypeCount = identifierMapping.getJdbcTypeCount();
|
||||
for ( int i = 0; i < ids.size(); i++ ) {
|
||||
final Object[] id = (Object[]) ids.get( i );
|
||||
final List<Expression> tupleElements = new ArrayList<>( jdbcTypeCount );
|
||||
inListExpressions.add( new SqlTuple( tupleElements, identifierMapping ) );
|
||||
identifierMapping.forEachJdbcType(
|
||||
(index, jdbcMapping) -> {
|
||||
tupleElements.add(
|
||||
new QueryLiteral<>( id[index], (BasicValuedMapping) jdbcMapping )
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final MultiTableSqmMutationConverter converterDelegate = new MultiTableSqmMutationConverter(
|
||||
entityDescriptor,
|
||||
sqmUpdate,
|
||||
sqmUpdate.getTarget(),
|
||||
domainParameterXref,
|
||||
executionContext.getQueryOptions(),
|
||||
executionContext.getSession().getLoadQueryInfluencers(),
|
||||
executionContext.getQueryParameterBindings(),
|
||||
sessionFactory
|
||||
);
|
||||
|
||||
final TableGroup updatingTableGroup = converterDelegate.getMutatingTableGroup();
|
||||
|
||||
final TableReference hierarchyRootTableReference = updatingTableGroup.resolveTableReference(
|
||||
updatingTableGroup.getNavigablePath(),
|
||||
hierarchyRootTableName
|
||||
);
|
||||
assert hierarchyRootTableReference != null;
|
||||
|
||||
final Map<SqmParameter<?>, List<List<JdbcParameter>>> parameterResolutions;
|
||||
if ( domainParameterXref.getSqmParameterCount() == 0 ) {
|
||||
parameterResolutions = Collections.emptyMap();
|
||||
}
|
||||
else {
|
||||
parameterResolutions = new IdentityHashMap<>();
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// visit the set-clause using our special converter, collecting
|
||||
// information about the assignments
|
||||
|
||||
final List<Assignment> assignments = new ArrayList<>();
|
||||
final Map<SqmParameter<?>, MappingModelExpressible<?>> paramTypeResolutions = new LinkedHashMap<>();
|
||||
|
||||
converterDelegate.visitSetClause(
|
||||
sqmUpdate.getSetClause(),
|
||||
assignments::add,
|
||||
(sqmParameter, mappingType, jdbcParameters) -> {
|
||||
parameterResolutions.computeIfAbsent(
|
||||
sqmParameter,
|
||||
k -> new ArrayList<>( 1 )
|
||||
).add( jdbcParameters );
|
||||
paramTypeResolutions.put( sqmParameter, mappingType );
|
||||
}
|
||||
);
|
||||
converterDelegate.addVersionedAssignment( assignments::add, sqmUpdate );
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// visit the where-clause using our special converter, collecting information
|
||||
// about the restrictions
|
||||
|
||||
final Predicate providedPredicate;
|
||||
final SqmWhereClause whereClause = sqmUpdate.getWhereClause();
|
||||
if ( whereClause == null || whereClause.getPredicate() == null ) {
|
||||
providedPredicate = null;
|
||||
}
|
||||
else {
|
||||
providedPredicate = converterDelegate.visitWhereClause(
|
||||
whereClause,
|
||||
columnReference -> {},
|
||||
(sqmParameter, mappingType, jdbcParameters) -> {
|
||||
parameterResolutions.computeIfAbsent(
|
||||
sqmParameter,
|
||||
k -> new ArrayList<>( 1 )
|
||||
).add( jdbcParameters );
|
||||
paramTypeResolutions.put( sqmParameter, mappingType );
|
||||
}
|
||||
|
||||
);
|
||||
assert providedPredicate != null;
|
||||
}
|
||||
|
||||
final PredicateCollector predicateCollector = new PredicateCollector( providedPredicate );
|
||||
|
||||
entityDescriptor.applyBaseRestrictions(
|
||||
predicateCollector::applyPredicate,
|
||||
updatingTableGroup,
|
||||
true,
|
||||
executionContext.getSession().getLoadQueryInfluencers().getEnabledFilters(),
|
||||
null,
|
||||
converterDelegate
|
||||
);
|
||||
|
||||
converterDelegate.pruneTableGroupJoins();
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// cross-reference the TableReference by alias. The TableGroup already
|
||||
// cross-references it by name, bu the ColumnReference only has the alias
|
||||
|
||||
final Map<String, TableReference> tableReferenceByAlias = CollectionHelper.mapOfSize( updatingTableGroup.getTableReferenceJoins().size() + 1 );
|
||||
collectTableReference( updatingTableGroup.getPrimaryTableReference(), tableReferenceByAlias::put );
|
||||
for ( int i = 0; i < updatingTableGroup.getTableReferenceJoins().size(); i++ ) {
|
||||
collectTableReference( updatingTableGroup.getTableReferenceJoins().get( i ), tableReferenceByAlias::put );
|
||||
}
|
||||
|
||||
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
|
||||
executionContext.getQueryParameterBindings(),
|
||||
domainParameterXref,
|
||||
SqmUtil.generateJdbcParamsXref(
|
||||
domainParameterXref,
|
||||
() -> parameterResolutions
|
||||
),
|
||||
sessionFactory.getRuntimeMetamodels().getMappingMetamodel(),
|
||||
navigablePath -> updatingTableGroup,
|
||||
new SqmParameterMappingModelResolutionAccess() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> MappingModelExpressible<T> getResolvedMappingModelType(SqmParameter<T> parameter) {
|
||||
return (MappingModelExpressible<T>) paramTypeResolutions.get( parameter );
|
||||
}
|
||||
},
|
||||
executionContext.getSession()
|
||||
);
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// segment the assignments by table-reference
|
||||
final Map<TableReference, List<Assignment>> assignmentsByTable = new HashMap<>();
|
||||
for ( int i = 0; i < assignments.size(); i++ ) {
|
||||
final Assignment assignment = assignments.get( i );
|
||||
final List<ColumnReference> assignmentColumnRefs = assignment.getAssignable().getColumnReferences();
|
||||
|
||||
TableReference assignmentTableReference = null;
|
||||
|
||||
for ( int c = 0; c < assignmentColumnRefs.size(); c++ ) {
|
||||
final ColumnReference columnReference = assignmentColumnRefs.get( c );
|
||||
final TableReference tableReference = resolveTableReference(
|
||||
columnReference,
|
||||
tableReferenceByAlias
|
||||
);
|
||||
|
||||
if ( assignmentTableReference != null && assignmentTableReference != tableReference ) {
|
||||
throw new SemanticException( "Assignment referred to columns from multiple tables: " + assignment.getAssignable() );
|
||||
}
|
||||
|
||||
assignmentTableReference = tableReference;
|
||||
}
|
||||
|
||||
List<Assignment> assignmentsForTable = assignmentsByTable.get( assignmentTableReference );
|
||||
if ( assignmentsForTable == null ) {
|
||||
assignmentsForTable = new ArrayList<>();
|
||||
assignmentsByTable.put( assignmentTableReference, assignmentsForTable );
|
||||
}
|
||||
assignmentsForTable.add( assignment );
|
||||
}
|
||||
|
||||
final int rows = ids.size();
|
||||
|
||||
final SqmJdbcExecutionContextAdapter executionContextAdapter = SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( executionContext );
|
||||
entityDescriptor.visitConstraintOrderedTables(
|
||||
(tableExpression, tableKeyColumnVisitationSupplier) -> updateTable(
|
||||
tableExpression,
|
||||
tableKeyColumnVisitationSupplier,
|
||||
entityDescriptor,
|
||||
updatingTableGroup,
|
||||
assignmentsByTable,
|
||||
inListExpressions,
|
||||
rows,
|
||||
jdbcParameterBindings,
|
||||
executionContextAdapter
|
||||
)
|
||||
);
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
private void updateTable(
|
||||
String tableExpression,
|
||||
Supplier<Consumer<SelectableConsumer>> tableKeyColumnVisitationSupplier,
|
||||
EntityPersister entityDescriptor,
|
||||
TableGroup updatingTableGroup,
|
||||
Map<TableReference, List<Assignment>> assignmentsByTable,
|
||||
List<Expression> inListExpressions,
|
||||
int expectedUpdateCount,
|
||||
JdbcParameterBindings jdbcParameterBindings,
|
||||
ExecutionContext executionContext) {
|
||||
final TableReference updatingTableReference = updatingTableGroup.getTableReference(
|
||||
updatingTableGroup.getNavigablePath(),
|
||||
tableExpression,
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
final List<Assignment> assignments = assignmentsByTable.get( updatingTableReference );
|
||||
if ( assignments == null || assignments.isEmpty() ) {
|
||||
// no assignments for this table - skip it
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// create the in-subquery predicate to restrict the updates to just
|
||||
// matching ids
|
||||
|
||||
final TableKeyExpressionCollector keyColumnCollector = new TableKeyExpressionCollector( entityDescriptor );
|
||||
|
||||
tableKeyColumnVisitationSupplier.get().accept(
|
||||
(columnIndex, selection) -> {
|
||||
assert selection.getContainingTableExpression().equals( tableExpression );
|
||||
keyColumnCollector.apply(
|
||||
new ColumnReference(
|
||||
(String) null,
|
||||
selection,
|
||||
sessionFactory
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
final Expression keyExpression = keyColumnCollector.buildKeyExpression();
|
||||
final InListPredicate idListPredicate = new InListPredicate(
|
||||
keyExpression,
|
||||
inListExpressions
|
||||
);
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Create the SQL AST and convert it into a JdbcOperation
|
||||
final NamedTableReference dmlTableReference = resolveUnionTableReference( updatingTableReference, tableExpression );
|
||||
final UpdateStatement sqlAst = new UpdateStatement(
|
||||
dmlTableReference,
|
||||
assignments,
|
||||
idListPredicate
|
||||
);
|
||||
|
||||
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
||||
final JdbcUpdate jdbcUpdate = jdbcServices.getJdbcEnvironment()
|
||||
.getSqlAstTranslatorFactory()
|
||||
.buildUpdateTranslator( sessionFactory, sqlAst )
|
||||
.translate( jdbcParameterBindings, executionContext.getQueryOptions() );
|
||||
|
||||
final int updateCount = jdbcServices.getJdbcMutationExecutor().execute(
|
||||
jdbcUpdate,
|
||||
jdbcParameterBindings,
|
||||
sql -> executionContext.getSession()
|
||||
.getJdbcCoordinator()
|
||||
.getStatementPreparer()
|
||||
.prepareStatement( sql ),
|
||||
(integer, preparedStatement) -> {
|
||||
},
|
||||
executionContext
|
||||
);
|
||||
|
||||
if ( updateCount == expectedUpdateCount ) {
|
||||
// We are done when the update count matches
|
||||
return;
|
||||
}
|
||||
// Otherwise we have to check if the table is nullable, and if so, insert into that table
|
||||
final AbstractEntityPersister entityPersister = (AbstractEntityPersister) entityDescriptor.getEntityPersister();
|
||||
boolean isNullable = false;
|
||||
for (int i = 0; i < entityPersister.getTableSpan(); i++) {
|
||||
if ( tableExpression.equals( entityPersister.getTableName( i ) ) && entityPersister.isNullableTable( i ) ) {
|
||||
isNullable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( isNullable ) {
|
||||
// Copy the subquery contents into a root query
|
||||
final QuerySpec querySpec = new QuerySpec( true );
|
||||
final NavigablePath valuesPath = new NavigablePath( "id" );
|
||||
final List<Values> valuesList = new ArrayList<>( inListExpressions.size() );
|
||||
for ( Expression inListExpression : inListExpressions ) {
|
||||
if ( inListExpression instanceof SqlTuple ) {
|
||||
//noinspection unchecked
|
||||
valuesList.add( new Values( (List<Expression>) ( (SqlTuple) inListExpression ).getExpressions() ) );
|
||||
}
|
||||
else {
|
||||
valuesList.add( new Values( Collections.singletonList( inListExpression ) ) );
|
||||
}
|
||||
}
|
||||
final TableGroup rootTableGroup = entityDescriptor.createRootTableGroup(
|
||||
true,
|
||||
updatingTableGroup.getNavigablePath(),
|
||||
updatingTableGroup.getSourceAlias(),
|
||||
() -> predicate -> {},
|
||||
new SqlAliasBaseImpl( updatingTableGroup.getGroupAlias() ),
|
||||
null,
|
||||
null,
|
||||
sessionFactory
|
||||
);
|
||||
final List<String> columnNames;
|
||||
final Predicate joinPredicate;
|
||||
if ( keyExpression instanceof SqlTuple ) {
|
||||
final List<? extends Expression> expressions = ( (SqlTuple) keyExpression ).getExpressions();
|
||||
final List<Expression> lhs = new ArrayList<>( expressions.size() );
|
||||
final List<Expression> rhs = new ArrayList<>( expressions.size() );
|
||||
columnNames = new ArrayList<>( expressions.size() );
|
||||
entityDescriptor.getIdentifierMapping().forEachSelectable(
|
||||
(i, selectableMapping) -> {
|
||||
final Expression expression = expressions.get( i );
|
||||
final ColumnReference columnReference = expression.getColumnReference();
|
||||
final ColumnReference valuesColumnReference = new ColumnReference(
|
||||
valuesPath.getLocalName(),
|
||||
columnReference.getColumnExpression(),
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
columnReference.getJdbcMapping(),
|
||||
null
|
||||
);
|
||||
columnNames.add( columnReference.getColumnExpression() );
|
||||
lhs.add( valuesColumnReference );
|
||||
rhs.add(
|
||||
new ColumnReference(
|
||||
rootTableGroup.getPrimaryTableReference(),
|
||||
selectableMapping.getSelectionExpression(),
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
columnReference.getJdbcMapping(),
|
||||
null
|
||||
)
|
||||
);
|
||||
querySpec.getSelectClause().addSqlSelection(
|
||||
new SqlSelectionImpl( 1, 0, valuesColumnReference )
|
||||
);
|
||||
}
|
||||
);
|
||||
joinPredicate = new ComparisonPredicate(
|
||||
new SqlTuple( lhs, entityDescriptor.getIdentifierMapping() ),
|
||||
ComparisonOperator.EQUAL,
|
||||
new SqlTuple( rhs, entityDescriptor.getIdentifierMapping() )
|
||||
);
|
||||
}
|
||||
else {
|
||||
final ColumnReference columnReference = keyExpression.getColumnReference();
|
||||
final ColumnReference valuesColumnReference = new ColumnReference(
|
||||
valuesPath.getLocalName(),
|
||||
columnReference.getColumnExpression(),
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
columnReference.getJdbcMapping(),
|
||||
null
|
||||
);
|
||||
columnNames = Collections.singletonList( columnReference.getColumnExpression() );
|
||||
joinPredicate = new ComparisonPredicate(
|
||||
valuesColumnReference,
|
||||
ComparisonOperator.EQUAL,
|
||||
new ColumnReference(
|
||||
rootTableGroup.getPrimaryTableReference(),
|
||||
( (BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping() ).getSelectionExpression(),
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
columnReference.getJdbcMapping(),
|
||||
null
|
||||
)
|
||||
);
|
||||
querySpec.getSelectClause().addSqlSelection(
|
||||
new SqlSelectionImpl( 1, 0, valuesColumnReference )
|
||||
);
|
||||
}
|
||||
final ValuesTableGroup valuesTableGroup = new ValuesTableGroup(
|
||||
valuesPath,
|
||||
null,
|
||||
valuesList,
|
||||
valuesPath.getLocalName(),
|
||||
columnNames,
|
||||
true,
|
||||
sessionFactory
|
||||
);
|
||||
valuesTableGroup.addNestedTableGroupJoin(
|
||||
new TableGroupJoin(
|
||||
rootTableGroup.getNavigablePath(),
|
||||
SqlAstJoinType.LEFT,
|
||||
rootTableGroup,
|
||||
joinPredicate
|
||||
)
|
||||
);
|
||||
querySpec.getFromClause().addRoot( valuesTableGroup );
|
||||
// Only when the target row does not exist
|
||||
querySpec.applyPredicate(
|
||||
new NullnessPredicate(
|
||||
new ColumnReference(
|
||||
rootTableGroup.resolveTableReference( tableExpression ),
|
||||
columnNames.get( 0 ),
|
||||
entityDescriptor.getIdentifierMapping().getJdbcMappings().get( 0 ),
|
||||
null
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// Collect the target column references from the key expressions
|
||||
final List<ColumnReference> targetColumnReferences = new ArrayList<>();
|
||||
if ( keyExpression instanceof SqlTuple ) {
|
||||
//noinspection unchecked
|
||||
targetColumnReferences.addAll( (Collection<? extends ColumnReference>) ( (SqlTuple) keyExpression ).getExpressions() );
|
||||
}
|
||||
else {
|
||||
targetColumnReferences.add( (ColumnReference) keyExpression );
|
||||
}
|
||||
// And transform assignments to target column references and selections
|
||||
for ( Assignment assignment : assignments ) {
|
||||
targetColumnReferences.addAll( assignment.getAssignable().getColumnReferences() );
|
||||
querySpec.getSelectClause().addSqlSelection(
|
||||
new SqlSelectionImpl(
|
||||
0,
|
||||
-1,
|
||||
assignment.getAssignedValue()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
final InsertStatement insertSqlAst = new InsertStatement(
|
||||
dmlTableReference
|
||||
);
|
||||
insertSqlAst.addTargetColumnReferences( targetColumnReferences.toArray( new ColumnReference[0] ) );
|
||||
insertSqlAst.setSourceSelectStatement( querySpec );
|
||||
|
||||
final JdbcInsert jdbcInsert = jdbcServices.getJdbcEnvironment()
|
||||
.getSqlAstTranslatorFactory()
|
||||
.buildInsertTranslator( sessionFactory, insertSqlAst )
|
||||
.translate( jdbcParameterBindings, executionContext.getQueryOptions() );
|
||||
|
||||
final int insertCount = jdbcServices.getJdbcMutationExecutor().execute(
|
||||
jdbcInsert,
|
||||
jdbcParameterBindings,
|
||||
sql -> executionContext.getSession()
|
||||
.getJdbcCoordinator()
|
||||
.getStatementPreparer()
|
||||
.prepareStatement( sql ),
|
||||
(integer, preparedStatement) -> {
|
||||
},
|
||||
executionContext
|
||||
);
|
||||
assert insertCount + updateCount == expectedUpdateCount;
|
||||
}
|
||||
}
|
||||
|
||||
private Expression asExpression(SelectClause selectClause) {
|
||||
final List<SqlSelection> sqlSelections = selectClause.getSqlSelections();
|
||||
if ( sqlSelections.size() == 1 ) {
|
||||
return sqlSelections.get( 0 ).getExpression();
|
||||
}
|
||||
final List<Expression> expressions = new ArrayList<>( sqlSelections.size() );
|
||||
for ( SqlSelection sqlSelection : sqlSelections ) {
|
||||
expressions.add( sqlSelection.getExpression() );
|
||||
}
|
||||
return new SqlTuple( expressions, null );
|
||||
}
|
||||
|
||||
private void collectTableReference(
|
||||
TableReference tableReference,
|
||||
BiConsumer<String, TableReference> consumer) {
|
||||
consumer.accept( tableReference.getIdentificationVariable(), tableReference );
|
||||
}
|
||||
|
||||
private void collectTableReference(
|
||||
TableReferenceJoin tableReferenceJoin,
|
||||
BiConsumer<String, TableReference> consumer) {
|
||||
collectTableReference( tableReferenceJoin.getJoinedTableReference(), consumer );
|
||||
}
|
||||
|
||||
private TableReference resolveTableReference(
|
||||
ColumnReference columnReference,
|
||||
Map<String, TableReference> tableReferenceByAlias) {
|
||||
final TableReference tableReferenceByQualifier = tableReferenceByAlias.get( columnReference.getQualifier() );
|
||||
if ( tableReferenceByQualifier != null ) {
|
||||
return tableReferenceByQualifier;
|
||||
}
|
||||
|
||||
throw new SemanticException( "Assignment referred to column of a joined association: " + columnReference );
|
||||
}
|
||||
|
||||
private NamedTableReference resolveUnionTableReference(
|
||||
TableReference tableReference,
|
||||
String tableExpression) {
|
||||
if ( tableReference instanceof UnionTableReference ) {
|
||||
return new NamedTableReference(
|
||||
tableExpression,
|
||||
tableReference.getIdentificationVariable(),
|
||||
tableReference.isOptional(),
|
||||
sessionFactory
|
||||
);
|
||||
}
|
||||
return (NamedTableReference) tableReference;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter;
|
|||
import org.hibernate.query.sqm.internal.SqmUtil;
|
||||
import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter;
|
||||
import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper;
|
||||
import org.hibernate.query.sqm.mutation.internal.TableKeyExpressionCollector;
|
||||
import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess;
|
||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.hibernate.query.sqm.ComparisonOperator;
|
|||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.internal.SqmUtil;
|
||||
import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter;
|
||||
import org.hibernate.query.sqm.mutation.internal.TableKeyExpressionCollector;
|
||||
import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
|
|
|
@ -12,7 +12,15 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
public class Values {
|
||||
private final List<Expression> expressions = new ArrayList<>();
|
||||
private final List<Expression> expressions;
|
||||
|
||||
public Values() {
|
||||
this.expressions = new ArrayList<>();
|
||||
}
|
||||
|
||||
public Values(List<Expression> expressions) {
|
||||
this.expressions = expressions;
|
||||
}
|
||||
|
||||
public List<Expression> getExpressions() {
|
||||
return expressions;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
package org.hibernate.orm.test.bulkid;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
@ -9,7 +9,7 @@ import jakarta.persistence.InheritanceType;
|
|||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Before;
|
||||
|
@ -21,7 +21,7 @@ import static org.junit.Assert.assertEquals;
|
|||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public abstract class AbstractBulkCompositeIdTest extends BaseCoreFunctionalTestCase {
|
||||
public abstract class AbstractMutationStrategyCompositeIdTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
|
@ -35,13 +35,13 @@ public abstract class AbstractBulkCompositeIdTest extends BaseCoreFunctionalTest
|
|||
@Override
|
||||
protected void configure(Configuration configuration) {
|
||||
super.configure( configuration );
|
||||
Class<? extends MultiTableBulkIdStrategy> strategyClass = getMultiTableBulkIdStrategyClass();
|
||||
Class<? extends SqmMultiTableMutationStrategy> strategyClass = getMultiTableBulkIdStrategyClass();
|
||||
if ( strategyClass != null ) {
|
||||
configuration.setProperty( AvailableSettings.HQL_BULK_ID_STRATEGY, strategyClass.getName() );
|
||||
configuration.setProperty( AvailableSettings.QUERY_MULTI_TABLE_MUTATION_STRATEGY, strategyClass.getName() );
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass();
|
||||
protected abstract Class<? extends SqmMultiTableMutationStrategy> getMultiTableBulkIdStrategyClass();
|
||||
|
||||
@Override
|
||||
protected boolean isCleanupTestDataRequired() {
|
|
@ -1,4 +1,4 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
package org.hibernate.orm.test.bulkid;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
|
@ -8,7 +8,7 @@ import jakarta.persistence.InheritanceType;
|
|||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Before;
|
||||
|
@ -20,7 +20,7 @@ import static org.junit.Assert.assertEquals;
|
|||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public abstract class AbstractBulkIdTest extends BaseCoreFunctionalTestCase {
|
||||
public abstract class AbstractMutationStrategyIdTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
|
@ -31,15 +31,20 @@ public abstract class AbstractBulkIdTest extends BaseCoreFunctionalTestCase {
|
|||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
protected void configure(Configuration configuration) {
|
||||
super.configure( configuration );
|
||||
configuration.setProperty( AvailableSettings.HQL_BULK_ID_STRATEGY, getMultiTableBulkIdStrategyClass().getName() );
|
||||
final Class<? extends SqmMultiTableMutationStrategy> multiTableBulkIdStrategyClass = getMultiTableBulkIdStrategyClass();
|
||||
if ( multiTableBulkIdStrategyClass != null ) {
|
||||
configuration.setProperty(
|
||||
AvailableSettings.QUERY_MULTI_TABLE_MUTATION_STRATEGY,
|
||||
multiTableBulkIdStrategyClass.getName()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected abstract Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass();
|
||||
protected abstract Class<? extends SqmMultiTableMutationStrategy> getMultiTableBulkIdStrategyClass();
|
||||
|
||||
@Override
|
||||
protected boolean isCleanupTestDataRequired() {
|
|
@ -0,0 +1,14 @@
|
|||
package org.hibernate.orm.test.bulkid;
|
||||
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class DefaultMutationStrategyCompositeIdTest extends AbstractMutationStrategyCompositeIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends SqmMultiTableMutationStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.hibernate.orm.test.bulkid;
|
||||
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class DefaultMutationStrategyIdTest extends AbstractMutationStrategyIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends SqmMultiTableMutationStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
package org.hibernate.orm.test.bulkid;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Arrays;
|
||||
|
@ -13,17 +13,13 @@ import jakarta.persistence.Temporal;
|
|||
import jakarta.persistence.TemporalType;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.inline.InlineIdsOrClauseBulkIdStrategy;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.query.sqm.mutation.internal.inline.InlineMutationStrategy;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
|
@ -31,8 +27,7 @@ import static org.junit.Assert.assertEquals;
|
|||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@TestForIssue( jiraKey = "HHH-12561" )
|
||||
public class GlobalQuotedIdentifiersBulkIdTest
|
||||
extends BaseEntityManagerFunctionalTestCase {
|
||||
public class GlobalQuotedIdentifiersBulkIdTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
|
@ -46,7 +41,7 @@ public class GlobalQuotedIdentifiersBulkIdTest
|
|||
@Override
|
||||
protected void addConfigOptions(Map options) {
|
||||
options.put( AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS, Boolean.TRUE );
|
||||
options.put( AvailableSettings.QUERY_MULTI_TABLE_MUTATION_STRATEGY, InlineIdsOrClauseBulkIdStrategy.class.getName() );
|
||||
options.put( AvailableSettings.QUERY_MULTI_TABLE_MUTATION_STRATEGY, InlineMutationStrategy.class.getName() );
|
||||
}
|
||||
|
||||
@Before
|
|
@ -0,0 +1,15 @@
|
|||
package org.hibernate.orm.test.bulkid;
|
||||
|
||||
import org.hibernate.query.sqm.mutation.internal.inline.InlineMutationStrategy;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineMutationStrategyCompositeIdTest extends AbstractMutationStrategyCompositeIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends SqmMultiTableMutationStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return InlineMutationStrategy.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package org.hibernate.orm.test.bulkid;
|
||||
|
||||
import org.hibernate.query.sqm.mutation.internal.inline.InlineMutationStrategy;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineMutationStrategyIdTest extends AbstractMutationStrategyIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends SqmMultiTableMutationStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return InlineMutationStrategy.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package org.hibernate.orm.test.bulkid;
|
||||
|
||||
/**
|
||||
* Special test that tries to update 1100 rows. Oracle only supports up to 1000 parameters per in-predicate,
|
||||
* so we want to test if this scenario works.
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class OracleInlineMutationStrategyIdTest extends InlineMutationStrategyIdTest {
|
||||
|
||||
@Override
|
||||
protected int entityCount() {
|
||||
return 1100;
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.cte.CteValuesListBulkIdStrategy;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialectFeature(DialectChecks.SupportNonQueryValuesListWithCTE.class)
|
||||
public class CteValuesListBulkCompositeIdTest extends AbstractBulkCompositeIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return CteValuesListBulkIdStrategy.class;
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.cte.CteValuesListBulkIdStrategy;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialectFeature(DialectChecks.SupportNonQueryValuesListWithCTE.class)
|
||||
public class CteValuesListBulkIdTest extends AbstractBulkIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return CteValuesListBulkIdStrategy.class;
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialectFeature(DialectChecks.SupportsGlobalTemporaryTables.class)
|
||||
public class GlobalTemporaryTableBulkCompositeIdTest extends AbstractBulkCompositeIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
// Since we only allow dialects that support global temporary tables, we avoid overriding the strategy
|
||||
// This is important because otherwise we would loose id table configurations that are made in the dialects
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.inline.InlineIdsInClauseBulkIdStrategy;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialectFeature(DialectChecks.SupportRowValueConstructorSyntaxInInList.class)
|
||||
public class InlineIdsInClauseBulkCompositeIdTest extends
|
||||
AbstractBulkCompositeIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return InlineIdsInClauseBulkIdStrategy.class;
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.inline.InlineIdsInClauseBulkIdStrategy;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialectFeature(DialectChecks.SupportRowValueConstructorSyntaxInInList.class)
|
||||
public class InlineIdsInClauseBulkIdTest extends AbstractBulkIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return InlineIdsInClauseBulkIdStrategy.class;
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineIdsOrClauseBulkCompositeIdTest extends
|
||||
AbstractBulkCompositeIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return InlineIdsOrClauseBulkIdStrategy.class;
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.inline.InlineIdsOrClauseBulkIdStrategy;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class InlineIdsOrClauseBulkIdTest extends AbstractBulkIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return InlineIdsOrClauseBulkIdStrategy.class;
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.inline.InlineIdsSubSelectValueListBulkIdStrategy;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialectFeature(DialectChecks.SupportValuesListAndRowValueConstructorSyntaxInInList.class)
|
||||
public class InlineIdsSubSelectValuesListBulkCompositeIdTest extends AbstractBulkCompositeIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return InlineIdsSubSelectValueListBulkIdStrategy.class;
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.id.inline.InlineIdsSubSelectValueListBulkIdStrategy;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialectFeature(DialectChecks.SupportValuesListAndRowValueConstructorSyntaxInInList.class)
|
||||
public class InlineIdsSubSelectValuesListBulkIdTest extends AbstractBulkIdTest {
|
||||
|
||||
@Override
|
||||
protected Class<? extends MultiTableBulkIdStrategy> getMultiTableBulkIdStrategyClass() {
|
||||
return InlineIdsSubSelectValueListBulkIdStrategy.class;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package org.hibernate.test.bulkid;
|
||||
|
||||
import org.hibernate.dialect.Oracle8iDialect;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(Oracle8iDialect.class)
|
||||
public class OracleInlineIdsInClauseBulkIdTest extends InlineIdsInClauseBulkIdTest {
|
||||
|
||||
@Override
|
||||
protected int entityCount() {
|
||||
return 1100;
|
||||
}
|
||||
}
|
|
@ -82,6 +82,9 @@ containing the various individual DML statements that would normally be executed
|
|||
This allows to run SQM DML statements in a single JDBC operation that does not move any data between the database and the application,
|
||||
which should provide a significant boost for statements that involve many rows.
|
||||
|
||||
Note that the configuration property `hibernate.hql.bulk_id_strategy` was changed to `hibernate.query.mutation_strategy`
|
||||
which will now refer to classes or objects implementing `org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy`.
|
||||
|
||||
[[identifier-object]]
|
||||
== Identifier as Object
|
||||
|
||||
|
|
Loading…
Reference in New Issue