HHH-18743 make batching explicit for StatelessSession

1. ignore hibernate.jdbc.batch_size setting
2. add insertMultiple() and friends

Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
Gavin King 2024-10-22 17:14:38 +02:00
parent dd8e186416
commit f82c581990
6 changed files with 132 additions and 14 deletions

View File

@ -203,8 +203,8 @@ public interface SharedSessionContract extends QueryProducer, AutoCloseable, Ser
/**
* Set the session-level JDBC batch size. Override the
* {@linkplain org.hibernate.boot.spi.SessionFactoryOptions#getJdbcBatchSize() factory-level}
* JDBC batch size controlled by the configuration property
* {@linkplain org.hibernate.boot.spi.SessionFactoryOptions#getJdbcBatchSize
* factory-level} JDBC batch size controlled by the configuration property
* {@value org.hibernate.cfg.AvailableSettings#STATEMENT_BATCH_SIZE}.
*
* @param jdbcBatchSize the new session-level JDBC batch size

View File

@ -60,6 +60,15 @@ import java.util.List;
* <li>when an exception is thrown by a stateless session, the current
* transaction is not automatically marked for rollback.
* </ul>
* <p>
* Since version 7, the configuration property
* {@value org.hibernate.cfg.BatchSettings#STATEMENT_BATCH_SIZE} has no effect
* on a stateless session. Automatic batching may be enabled by explicitly
* {@linkplain #setJdbcBatchSize setting the batch size}. However, automatic
* batching has the side effect of delaying execution of the batched operation,
* thus undermining the synchronous nature of operations performed through a
* stateless session. A preferred approach is to explicitly batch operations via
* {@link #insertMultiple}, {@link #updateMultiple}, or {@link #deleteMultiple}.
*
* @author Gavin King
*/
@ -85,6 +94,16 @@ public interface StatelessSession extends SharedSessionContract {
*/
Object insert(Object entity);
/**
* Insert multiple records.
*
* @param entities a list of transient instances to be inserted
*
* @since 7.0
*/
@Incubating
void insertMultiple(List<Object> entities);
/**
* Insert a record.
* <p>
@ -108,6 +127,16 @@ public interface StatelessSession extends SharedSessionContract {
*/
void update(Object entity);
/**
* Update multiple records.
*
* @param entities a list of detached instances to be updated
*
* @since 7.0
*/
@Incubating
void updateMultiple(List<Object> entities);
/**
* Update a record.
* <p>
@ -129,6 +158,16 @@ public interface StatelessSession extends SharedSessionContract {
*/
void delete(Object entity);
/**
* Delete multiple records.
*
* @param entities a list of detached instances to be deleted
*
* @since 7.0
*/
@Incubating
void deleteMultiple(List<Object> entities);
/**
* Delete a record.
* <p>
@ -164,6 +203,19 @@ public interface StatelessSession extends SharedSessionContract {
@Incubating
void upsert(Object entity);
/**
* Perform an upsert, that is, to insert the record if it does
* not exist, or update the record if it already exists, for
* each given record.
*
* @param entities a list of detached instances and new
* instances with assigned identifiers
*
* @since 7.0
*/
@Incubating
void upsertMultiple(List<Object> entities);
/**
* Use a SQL {@code merge into} statement to perform an upsert.
*

View File

@ -1402,18 +1402,19 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
return exceptionConverter;
}
@Override
public Integer getJdbcBatchSize() {
return jdbcBatchSize;
}
@Override
public EventManager getEventManager() {
return fastSessionServices.getEventManager();
public void setJdbcBatchSize(Integer jdbcBatchSize) {
this.jdbcBatchSize = jdbcBatchSize;
}
@Override
public void setJdbcBatchSize(Integer jdbcBatchSize) {
this.jdbcBatchSize = jdbcBatchSize;
public EventManager getEventManager() {
return fastSessionServices.getEventManager();
}
@Override

View File

@ -105,6 +105,7 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
temporaryPersistenceContext = new StatefulPersistenceContext( this );
influencers = new LoadQueryInfluencers( getFactory() );
setUpMultitenancy( factory, influencers );
setJdbcBatchSize( 0 );
}
@Override
@ -119,6 +120,20 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
return insert( null, entity );
}
@Override
public void insertMultiple(List<Object> entities) {
final Integer batchSize = getJdbcBatchSize();
setJdbcBatchSize( entities.size() );
try {
for ( Object entity : entities ) {
insert( null, entity );
}
}
finally {
setJdbcBatchSize( batchSize );
}
}
@Override
public Object insert(String entityName, Object entity) {
checkOpen();
@ -180,6 +195,20 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
delete( null, entity );
}
@Override
public void deleteMultiple(List<Object> entities) {
final Integer batchSize = getJdbcBatchSize();
setJdbcBatchSize( entities.size() );
try {
for ( Object entity : entities ) {
delete( null, entity );
}
}
finally {
setJdbcBatchSize( batchSize );
}
}
@Override
public void delete(String entityName, Object entity) {
checkOpen();
@ -215,8 +244,17 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
}
@Override
public void upsert(Object entity) {
upsert( null, entity );
public void updateMultiple(List<Object> entities) {
final Integer batchSize = getJdbcBatchSize();
setJdbcBatchSize( entities.size() );
try {
for ( Object entity : entities ) {
update( null, entity );
}
}
finally {
setJdbcBatchSize( batchSize );
}
}
@Override
@ -257,6 +295,25 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
}
}
@Override
public void upsert(Object entity) {
upsert( null, entity );
}
@Override
public void upsertMultiple(List<Object> entities) {
final Integer batchSize = getJdbcBatchSize();
setJdbcBatchSize( entities.size() );
try {
for ( Object entity : entities ) {
upsert( null, entity );
}
}
finally {
setJdbcBatchSize( batchSize );
}
}
@Override
public void upsert(String entityName, Object entity) {
checkOpen();

View File

@ -12,7 +12,7 @@ import org.hibernate.resource.transaction.spi.TransactionCoordinator;
/**
* Contract for something that controls a {@link JdbcSessionContext}.
* <p>
* The term "JDBC session" is taken from the SQL specification which
* The term <em>JDBC session</em> is taken from the SQL specification which
* calls a connection and its associated transaction context a "session".
*
* @apiNote The name comes from the design idea of a {@code JdbcSession}
@ -28,9 +28,9 @@ public interface JdbcSessionOwner {
JdbcConnectionAccess getJdbcConnectionAccess();
/**
* Obtain the builder for TransactionCoordinator instances
* Obtain the {@link TransactionCoordinator}.
*
* @return The TransactionCoordinatorBuilder
* @return The {@code TransactionCoordinator}
*/
TransactionCoordinator getTransactionCoordinator();
@ -43,7 +43,7 @@ public interface JdbcSessionOwner {
void startTransactionBoundary();
/**
* A after-begin callback from the coordinator to its owner.
* An after-begin callback from the coordinator to its owner.
*/
void afterTransactionBegin();
@ -63,8 +63,8 @@ public interface JdbcSessionOwner {
void flushBeforeTransactionCompletion();
/**
* Get the Session-level JDBC batch size.
* @return Session-level JDBC batch size
* Get the session-level JDBC batch size.
* @return session-level JDBC batch size
*
* @since 5.2
*/

View File

@ -340,6 +340,14 @@ must be explicitly set to true.
The signature of the `Configurable#configure` method changed from accepting just a `ServiceRegistry` instance to the new `GeneratorCreationContext` interface, which exposes a lot more useful information when configuring the generator itself. The old signature has been deprecated for removal, so you should migrate any custom `Configurable` generator implementation to the new one.
[[stateless-session-jdbc-batching]]
== JDBC batching with `StatelessSession`
Automatic JDBC batching has the side effect of delaying the execution of the batched operation, and this undermines the synchronous nature of operations performed through a stateless session.
In Hibernate 7, the configuration property `hibernate.jdbc.batch_size` now has no effect on a stateless session.
Automatic batching may be enabled by explicitly calling `setJdbcBatchSize()`.
However, the preferred approach is to explicitly batch operations via `insertMultiple()`, `updateMultiple()`, or `deleteMultiple()`.
[[hbm-transform]]
== hbm.xml Transformation