This is an automated email from the ASF dual-hosted git repository.

mchades pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new 1ff0802039 [#7744] improvement(core): allow nested SqlSession call 
(#8242)
1ff0802039 is described below

commit 1ff08020398925893c66327b78320b2cb9b90c5f
Author: mchades <[email protected]>
AuthorDate: Tue Aug 26 21:58:42 2025 +0800

    [#7744] improvement(core): allow nested SqlSession call (#8242)
    
    ### What changes were proposed in this pull request?
    
    - add sessionCount for lifecycle management
    
    ### Why are the changes needed?
    
    Fix: #7744
    
    ### Does this PR introduce _any_ user-facing change?
    
    no
    
    ### How was this patch tested?
    
    tests added
---
 .../relational/service/GroupMetaService.java       |   4 +-
 .../relational/service/JobTemplateMetaService.java |   2 +-
 .../relational/service/ModelMetaService.java       |   2 +-
 .../service/ModelVersionMetaService.java           |  10 +-
 .../relational/service/PolicyMetaService.java      |   8 +-
 .../relational/service/RoleMetaService.java        |   8 +-
 .../relational/service/TableColumnMetaService.java |   2 +-
 .../relational/service/TableMetaService.java       |   4 +-
 .../storage/relational/service/TagMetaService.java |  12 +-
 .../relational/service/UserMetaService.java        |   4 +-
 .../storage/relational/session/SqlSessions.java    |  83 ++++++++-----
 .../storage/relational/utils/SessionUtils.java     | 135 ++++++++-------------
 .../storage/relational/session/TestSqlSession.java |   6 +
 13 files changed, 136 insertions(+), 144 deletions(-)

diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/GroupMetaService.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/GroupMetaService.java
index 4329b3a0a1..e5a32171df 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/GroupMetaService.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/GroupMetaService.java
@@ -288,12 +288,12 @@ public class GroupMetaService {
     SessionUtils.doMultipleWithCommit(
         () ->
             groupDeletedCount[0] =
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     GroupMetaMapper.class,
                     mapper -> 
mapper.deleteGroupMetasByLegacyTimeline(legacyTimeline, limit)),
         () ->
             groupRoleRelDeletedCount[0] =
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     GroupRoleRelMapper.class,
                     mapper ->
                         
mapper.deleteGroupRoleRelMetasByLegacyTimeline(legacyTimeline, limit)));
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/JobTemplateMetaService.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/JobTemplateMetaService.java
index c8c61cb51f..a042d8ba70 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/JobTemplateMetaService.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/JobTemplateMetaService.java
@@ -117,7 +117,7 @@ public class JobTemplateMetaService {
                     
mapper.softDeleteJobMetaByMetalakeAndTemplate(metalakeName, jobTemplateName)),
         () ->
             result.set(
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     JobTemplateMetaMapper.class,
                     mapper ->
                         mapper.softDeleteJobTemplateMetaByMetalakeAndName(
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/ModelMetaService.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/ModelMetaService.java
index 2a5db664c7..58c46d6a01 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/ModelMetaService.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/ModelMetaService.java
@@ -137,7 +137,7 @@ public class ModelMetaService {
         // delete model meta
         () ->
             modelDeletedCount.set(
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     ModelMetaMapper.class,
                     mapper ->
                         
mapper.softDeleteModelMetaBySchemaIdAndModelName(schemaId, ident.name()))),
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/ModelVersionMetaService.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/ModelVersionMetaService.java
index 085ae844dd..0c3caa52b8 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/ModelVersionMetaService.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/ModelVersionMetaService.java
@@ -205,7 +205,7 @@ public class ModelVersionMetaService {
         // Delete model version relations first
         () ->
             modelVersionDeletedCount.set(
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     ModelVersionMetaMapper.class,
                     mapper -> {
                       if (isVersionNumber) {
@@ -245,13 +245,13 @@ public class ModelVersionMetaService {
     SessionUtils.doMultipleWithCommit(
         () ->
             modelVersionDeletedCount[0] =
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     ModelVersionMetaMapper.class,
                     mapper ->
                         
mapper.deleteModelVersionMetasByLegacyTimeline(legacyTimeline, limit)),
         () ->
             modelVersionAliasRelDeletedCount[0] =
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     ModelVersionAliasRelMapper.class,
                     mapper ->
                         
mapper.deleteModelVersionAliasRelsByLegacyTimeline(legacyTimeline, limit)));
@@ -334,7 +334,7 @@ public class ModelVersionMetaService {
             if (isModelVersionUriUpdated) {
               // delete old model version POs first
               updateResult.addAndGet(
-                  SessionUtils.doWithoutCommitAndFetchResult(
+                  SessionUtils.getWithoutCommit(
                       ModelVersionMetaMapper.class,
                       mapper -> {
                         if (isVersionNumber) {
@@ -355,7 +355,7 @@ public class ModelVersionMetaService {
             } else {
               // update model version POs directly
               updateResult.addAndGet(
-                  SessionUtils.doWithoutCommitAndFetchResult(
+                  SessionUtils.getWithoutCommit(
                       ModelVersionMetaMapper.class,
                       mapper ->
                           mapper.updateModelVersionMeta(
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/PolicyMetaService.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/PolicyMetaService.java
index add500f5b1..af45cc0c85 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/PolicyMetaService.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/PolicyMetaService.java
@@ -175,13 +175,13 @@ public class PolicyMetaService {
     SessionUtils.doMultipleWithCommit(
         () ->
             policyMetaDeletedCount[0] =
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     PolicyMetaMapper.class,
                     mapper ->
                         
mapper.softDeletePolicyByMetalakeAndPolicyName(metalakeName, ident.name())),
         () ->
             policyVersionDeletedCount[0] =
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     PolicyVersionMapper.class,
                     mapper ->
                         mapper.softDeletePolicyVersionByMetalakeAndPolicyName(
@@ -203,7 +203,7 @@ public class PolicyMetaService {
               metalakeId, metadataObject.fullName(), metadataObject.type());
 
       PolicyPOs =
-          SessionUtils.doWithoutCommitAndFetchResult(
+          SessionUtils.getWithoutCommit(
               PolicyMetadataObjectRelMapper.class,
               mapper ->
                   mapper.listPolicyPOsByMetadataObjectIdAndType(
@@ -350,7 +350,7 @@ public class PolicyMetaService {
 
       // Fetch all the policies associated with the metadata object after the 
operation.
       List<PolicyPO> policyPOs =
-          SessionUtils.doWithoutCommitAndFetchResult(
+          SessionUtils.getWithoutCommit(
               PolicyMetadataObjectRelMapper.class,
               mapper ->
                   mapper.listPolicyPOsByMetadataObjectIdAndType(
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/RoleMetaService.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/RoleMetaService.java
index c75462005a..9c3f324966 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/RoleMetaService.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/RoleMetaService.java
@@ -336,23 +336,23 @@ public class RoleMetaService {
     SessionUtils.doMultipleWithCommit(
         () ->
             roleDeletedCount[0] =
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     RoleMetaMapper.class,
                     mapper -> 
mapper.deleteRoleMetasByLegacyTimeline(legacyTimeline, limit)),
         () ->
             userRoleRelDeletedCount[0] =
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     UserRoleRelMapper.class,
                     mapper -> 
mapper.deleteUserRoleRelMetasByLegacyTimeline(legacyTimeline, limit)),
         () ->
             groupRoleRelDeletedCount[0] =
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     GroupRoleRelMapper.class,
                     mapper ->
                         
mapper.deleteGroupRoleRelMetasByLegacyTimeline(legacyTimeline, limit)),
         () ->
             securableObjectsCount[0] =
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     SecurableObjectMapper.class,
                     mapper ->
                         
mapper.deleteSecurableObjectsByLegacyTimeline(legacyTimeline, limit)));
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/TableColumnMetaService.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/TableColumnMetaService.java
index 0fb78573be..7391dffd25 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/TableColumnMetaService.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/TableColumnMetaService.java
@@ -100,7 +100,7 @@ public class TableColumnMetaService {
   boolean deleteColumnsByTableId(Long tableId) {
     // deleteColumns will be done in deleteTable transaction, so we don't do 
commit here.
     Integer result =
-        SessionUtils.doWithoutCommitAndFetchResult(
+        SessionUtils.getWithoutCommit(
             TableColumnMapper.class, mapper -> 
mapper.softDeleteColumnsByTableId(tableId));
     return result > 0;
   }
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/TableMetaService.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/TableMetaService.java
index 4d2a979342..190f3b8903 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/TableMetaService.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/TableMetaService.java
@@ -180,7 +180,7 @@ public class TableMetaService {
       SessionUtils.doMultipleWithCommit(
           () ->
               updateResult.set(
-                  SessionUtils.doWithoutCommitAndFetchResult(
+                  SessionUtils.getWithoutCommit(
                       TableMetaMapper.class,
                       mapper -> mapper.updateTableMeta(newTablePO, 
oldTablePO))),
           () -> {
@@ -217,7 +217,7 @@ public class TableMetaService {
     SessionUtils.doMultipleWithCommit(
         () ->
             deleteResult.set(
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     TableMetaMapper.class,
                     mapper -> mapper.softDeleteTableMetasByTableId(tableId))),
         () -> {
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/TagMetaService.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/TagMetaService.java
index 3b2c6d42e2..c5481357df 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/TagMetaService.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/TagMetaService.java
@@ -140,13 +140,13 @@ public class TagMetaService {
     SessionUtils.doMultipleWithCommit(
         () ->
             tagDeletedCount[0] =
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     TagMetaMapper.class,
                     mapper ->
                         
mapper.softDeleteTagMetaByMetalakeAndTagName(metalakeName, ident.name())),
         () ->
             tagMetadataObjectRelDeletedCount[0] =
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     TagMetadataObjectRelMapper.class,
                     mapper ->
                         
mapper.softDeleteTagMetadataObjectRelsByMetalakeAndTagName(
@@ -169,7 +169,7 @@ public class TagMetaService {
               metalakeId, metadataObject.fullName(), metadataObject.type());
 
       tagPOs =
-          SessionUtils.doWithoutCommitAndFetchResult(
+          SessionUtils.getWithoutCommit(
               TagMetadataObjectRelMapper.class,
               mapper ->
                   mapper.listTagPOsByMetadataObjectIdAndType(
@@ -336,7 +336,7 @@ public class TagMetaService {
 
       // Fetch all the tags associated with the metadata object after the 
operation.
       List<TagPO> tagPOs =
-          SessionUtils.doWithoutCommitAndFetchResult(
+          SessionUtils.getWithoutCommit(
               TagMetadataObjectRelMapper.class,
               mapper ->
                   mapper.listTagPOsByMetadataObjectIdAndType(
@@ -359,12 +359,12 @@ public class TagMetaService {
     SessionUtils.doMultipleWithCommit(
         () ->
             tagDeletedCount[0] =
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     TagMetaMapper.class,
                     mapper -> 
mapper.deleteTagMetasByLegacyTimeline(legacyTimeline, limit)),
         () ->
             tagMetadataObjectRelDeletedCount[0] =
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     TagMetadataObjectRelMapper.class,
                     mapper -> 
mapper.deleteTagEntityRelsByLegacyTimeline(legacyTimeline, limit)));
 
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/UserMetaService.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/UserMetaService.java
index a8991da14b..98e14fb734 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/UserMetaService.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/UserMetaService.java
@@ -285,12 +285,12 @@ public class UserMetaService {
     SessionUtils.doMultipleWithCommit(
         () ->
             userDeletedCount[0] =
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     UserMetaMapper.class,
                     mapper -> 
mapper.deleteUserMetasByLegacyTimeline(legacyTimeline, limit)),
         () ->
             userRoleRelDeletedCount[0] =
-                SessionUtils.doWithoutCommitAndFetchResult(
+                SessionUtils.getWithoutCommit(
                     UserRoleRelMapper.class,
                     mapper ->
                         
mapper.deleteUserRoleRelMetasByLegacyTimeline(legacyTimeline, limit)));
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/session/SqlSessions.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/session/SqlSessions.java
index def9c5e43f..03d6608a65 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/session/SqlSessions.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/session/SqlSessions.java
@@ -20,8 +20,11 @@
 package org.apache.gravitino.storage.relational.session;
 
 import com.google.common.annotations.VisibleForTesting;
+import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.ibatis.session.SqlSession;
 import org.apache.ibatis.session.TransactionIsolationLevel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * SqlSessions is a utility class to maintain the MyBatis's {@link SqlSession} 
object. It is a
@@ -29,7 +32,10 @@ import org.apache.ibatis.session.TransactionIsolationLevel;
  * methods to commit, rollback and close the {@link SqlSession} object.
  */
 public final class SqlSessions {
+  private static final Logger LOG = LoggerFactory.getLogger(SqlSessions.class);
   private static final ThreadLocal<SqlSession> sessions = new ThreadLocal<>();
+  private static final ThreadLocal<AtomicInteger> sessionCount =
+      ThreadLocal.withInitial(() -> new AtomicInteger(0));
 
   private SqlSessions() {}
 
@@ -38,9 +44,15 @@ public final class SqlSessions {
     return sessions;
   }
 
+  @VisibleForTesting
+  static Integer getSessionCount() {
+    return sessionCount.get().get();
+  }
+
   /**
    * Get the SqlSession object. If the SqlSession object is not present in the 
thread local, then
-   * create a new SqlSession object and set it in the thread local.
+   * create a new SqlSession object and set it in the thread local. This 
method also increments the
+   * session count.
    *
    * @return SqlSession object from the thread local storage.
    */
@@ -52,8 +64,8 @@ public final class SqlSessions {
               .getSqlSessionFactory()
               .openSession(TransactionIsolationLevel.READ_COMMITTED);
       sessions.set(sqlSession);
-      return sqlSession;
     }
+    sessionCount.get().incrementAndGet();
     return sqlSession;
   }
 
@@ -62,15 +74,7 @@ public final class SqlSessions {
    * thread local storage.
    */
   public static void commitAndCloseSqlSession() {
-    SqlSession sqlSession = sessions.get();
-    if (sqlSession != null) {
-      try {
-        sqlSession.commit();
-        sqlSession.close();
-      } finally {
-        sessions.remove();
-      }
-    }
+    handleSessionClose(true /* commit */, false /* rollback */);
   }
 
   /**
@@ -78,37 +82,58 @@ public final class SqlSessions {
    * thread local storage.
    */
   public static void rollbackAndCloseSqlSession() {
-    SqlSession sqlSession = sessions.get();
-    if (sqlSession != null) {
-      try {
-        sqlSession.rollback();
-        sqlSession.close();
-      } finally {
-        sessions.remove();
-      }
-    }
+    handleSessionClose(false /* commit */, true /* rollback */);
   }
 
   /** Close the SqlSession object and remove it from the thread local storage. 
*/
   public static void closeSqlSession() {
-    SqlSession sqlSession = sessions.get();
-    if (sqlSession != null) {
-      try {
-        sqlSession.close();
-      } finally {
-        sessions.remove();
-      }
-    }
+    handleSessionClose(false /* commit */, false /* rollback */);
   }
 
   /**
-   * Get the Mapper object from the SqlSession object.
+   * Get the Mapper object from the SqlSession object. This method will open a 
session if one is not
+   * already opened.
    *
    * @param <T> the type of the mapper interface.
    * @param className the class name of the Mapper object.
    * @return the Mapper object.
    */
   public static <T> T getMapper(Class<T> className) {
+    // getSqlSession() is called to ensure a session exists and increment the 
count.
     return getSqlSession().getMapper(className);
   }
+
+  private static void handleSessionClose(boolean commit, boolean rollback) {
+    SqlSession sqlSession = sessions.get();
+    if (sqlSession == null) {
+      return;
+    }
+
+    int count = sessionCount.get().decrementAndGet();
+    if (count == 0) {
+      try {
+        if (commit) {
+          sqlSession.commit();
+        } else if (rollback) {
+          sqlSession.rollback();
+        }
+      } finally {
+        try {
+          // Ensure the session is always closed
+          sqlSession.close();
+        } finally {
+          // Ensure ThreadLocal is always cleaned up
+          sessions.remove();
+          sessionCount.remove();
+        }
+      }
+    } else if (count < 0) {
+      // This should not happen if the session management is correct.
+      // Reset the count and remove the session to avoid further issues.
+      LOG.warn(
+          "Session count is negative: {}. Resetting session count and removing 
session.", count);
+      sessions.remove();
+      sessionCount.remove();
+    }
+  }
 }
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/utils/SessionUtils.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/utils/SessionUtils.java
index 5138b4dcbb..752d89533d 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/utils/SessionUtils.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/utils/SessionUtils.java
@@ -23,7 +23,6 @@ import java.util.Arrays;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import org.apache.gravitino.storage.relational.session.SqlSessions;
-import org.apache.ibatis.session.SqlSession;
 
 /**
  * This class provides utility methods to perform database operations with 
MyBatis mappers in the
@@ -33,116 +32,78 @@ public class SessionUtils {
   private SessionUtils() {}
 
   /**
-   * This method is used to perform a database operation with a commit. If the 
operation fails, the
-   * transaction will roll back.
-   *
-   * @param mapperClazz mapper class to be used for the operation
-   * @param consumer the operation to be performed with the mapper
-   * @param <T> the type of the mapper
+   * Performs a database operation with a commit. Manages the full transaction 
lifecycle. Can be
+   * nested within other transactions.
    */
   public static <T> void doWithCommit(Class<T> mapperClazz, Consumer<T> 
consumer) {
-    try (SqlSession session = SqlSessions.getSqlSession()) {
-      try {
-        T mapper = SqlSessions.getMapper(mapperClazz);
-        consumer.accept(mapper);
-        SqlSessions.commitAndCloseSqlSession();
-      } catch (Exception e) {
-        SqlSessions.rollbackAndCloseSqlSession();
-        throw e;
-      }
+    try {
+      T mapper = SqlSessions.getMapper(mapperClazz);
+      consumer.accept(mapper);
+      SqlSessions.commitAndCloseSqlSession();
+    } catch (Exception e) {
+      SqlSessions.rollbackAndCloseSqlSession();
+      throw e;
     }
   }
 
   /**
-   * This method is used to perform a database operation with a commit and 
fetch the result. If the
-   * operation fails, the transaction will roll back.
-   *
-   * @param mapperClazz mapper class to be used for the operation
-   * @param func the operation to be performed with the mapper
-   * @return the result of the operation
-   * @param <T> the type of the mapper
-   * @param <R> the type of the result
+   * Performs a database operation with a commit and fetches a result. Manages 
the full transaction
+   * lifecycle. Can be nested within other transactions.
    */
   public static <T, R> R doWithCommitAndFetchResult(Class<T> mapperClazz, 
Function<T, R> func) {
-    try (SqlSession session = SqlSessions.getSqlSession()) {
-      try {
-        T mapper = SqlSessions.getMapper(mapperClazz);
-        R result = func.apply(mapper);
-        SqlSessions.commitAndCloseSqlSession();
-        return result;
-      } catch (Exception e) {
-        SqlSessions.rollbackAndCloseSqlSession();
-        throw e;
-      }
+    try {
+      T mapper = SqlSessions.getMapper(mapperClazz);
+      R result = func.apply(mapper);
+      SqlSessions.commitAndCloseSqlSession();
+      return result;
+    } catch (Exception e) {
+      SqlSessions.rollbackAndCloseSqlSession();
+      throw e;
     }
   }
 
   /**
-   * This method is used to perform a database operation without a commit and 
fetch the result. If
-   * the operation fails, will throw the RuntimeException.
-   *
-   * @param mapperClazz mapper class to be used for the operation
-   * @param func the operation to be performed with the mapper
-   * @return the result of the operation
-   * @param <T> the type of the mapper
-   * @param <R> the type of the result
+   * Performs a read-only database operation without a commit. Can be used 
standalone or nested
+   * within other transactions.
    */
-  public static <T, R> R doWithoutCommitAndFetchResult(Class<T> mapperClazz, 
Function<T, R> func) {
-    T mapper = SqlSessions.getMapper(mapperClazz);
-    return func.apply(mapper);
+  public static <T, R> R getWithoutCommit(Class<T> mapperClazz, Function<T, R> 
func) {
+    try {
+      T mapper = SqlSessions.getMapper(mapperClazz);
+      return func.apply(mapper);
+    } finally {
+      // This will decrement the counter, the session is closed only when the 
counter is 0.
+      SqlSessions.closeSqlSession();
+    }
   }
 
   /**
-   * This method is used to perform a database operation without a commit. If 
the operation fails,
-   * will throw the RuntimeException.
-   *
-   * @param mapperClazz mapper class to be used for the operation
-   * @param consumer the operation to be performed with the mapper
-   * @param <T> the type of the mapper
+   * Performs a database operation without a commit. Can be used standalone or 
nested within other
+   * transactions. This method is for operations that do not return a result.
    */
   public static <T> void doWithoutCommit(Class<T> mapperClazz, Consumer<T> 
consumer) {
-    T mapper = SqlSessions.getMapper(mapperClazz);
-    consumer.accept(mapper);
-  }
-
-  /**
-   * This method is used to perform a database operation without a commit and 
fetch the result. If
-   * the operation fails, will throw a RuntimeException.
-   *
-   * @param mapperClazz mapper class to be used for the operation
-   * @param func the operation to be performed with the mapper
-   * @return the result of the operation
-   * @param <T> the type of the mapper
-   * @param <R> the type of the result
-   */
-  public static <T, R> R getWithoutCommit(Class<T> mapperClazz, Function<T, R> 
func) {
-    try (SqlSession session = SqlSessions.getSqlSession()) {
-      try {
-        T mapper = SqlSessions.getMapper(mapperClazz);
-        return func.apply(mapper);
-      } catch (Exception e) {
-        throw e;
-      } finally {
-        SqlSessions.closeSqlSession();
-      }
+    try {
+      T mapper = SqlSessions.getMapper(mapperClazz);
+      consumer.accept(mapper);
+    } finally {
+      // This will decrement the counter, the session is closed only when the 
counter is 0.
+      SqlSessions.closeSqlSession();
     }
   }
 
   /**
-   * This method is used to perform multiple database operations with a 
commit. If any of the
-   * operations fail, the transaction will totally roll back.
-   *
-   * @param operations the operations to be performed
+   * Performs multiple database operations within a single commit. Manages the 
full transaction
+   * lifecycle.
    */
   public static void doMultipleWithCommit(Runnable... operations) {
-    try (SqlSession session = SqlSessions.getSqlSession()) {
-      try {
-        Arrays.stream(operations).forEach(Runnable::run);
-        SqlSessions.commitAndCloseSqlSession();
-      } catch (Exception e) {
-        SqlSessions.rollbackAndCloseSqlSession();
-        throw e;
-      }
+    // This method acts as the outermost transaction boundary.
+    // It increments the session count once.
+    SqlSessions.getSqlSession();
+    try {
+      Arrays.stream(operations).forEach(Runnable::run);
+      SqlSessions.commitAndCloseSqlSession();
+    } catch (Exception e) {
+      SqlSessions.rollbackAndCloseSqlSession();
+      throw e;
     }
   }
 }
diff --git 
a/core/src/test/java/org/apache/gravitino/storage/relational/session/TestSqlSession.java
 
b/core/src/test/java/org/apache/gravitino/storage/relational/session/TestSqlSession.java
index 9942d2fe00..daad141318 100644
--- 
a/core/src/test/java/org/apache/gravitino/storage/relational/session/TestSqlSession.java
+++ 
b/core/src/test/java/org/apache/gravitino/storage/relational/session/TestSqlSession.java
@@ -134,23 +134,29 @@ public class TestSqlSession {
   public void testOpenAndCloseSqlSession() {
     SqlSession session = SqlSessions.getSqlSession();
     assertNotNull(session);
+    assertEquals(1, SqlSessions.getSessionCount());
     SqlSessions.closeSqlSession();
     assertNull(SqlSessions.getSessions().get());
+    assertEquals(0, SqlSessions.getSessionCount());
   }
 
   @Test
   public void testOpenAndCommitAndCloseSqlSession() {
     SqlSession session = SqlSessions.getSqlSession();
     assertNotNull(session);
+    assertEquals(1, SqlSessions.getSessionCount());
     SqlSessions.commitAndCloseSqlSession();
     assertNull(SqlSessions.getSessions().get());
+    assertEquals(0, SqlSessions.getSessionCount());
   }
 
   @Test
   public void testOpenAndRollbackAndCloseSqlSession() {
     SqlSession session = SqlSessions.getSqlSession();
     assertNotNull(session);
+    assertEquals(1, SqlSessions.getSessionCount());
     SqlSessions.rollbackAndCloseSqlSession();
     assertNull(SqlSessions.getSessions().get());
+    assertEquals(0, SqlSessions.getSessionCount());
   }
 }

Reply via email to