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
|
==== Multi-table bulk HQL operations
|
||||||
|
|
||||||
`*hibernate.hql.bulk_id_strategy*` (e.g. A fully-qualified class name, an instance, or a `Class` object reference)::
|
`*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/hql/spi/id/MultiTableBulkIdStrategy.html[`org.hibernate.hql.spi.id.MultiTableBulkIdStrategy`] implementation for handling multi-table bulk HQL operations.
|
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))::
|
`*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.
|
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]]
|
[[batch]]
|
||||||
== Batching
|
== Batching
|
||||||
:sourcedir: ../../../../../test/java/org/hibernate/userguide/batch
|
: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
|
:extrasdir: extras
|
||||||
|
|
||||||
[[batch-jdbcbatch]]
|
[[batch-jdbcbatch]]
|
||||||
|
@ -165,7 +165,7 @@ Both the Hibernate native Query Language and JPQL (Java Persistence Query Langua
|
||||||
[[batch-bulk-hql-update-delete-example]]
|
[[batch-bulk-hql-update-delete-example]]
|
||||||
.Pseudo-syntax for UPDATE and DELETE statements using HQL
|
.Pseudo-syntax for UPDATE and DELETE statements using HQL
|
||||||
====
|
====
|
||||||
[source, JAVA, indent=0]
|
[source, SQL, indent=0]
|
||||||
----
|
----
|
||||||
UPDATE FROM EntityName e WHERE e.name = ?
|
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
|
==== 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
|
INSERT INTO EntityName
|
||||||
properties_list
|
properties_list
|
||||||
SELECT properties_list
|
SELECT select_list
|
||||||
FROM ...
|
FROM ...
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
Only the `INSERT INTO ... SELECT ...` form is supported.
|
Alternatively one can also declare individual values
|
||||||
You cannot specify explicit values to insert.
|
|
||||||
|
.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.
|
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`.
|
Note that `INSERT` statements are inherently non-polymorphic, so it is not possible to use an `EntityName`
|
||||||
Superclass properties are not allowed and subclass properties are irrelevant.
|
which is abstract or refer to subclass properties.
|
||||||
In other words, `INSERT` statements are inherently non-polymorphic.
|
|
||||||
|
|
||||||
The SELECT statement can be any valid HQL select query, but the return types must match the types expected by the INSERT.
|
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.
|
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.
|
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,
|
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]]
|
[[batch-bulk-hql-insert-example]]
|
||||||
.HQL INSERT statement
|
.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>>.
|
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]]
|
[[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
|
When a bulk mutation involves multiple tables, Hibernate has to issue individual DML statements to the respective tables.
|
||||||
strategies to work even when you cannot create temporary 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]]
|
[[batch-bulk-hql-strategies-class-diagram]]
|
||||||
===== 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:
|
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]]
|
[[batch-bulk-hql-temp-table-base-class-example]]
|
||||||
.Bulk-id base class entity
|
.Bulk mutation base class entity
|
||||||
====
|
====
|
||||||
[source, JAVA, indent=0]
|
[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:
|
Both the `Doctor` and `Engineer` entity classes extend the `Person` base class:
|
||||||
|
|
||||||
[[batch-bulk-hql-temp-table-sub-classes-example]]
|
[[batch-bulk-hql-temp-table-sub-classes-example]]
|
||||||
.Bulk-id subclass entities
|
.Bulk mutation subclass entities
|
||||||
====
|
====
|
||||||
[source, JAVA, indent=0]
|
[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:
|
Now, when you try to execute a bulk entity delete query:
|
||||||
|
|
||||||
[[batch-bulk-hql-temp-table-delete-query-example]]
|
[[batch-bulk-hql-temp-table-delete-query-example]]
|
||||||
.Bulk-id delete query example
|
.Bulk mutation delete query example
|
||||||
====
|
====
|
||||||
[source, JAVA, indent=0]
|
[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]
|
[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.
|
The temporary table can be either global or local, depending on the underlying database capabilities.
|
||||||
|
|
||||||
[[batch-bulk-hql-strategies-non-temporary-table]]
|
[[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
|
When the temporary table strategy can not be used because the database user lacks privilege to create temporary tables,
|
||||||
the database user lacks this privilege.
|
the `InlineMutationStrategy` must be used.
|
||||||
|
|
||||||
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`
|
|
||||||
|
|
||||||
To use this strategy, you need to configure the following configuration property:
|
To use this strategy, you need to configure the following configuration property:
|
||||||
|
|
||||||
[source,xml]
|
[source,xml]
|
||||||
----
|
----
|
||||||
<property name="hibernate.hql.bulk_id_strategy"
|
<property name="hibernate.query.mutation_strategy"
|
||||||
value="org.hibernate.hql.spi.id.inline.InlineIdsInClauseBulkIdStrategy"
|
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.
|
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.spi.SqmParameterMappingModelResolutionAccess;
|
||||||
import org.hibernate.query.sqm.sql.internal.SqlAstQueryPartProcessingStateImpl;
|
import org.hibernate.query.sqm.sql.internal.SqlAstQueryPartProcessingStateImpl;
|
||||||
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
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.query.sqm.tree.expression.SqmParameter;
|
||||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
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.graph.basic.BasicResult;
|
||||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||||
import org.hibernate.sql.results.spi.ListResultsConsumer;
|
import org.hibernate.sql.results.spi.ListResultsConsumer;
|
||||||
|
import org.hibernate.sql.results.spi.RowTransformer;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
@ -190,7 +192,7 @@ public class MatchingIdSelectionHelper {
|
||||||
* or UPDATE SQM query
|
* or UPDATE SQM query
|
||||||
*/
|
*/
|
||||||
public static List<Object> selectMatchingIds(
|
public static List<Object> selectMatchingIds(
|
||||||
SqmDeleteOrUpdateStatement sqmMutationStatement,
|
SqmDeleteOrUpdateStatement<?> sqmMutationStatement,
|
||||||
DomainParameterXref domainParameterXref,
|
DomainParameterXref domainParameterXref,
|
||||||
DomainQueryExecutionContext executionContext) {
|
DomainQueryExecutionContext executionContext) {
|
||||||
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
|
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
|
||||||
|
@ -236,46 +238,50 @@ public class MatchingIdSelectionHelper {
|
||||||
factory
|
factory
|
||||||
);
|
);
|
||||||
|
|
||||||
sqmConverter.getProcessingStateStack().push(
|
if ( sqmMutationStatement instanceof SqmDeleteStatement<?> ) {
|
||||||
new SqlAstQueryPartProcessingStateImpl(
|
// For delete statements we also want to collect FK values to execute collection table cleanups
|
||||||
matchingIdSelection.getQuerySpec(),
|
|
||||||
sqmConverter.getCurrentProcessingState(),
|
|
||||||
sqmConverter.getSqlAstCreationState(),
|
|
||||||
sqmConverter.getCurrentClauseStack()::getCurrent,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
);
|
|
||||||
entityDescriptor.visitSubTypeAttributeMappings(
|
|
||||||
attribute -> {
|
|
||||||
if ( attribute instanceof PluralAttributeMapping ) {
|
|
||||||
final PluralAttributeMapping pluralAttribute = (PluralAttributeMapping) attribute;
|
|
||||||
|
|
||||||
if ( pluralAttribute.getSeparateCollectionTable() != null ) {
|
sqmConverter.getProcessingStateStack().push(
|
||||||
// Ensure that the FK target columns are available
|
new SqlAstQueryPartProcessingStateImpl(
|
||||||
final boolean useFkTarget = !( pluralAttribute.getKeyDescriptor()
|
matchingIdSelection.getQuerySpec(),
|
||||||
.getTargetPart() instanceof EntityIdentifierMapping );
|
sqmConverter.getCurrentProcessingState(),
|
||||||
if ( useFkTarget ) {
|
sqmConverter.getSqlAstCreationState(),
|
||||||
final TableGroup mutatingTableGroup = sqmConverter.getMutatingTableGroup();
|
sqmConverter.getCurrentClauseStack()::getCurrent,
|
||||||
pluralAttribute.getKeyDescriptor().getTargetPart().applySqlSelections(
|
true
|
||||||
mutatingTableGroup.getNavigablePath(),
|
)
|
||||||
mutatingTableGroup,
|
);
|
||||||
sqmConverter,
|
entityDescriptor.visitSubTypeAttributeMappings(
|
||||||
(selection, jdbcMapping) -> {
|
attribute -> {
|
||||||
matchingIdSelection.getDomainResultDescriptors().add(
|
if ( attribute instanceof PluralAttributeMapping ) {
|
||||||
new BasicResult<>(
|
final PluralAttributeMapping pluralAttribute = (PluralAttributeMapping) attribute;
|
||||||
selection.getValuesArrayPosition(),
|
|
||||||
null,
|
if ( pluralAttribute.getSeparateCollectionTable() != null ) {
|
||||||
jdbcMapping.getJavaTypeDescriptor()
|
// Ensure that the FK target columns are available
|
||||||
)
|
final boolean useFkTarget = !( pluralAttribute.getKeyDescriptor()
|
||||||
);
|
.getTargetPart() instanceof EntityIdentifierMapping );
|
||||||
}
|
if ( useFkTarget ) {
|
||||||
);
|
final TableGroup mutatingTableGroup = sqmConverter.getMutatingTableGroup();
|
||||||
|
pluralAttribute.getKeyDescriptor().getTargetPart().applySqlSelections(
|
||||||
|
mutatingTableGroup.getNavigablePath(),
|
||||||
|
mutatingTableGroup,
|
||||||
|
sqmConverter,
|
||||||
|
(selection, jdbcMapping) -> {
|
||||||
|
matchingIdSelection.getDomainResultDescriptors().add(
|
||||||
|
new BasicResult<>(
|
||||||
|
selection.getValuesArrayPosition(),
|
||||||
|
null,
|
||||||
|
jdbcMapping.getJavaTypeDescriptor()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
);
|
||||||
);
|
sqmConverter.getProcessingStateStack().pop();
|
||||||
sqmConverter.getProcessingStateStack().pop();
|
}
|
||||||
|
|
||||||
final JdbcServices jdbcServices = factory.getJdbcServices();
|
final JdbcServices jdbcServices = factory.getJdbcServices();
|
||||||
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
|
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
|
||||||
|
@ -318,11 +324,18 @@ public class MatchingIdSelectionHelper {
|
||||||
);
|
);
|
||||||
lockOptions.setLockMode( lockMode );
|
lockOptions.setLockMode( lockMode );
|
||||||
|
|
||||||
|
final RowTransformer<Object> rowTransformer;
|
||||||
|
if ( matchingIdSelection.getDomainResultDescriptors().size() == 1 ) {
|
||||||
|
rowTransformer = row -> row[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rowTransformer = row -> row;
|
||||||
|
}
|
||||||
return jdbcServices.getJdbcSelectExecutor().list(
|
return jdbcServices.getJdbcSelectExecutor().list(
|
||||||
idSelectJdbcOperation,
|
idSelectJdbcOperation,
|
||||||
jdbcParameterBindings,
|
jdbcParameterBindings,
|
||||||
SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( executionContext ),
|
SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( executionContext ),
|
||||||
row -> row[0],
|
rowTransformer,
|
||||||
ListResultsConsumer.UniqueSemantic.FILTER
|
ListResultsConsumer.UniqueSemantic.FILTER
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
* 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
|
* 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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -17,17 +17,17 @@ import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
class TableKeyExpressionCollector {
|
public class TableKeyExpressionCollector {
|
||||||
private final EntityMappingType entityMappingType;
|
|
||||||
|
|
||||||
TableKeyExpressionCollector(EntityMappingType entityMappingType) {
|
private final EntityMappingType entityMappingType;
|
||||||
|
private Expression firstColumnExpression;
|
||||||
|
private List<Expression> collectedColumnExpressions;
|
||||||
|
|
||||||
|
public TableKeyExpressionCollector(EntityMappingType entityMappingType) {
|
||||||
this.entityMappingType = entityMappingType;
|
this.entityMappingType = entityMappingType;
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression firstColumnExpression;
|
public void apply(ColumnReference columnReference) {
|
||||||
List<Expression> collectedColumnExpressions;
|
|
||||||
|
|
||||||
void apply(ColumnReference columnReference) {
|
|
||||||
if ( firstColumnExpression == null ) {
|
if ( firstColumnExpression == null ) {
|
||||||
firstColumnExpression = columnReference;
|
firstColumnExpression = columnReference;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ class TableKeyExpressionCollector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression buildKeyExpression() {
|
public Expression buildKeyExpression() {
|
||||||
if ( collectedColumnExpressions == null ) {
|
if ( collectedColumnExpressions == null ) {
|
||||||
return firstColumnExpression;
|
return firstColumnExpression;
|
||||||
}
|
}
|
|
@ -36,7 +36,7 @@ import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||||
import org.hibernate.sql.results.graph.basic.BasicResult;
|
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
|
* @author Christian Beikov
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -43,7 +43,7 @@ import org.hibernate.sql.exec.spi.StatementCreatorHelper;
|
||||||
*/
|
*/
|
||||||
public class InlineDeleteHandler implements DeleteHandler {
|
public class InlineDeleteHandler implements DeleteHandler {
|
||||||
private final MatchingIdRestrictionProducer matchingIdsPredicateProducer;
|
private final MatchingIdRestrictionProducer matchingIdsPredicateProducer;
|
||||||
private final SqmDeleteStatement sqmDeleteStatement;
|
private final SqmDeleteStatement<?> sqmDeleteStatement;
|
||||||
private final DomainParameterXref domainParameterXref;
|
private final DomainParameterXref domainParameterXref;
|
||||||
|
|
||||||
private final DomainQueryExecutionContext executionContext;
|
private final DomainQueryExecutionContext executionContext;
|
||||||
|
@ -54,7 +54,7 @@ public class InlineDeleteHandler implements DeleteHandler {
|
||||||
|
|
||||||
protected InlineDeleteHandler(
|
protected InlineDeleteHandler(
|
||||||
MatchingIdRestrictionProducer matchingIdsPredicateProducer,
|
MatchingIdRestrictionProducer matchingIdsPredicateProducer,
|
||||||
SqmDeleteStatement sqmDeleteStatement,
|
SqmDeleteStatement<?> sqmDeleteStatement,
|
||||||
DomainParameterXref domainParameterXref,
|
DomainParameterXref domainParameterXref,
|
||||||
DomainQueryExecutionContext context) {
|
DomainQueryExecutionContext context) {
|
||||||
this.sqmDeleteStatement = sqmDeleteStatement;
|
this.sqmDeleteStatement = sqmDeleteStatement;
|
||||||
|
|
|
@ -26,17 +26,17 @@ import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class InlineMutationStrategy implements SqmMultiTableMutationStrategy {
|
public class InlineMutationStrategy implements SqmMultiTableMutationStrategy {
|
||||||
private final Function<SqmDeleteOrUpdateStatement,MatchingIdRestrictionProducer> matchingIdsStrategy;
|
private final Function<SqmDeleteOrUpdateStatement<?>,MatchingIdRestrictionProducer> matchingIdsStrategy;
|
||||||
|
|
||||||
public InlineMutationStrategy(Dialect dialect) {
|
public InlineMutationStrategy(Dialect dialect) {
|
||||||
this( determinePredicateProducer( dialect ) );
|
this( determinePredicateProducer( dialect ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Function<SqmDeleteOrUpdateStatement,MatchingIdRestrictionProducer> determinePredicateProducer(Dialect dialect) {
|
private static Function<SqmDeleteOrUpdateStatement<?>,MatchingIdRestrictionProducer> determinePredicateProducer(Dialect dialect) {
|
||||||
return statement -> new InPredicateRestrictionProducer();
|
return statement -> new InPredicateRestrictionProducer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public InlineMutationStrategy(Function<SqmDeleteOrUpdateStatement,MatchingIdRestrictionProducer> matchingIdsStrategy) {
|
public InlineMutationStrategy(Function<SqmDeleteOrUpdateStatement<?>,MatchingIdRestrictionProducer> matchingIdsStrategy) {
|
||||||
this.matchingIdsStrategy = matchingIdsStrategy;
|
this.matchingIdsStrategy = matchingIdsStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,21 +6,85 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.sqm.mutation.internal.inline;
|
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.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.spi.DomainQueryExecutionContext;
|
||||||
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
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.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.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.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.ExecutionContext;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcInsert;
|
||||||
import org.hibernate.sql.exec.spi.JdbcMutationExecutor;
|
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
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class InlineUpdateHandler implements UpdateHandler {
|
public class InlineUpdateHandler implements UpdateHandler {
|
||||||
private final SqmUpdateStatement sqmUpdate;
|
private final SqmUpdateStatement<?> sqmUpdate;
|
||||||
private final DomainParameterXref domainParameterXref;
|
private final DomainParameterXref domainParameterXref;
|
||||||
private final MatchingIdRestrictionProducer matchingIdsPredicateProducer;
|
private final MatchingIdRestrictionProducer matchingIdsPredicateProducer;
|
||||||
|
|
||||||
|
@ -32,7 +96,7 @@ public class InlineUpdateHandler implements UpdateHandler {
|
||||||
|
|
||||||
public InlineUpdateHandler(
|
public InlineUpdateHandler(
|
||||||
MatchingIdRestrictionProducer matchingIdsPredicateProducer,
|
MatchingIdRestrictionProducer matchingIdsPredicateProducer,
|
||||||
SqmUpdateStatement sqmUpdate,
|
SqmUpdateStatement<?> sqmUpdate,
|
||||||
DomainParameterXref domainParameterXref,
|
DomainParameterXref domainParameterXref,
|
||||||
DomainQueryExecutionContext context) {
|
DomainQueryExecutionContext context) {
|
||||||
this.matchingIdsPredicateProducer = matchingIdsPredicateProducer;
|
this.matchingIdsPredicateProducer = matchingIdsPredicateProducer;
|
||||||
|
@ -48,6 +112,527 @@ public class InlineUpdateHandler implements UpdateHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int execute(DomainQueryExecutionContext executionContext) {
|
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.internal.SqmUtil;
|
||||||
import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter;
|
import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter;
|
||||||
import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper;
|
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.spi.SqmParameterMappingModelResolutionAccess;
|
||||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
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.DomainParameterXref;
|
||||||
import org.hibernate.query.sqm.internal.SqmUtil;
|
import org.hibernate.query.sqm.internal.SqmUtil;
|
||||||
import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter;
|
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.spi.SqmParameterMappingModelResolutionAccess;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||||
|
|
|
@ -12,7 +12,15 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Values {
|
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() {
|
public List<Expression> getExpressions() {
|
||||||
return expressions;
|
return expressions;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.hibernate.test.bulkid;
|
package org.hibernate.orm.test.bulkid;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -9,7 +9,7 @@ import jakarta.persistence.InheritanceType;
|
||||||
|
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.cfg.Configuration;
|
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.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -21,7 +21,7 @@ import static org.junit.Assert.assertEquals;
|
||||||
/**
|
/**
|
||||||
* @author Vlad Mihalcea
|
* @author Vlad Mihalcea
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractBulkCompositeIdTest extends BaseCoreFunctionalTestCase {
|
public abstract class AbstractMutationStrategyCompositeIdTest extends BaseCoreFunctionalTestCase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<?>[] getAnnotatedClasses() {
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
@ -35,13 +35,13 @@ public abstract class AbstractBulkCompositeIdTest extends BaseCoreFunctionalTest
|
||||||
@Override
|
@Override
|
||||||
protected void configure(Configuration configuration) {
|
protected void configure(Configuration configuration) {
|
||||||
super.configure( configuration );
|
super.configure( configuration );
|
||||||
Class<? extends MultiTableBulkIdStrategy> strategyClass = getMultiTableBulkIdStrategyClass();
|
Class<? extends SqmMultiTableMutationStrategy> strategyClass = getMultiTableBulkIdStrategyClass();
|
||||||
if ( strategyClass != null ) {
|
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
|
@Override
|
||||||
protected boolean isCleanupTestDataRequired() {
|
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.Entity;
|
||||||
import jakarta.persistence.GeneratedValue;
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
@ -8,7 +8,7 @@ import jakarta.persistence.InheritanceType;
|
||||||
|
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.cfg.Configuration;
|
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.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -20,7 +20,7 @@ import static org.junit.Assert.assertEquals;
|
||||||
/**
|
/**
|
||||||
* @author Vlad Mihalcea
|
* @author Vlad Mihalcea
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractBulkIdTest extends BaseCoreFunctionalTestCase {
|
public abstract class AbstractMutationStrategyIdTest extends BaseCoreFunctionalTestCase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<?>[] getAnnotatedClasses() {
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
@ -31,15 +31,20 @@ public abstract class AbstractBulkIdTest extends BaseCoreFunctionalTestCase {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure(Configuration configuration) {
|
protected void configure(Configuration configuration) {
|
||||||
super.configure( 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
|
@Override
|
||||||
protected boolean isCleanupTestDataRequired() {
|
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.sql.Timestamp;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -13,17 +13,13 @@ import jakarta.persistence.Temporal;
|
||||||
import jakarta.persistence.TemporalType;
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.cfg.Configuration;
|
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
import org.hibernate.query.sqm.mutation.internal.inline.InlineMutationStrategy;
|
||||||
import org.hibernate.hql.spi.id.inline.InlineIdsOrClauseBulkIdStrategy;
|
|
||||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
|
||||||
|
|
||||||
import org.hibernate.testing.TestForIssue;
|
import org.hibernate.testing.TestForIssue;
|
||||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
|
||||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
@ -31,8 +27,7 @@ import static org.junit.Assert.assertEquals;
|
||||||
* @author Vlad Mihalcea
|
* @author Vlad Mihalcea
|
||||||
*/
|
*/
|
||||||
@TestForIssue( jiraKey = "HHH-12561" )
|
@TestForIssue( jiraKey = "HHH-12561" )
|
||||||
public class GlobalQuotedIdentifiersBulkIdTest
|
public class GlobalQuotedIdentifiersBulkIdTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
extends BaseEntityManagerFunctionalTestCase {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<?>[] getAnnotatedClasses() {
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
@ -46,7 +41,7 @@ public class GlobalQuotedIdentifiersBulkIdTest
|
||||||
@Override
|
@Override
|
||||||
protected void addConfigOptions(Map options) {
|
protected void addConfigOptions(Map options) {
|
||||||
options.put( AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS, Boolean.TRUE );
|
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
|
@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,
|
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.
|
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-object]]
|
||||||
== Identifier as Object
|
== Identifier as Object
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue