diff --git a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java
index c945057f00b2..109d37d56a3d 100644
--- a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java
+++ b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java
@@ -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
diff --git a/hibernate-core/src/main/java/org/hibernate/StatelessSession.java b/hibernate-core/src/main/java/org/hibernate/StatelessSession.java
index c36019afe6ef..ff107c212672 100644
--- a/hibernate-core/src/main/java/org/hibernate/StatelessSession.java
+++ b/hibernate-core/src/main/java/org/hibernate/StatelessSession.java
@@ -60,6 +60,15 @@
*
when an exception is thrown by a stateless session, the current
* transaction is not automatically marked for rollback.
*
+ *
+ * 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 entities);
+
/**
* Insert a record.
*
@@ -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 entities);
+
/**
* Update a record.
*
@@ -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 entities);
+
/**
* Delete a record.
*
@@ -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 entities);
+
/**
* Use a SQL {@code merge into} statement to perform an upsert.
*
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java
index fc4f024dc86f..db36fa91c2e0 100644
--- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java
+++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java
@@ -1402,18 +1402,19 @@ public ExceptionConverter getExceptionConverter() {
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
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java
index 8fea386caf84..377dbe06ac8c 100644
--- a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java
@@ -105,6 +105,7 @@ public StatelessSessionImpl(SessionFactoryImpl factory, SessionCreationOptions o
temporaryPersistenceContext = new StatefulPersistenceContext( this );
influencers = new LoadQueryInfluencers( getFactory() );
setUpMultitenancy( factory, influencers );
+ setJdbcBatchSize( 0 );
}
@Override
@@ -119,6 +120,20 @@ public Object insert(Object entity) {
return insert( null, entity );
}
+ @Override
+ public void insertMultiple(List 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 void delete(Object entity) {
delete( null, entity );
}
+ @Override
+ public void deleteMultiple(List 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 void update(Object entity) {
}
@Override
- public void upsert(Object entity) {
- upsert( null, entity );
+ public void updateMultiple(List 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 void update(String entityName, Object entity) {
}
}
+ @Override
+ public void upsert(Object entity) {
+ upsert( null, entity );
+ }
+
+ @Override
+ public void upsertMultiple(List 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();
diff --git a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/JdbcSessionOwner.java b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/JdbcSessionOwner.java
index 249d62624d62..fd0f82095fa4 100644
--- a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/JdbcSessionOwner.java
+++ b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/JdbcSessionOwner.java
@@ -12,7 +12,7 @@
/**
* Contract for something that controls a {@link JdbcSessionContext}.
*
- * The term "JDBC session" is taken from the SQL specification which
+ * The term JDBC session 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
*/
diff --git a/migration-guide.adoc b/migration-guide.adoc
index 530c331ddbbc..71dd79038c6a 100644
--- a/migration-guide.adoc
+++ b/migration-guide.adoc
@@ -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