HHH-16386 - Disable batching for dynamic-insert and dynamic-update

Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
Jan Schatteman 2023-03-27 16:49:56 +02:00 committed by Steve Ebersole
parent 0455e2d676
commit 1b86b2228c
4 changed files with 372 additions and 9 deletions

View File

@ -152,7 +152,7 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
final TableInclusionChecker tableInclusionChecker = getTableInclusionChecker( insertValuesAnalysis );
final MutationExecutor mutationExecutor = executor( session, staticInsertGroup );
final MutationExecutor mutationExecutor = executor( session, staticInsertGroup, false );
decomposeForInsert(
mutationExecutor,
@ -271,7 +271,7 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
final boolean[] insertability = getPropertiesToInsert( values );
final MutationOperationGroup insertGroup = generateDynamicInsertSqlGroup( insertability );
final MutationExecutor mutationExecutor = executor( session, insertGroup );
final MutationExecutor mutationExecutor = executor( session, insertGroup, true );
final InsertValuesAnalysis insertValuesAnalysis = new InsertValuesAnalysis( entityPersister(), values );
@ -301,11 +301,11 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
}
}
private MutationExecutor executor(SharedSessionContractImplementor session, MutationOperationGroup group) {
private MutationExecutor executor(SharedSessionContractImplementor session, MutationOperationGroup group, boolean dynamicUpdate) {
return session.getFactory()
.getServiceRegistry()
.getService( MutationExecutorService.class )
.createExecutor( ( session.getTransactionCoordinator() != null &&
.createExecutor( ( !dynamicUpdate && session.getTransactionCoordinator() != null &&
session.getTransactionCoordinator().isTransactionActive() ? () -> batchKey : () -> null ),
group, session );
}

View File

@ -447,7 +447,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
final EntityTableMapping mutatingTableDetails = (EntityTableMapping) versionUpdateGroup.getSingleOperation().getTableDetails();
final MutationExecutor mutationExecutor = executor( session, versionUpdateGroup );
final MutationExecutor mutationExecutor = executor( session, versionUpdateGroup, false );
final EntityVersionMapping versionMapping = entityPersister().getVersionMapping();
@ -703,7 +703,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
UpdateValuesAnalysisImpl valuesAnalysis,
SharedSessionContractImplementor session) {
final MutationExecutor mutationExecutor = executor( session, staticUpdateGroup );
final MutationExecutor mutationExecutor = executor( session, staticUpdateGroup, false );
decomposeForUpdate(
id,
@ -921,7 +921,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
// and then execute them
final MutationExecutor mutationExecutor = executor( session, dynamicUpdateGroup );
final MutationExecutor mutationExecutor = executor( session, dynamicUpdateGroup, true );
decomposeForUpdate(
id,
@ -966,11 +966,11 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
}
}
private MutationExecutor executor(SharedSessionContractImplementor session, MutationOperationGroup group) {
private MutationExecutor executor(SharedSessionContractImplementor session, MutationOperationGroup group, boolean dynamicUpdate) {
return session.getSessionFactory()
.getServiceRegistry()
.getService( MutationExecutorService.class )
.createExecutor( ( session.getTransactionCoordinator() != null &&
.createExecutor( ( !dynamicUpdate && session.getTransactionCoordinator() != null &&
session.getTransactionCoordinator().isTransactionActive() ? () -> batchKey : () -> null ),
group, session );
}

View File

@ -0,0 +1,181 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* 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.orm.test.batch;
import java.util.List;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.testing.jdbc.SQLStatementInspector;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Basic;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Inheritance;
import jakarta.persistence.PrimaryKeyJoinColumn;
import jakarta.persistence.Table;
import static jakarta.persistence.InheritanceType.JOINED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.cfg.AvailableSettings.STATEMENT_BATCH_SIZE;
/**
* @author Steve Ebersole
*/
public class BatchedMultiTableDynamicStatementTests {
@Test
@ServiceRegistry( settings = @Setting( name = STATEMENT_BATCH_SIZE, value = "2" ) )
@DomainModel( annotatedClasses = { Payment.class, CheckPayment.class } )
@SessionFactory( useCollectingStatementInspector = true )
public void testBatched(SessionFactoryScope scope) {
final SQLStatementInspector statementCollector = scope.getCollectingStatementInspector();
statementCollector.clear();
createData( scope );
assertThat( statementCollector.getSqlQueries() ).hasSize( 6 );
scope.inTransaction( (session) -> {
final List<Payment> payments = session.createSelectionQuery( "from Payment", Payment.class ).list();
assertThat( payments ).hasSize( 3 );
} );
}
@Test
@ServiceRegistry( settings = @Setting( name = STATEMENT_BATCH_SIZE, value = "-1" ) )
@DomainModel( annotatedClasses = { Payment.class, CheckPayment.class } )
@SessionFactory( useCollectingStatementInspector = true )
public void testNonBatched(SessionFactoryScope scope) {
final SQLStatementInspector statementCollector = scope.getCollectingStatementInspector();
statementCollector.clear();
createData( scope );
assertThat( statementCollector.getSqlQueries() ).hasSize( 6 );
scope.inTransaction( (session) -> {
final List<Payment> payments = session.createSelectionQuery( "from Payment", Payment.class ).list();
assertThat( payments ).hasSize( 3 );
} );
}
private static void createData(SessionFactoryScope scope) {
final CheckPayment payment = new CheckPayment();
payment.setId( 1 );
payment.setAmount( 123.00 );
payment.setRoute( "0123-45-6789" );
payment.setAccount( "0089654321" );
final CheckPayment payment2 = new CheckPayment();
payment2.setId( 2 );
payment2.setAmount( 230.00 );
payment2.setRoute( "0123-45-6789" );
payment2.setAccount( "0089654321" );
payment2.setMemo( "Car Loan" );
final CheckPayment payment3 = new CheckPayment();
payment3.setId( 3 );
payment3.setAmount( 1234.00 );
payment3.setRoute( "0123-45-6789" );
payment3.setAccount( "0089654321" );
scope.inTransaction( (session) -> {
session.persist( payment );
session.persist( payment2 );
session.persist( payment3 );
} );
}
@AfterEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
session.createMutationQuery( "delete Payment" ).executeUpdate();
} );
}
@Entity( name = "Payment" )
@Table( name = "payments" )
@Inheritance( strategy = JOINED )
@DynamicInsert @DynamicUpdate
public static class Payment {
@Id
private Integer id;
private double amount;
private String comment;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
}
@Entity( name = "CheckPayment")
@Table( name = "check_payments" )
@PrimaryKeyJoinColumn( name = "payment_fk" )
@DynamicInsert
@DynamicUpdate
public static class CheckPayment extends Payment {
@Basic(optional = false)
private String route;
@Basic(optional = false)
private String account;
private String memo;
public String getRoute() {
return route;
}
public void setRoute(String route) {
this.route = route;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getMemo() {
return memo;
}
public void setMemo(String memo) {
this.memo = memo;
}
}
}

View File

@ -0,0 +1,182 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* 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.orm.test.batch;
import java.util.List;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import org.hibernate.Transaction;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.query.Query;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
/**
* @author Jan Schatteman
*/
@ServiceRegistry(
settings = {
@Setting(name = AvailableSettings.STATEMENT_BATCH_SIZE, value = "2"),
}
)
@DomainModel(
annotatedClasses = { DynamicMutationsAndBatchingTest.EntityA.class }
)
@SessionFactory
@TestForIssue( jiraKey = "HHH-16352")
public class DynamicMutationsAndBatchingTest {
@AfterEach
public void cleanup( SessionFactoryScope scope ) {
scope.inTransaction(
s -> {
s.createMutationQuery( "delete from EntityA" ).executeUpdate();
}
);
}
@Test
public void testDynamicInserts( SessionFactoryScope scope ) {
scope.inTransaction(
s -> {
EntityA entityA1 = new EntityA(1);
entityA1.propertyA = 1;
EntityA entityA2 = new EntityA(2);
entityA2.propertyB = 2;
entityA2.propertyC = 3;
EntityA entityA3 = new EntityA(3);
entityA3.propertyA = 4;
entityA3.propertyC = 5;
s.persist(entityA1);
s.persist(entityA2);
s.persist(entityA3);
}
);
scope.inTransaction(
s -> {
Query<EntityA> query = s.createQuery( "select e from EntityA e where id = :id", EntityA.class);
EntityA a1 = query.setParameter( "id", 1 ).uniqueResult();
assertNotNull( a1 );
assertNull(a1.propertyB);
assertNull(a1.propertyC);
assertEquals( 1, a1.propertyA );
EntityA a2 = query.setParameter( "id", 2 ).uniqueResult();
assertNotNull( a2 );
assertNull( a2.propertyA );
assertEquals( 2, a2.propertyB );
assertEquals( 3, a2.propertyC );
EntityA a3 = query.setParameter( "id", 3 ).uniqueResult();
assertNotNull( a3 );
assertNull( a3.propertyB );
assertEquals( 4, a3.propertyA );
assertEquals( 5, a3.propertyC );
}
);
}
@Test
public void testDynamicUpdates( SessionFactoryScope scope ) {
scope.inTransaction(
s -> {
EntityA entityA1 = new EntityA(1);
EntityA entityA2 = new EntityA(2);
EntityA entityA3 = new EntityA(3);
s.persist(entityA1);
s.persist(entityA2);
s.persist(entityA3);
}
);
scope.inSession(
s -> {
Query<EntityA> query = s.createQuery( "select e from EntityA e order by id asc", EntityA.class);
Transaction tx = s.beginTransaction();
List<EntityA> actual = query.list();
actual.get(0).propertyA = 1;
actual.get(1).propertyA = 2;
actual.get(1).propertyB = 2;
actual.get(2).propertyA = 4;
s.flush();
tx.commit();
}
);
scope.inTransaction(
s -> {
Query<EntityA> query = s.createQuery( "select e from EntityA e where id = :id", EntityA.class);
EntityA a1 = query.setParameter( "id", 1 ).uniqueResult();
assertNotNull( a1 );
assertEquals( 1, a1.propertyA );
assertNull(a1.propertyB);
assertNull(a1.propertyC);
EntityA a2 = query.setParameter( "id", 2 ).uniqueResult();
assertNotNull( a2 );
assertNull( a2.propertyC );
assertEquals( 2, a2.propertyA );
assertEquals( 2, a2.propertyB );
EntityA a3 = query.setParameter( "id", 3 ).uniqueResult();
assertNotNull( a3 );
assertNull( a3.propertyB );
assertNull( a3.propertyC );
assertEquals( 4, a3.propertyA );
}
);
}
@DynamicInsert
@DynamicUpdate
@Entity(name="EntityA")
@Table(name = "ENTITY_A")
public static class EntityA {
@Id
@Column(name = "ID")
Integer id;
@Column(name = "PROPERTY_A")
Integer propertyA;
@Column(name = "PROPERTY_B")
Integer propertyB;
@Column(name = "PROPERTY_C")
Integer propertyC;
public EntityA() {
}
public EntityA(Integer id) {
this.id = id;
}
}
}