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 * Set the session-level JDBC batch size. Override the
* {@linkplain org.hibernate.boot.spi.SessionFactoryOptions#getJdbcBatchSize() factory-level} * {@linkplain org.hibernate.boot.spi.SessionFactoryOptions#getJdbcBatchSize
* JDBC batch size controlled by the configuration property * factory-level} JDBC batch size controlled by the configuration property
* {@value org.hibernate.cfg.AvailableSettings#STATEMENT_BATCH_SIZE}. * {@value org.hibernate.cfg.AvailableSettings#STATEMENT_BATCH_SIZE}.
* *
* @param jdbcBatchSize the new session-level JDBC 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 * <li>when an exception is thrown by a stateless session, the current
* transaction is not automatically marked for rollback. * transaction is not automatically marked for rollback.
* </ul> * </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 * @author Gavin King
*/ */
@ -85,6 +94,16 @@ public interface StatelessSession extends SharedSessionContract {
*/ */
Object insert(Object entity); 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. * Insert a record.
* <p> * <p>
@ -108,6 +127,16 @@ public interface StatelessSession extends SharedSessionContract {
*/ */
void update(Object entity); 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. * Update a record.
* <p> * <p>
@ -129,6 +158,16 @@ public interface StatelessSession extends SharedSessionContract {
*/ */
void delete(Object entity); 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. * Delete a record.
* <p> * <p>
@ -164,6 +203,19 @@ public interface StatelessSession extends SharedSessionContract {
@Incubating @Incubating
void upsert(Object entity); 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. * 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; return exceptionConverter;
} }
@Override
public Integer getJdbcBatchSize() { public Integer getJdbcBatchSize() {
return jdbcBatchSize; return jdbcBatchSize;
} }
@Override @Override
public EventManager getEventManager() { public void setJdbcBatchSize(Integer jdbcBatchSize) {
return fastSessionServices.getEventManager(); this.jdbcBatchSize = jdbcBatchSize;
} }
@Override @Override
public void setJdbcBatchSize(Integer jdbcBatchSize) { public EventManager getEventManager() {
this.jdbcBatchSize = jdbcBatchSize; return fastSessionServices.getEventManager();
} }
@Override @Override

View File

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

View File

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