Hi,

after initially reporting a n^2 problem in the processRow method of RecordMappers in TORQUE-364, fixing it - only to then find our suspected concurrency troubles to be confirmed in TORQUE-372 - we can (hopefully) report a far more robust solution.

Looking into the SQL result stack we would propose to re-introduce a slightly modified MappingStrategy. The key insight to fixing the concurrent modification issue lies in doing so at the ResultsetSpliterator level. This ensures that every query gets its own MappingStrategy.

Doing this would also require two new methods for every RecordMapper to generate a fitting MappingStrategy and to process a row with a potential strategy. To keep backwards compatibility both would be defaulted in the interface to do nothing and forward to the old method respectively.

We have already implemented a test of this locally and can see similar performance to the old, but flawed, version and are sharing the patch here. (Hopefully this actually works...)

However, because the patch changes the methods the ResultsetSpliterator expects a RecordMapper to have (by calling a generateStrategy method to either do just that or return null) this would break backwards compatibility with old RecordMappers and we wanted to a feedback cycle early.

Best regards,
Max & Rene
Index: 
torque-runtime/src/main/java/org/apache/torque/om/mapper/MappingStrategy.java
<+>ISO-8859-1
===================================================================
diff --git 
a/torque-runtime/src/main/java/org/apache/torque/om/mapper/MappingStrategy.java 
b/torque-runtime/src/main/java/org/apache/torque/om/mapper/MappingStrategy.java
--- 
a/torque-runtime/src/main/java/org/apache/torque/om/mapper/MappingStrategy.java 
    (revision 1928982)
+++ 
b/torque-runtime/src/main/java/org/apache/torque/om/mapper/MappingStrategy.java 
    (date 1759875309936)
@@ -31,36 +31,36 @@
 
 /**
  * Mapping strategy used in processRow method of generated mappers.
- * 
+ *
  * @param <T>
  */
 public class MappingStrategy<T> {
-    
+
     /**
-     * The {@link Pair#getLeft()} is to allow lazy sorting, {@link 
Pair#getRight()} contains the object to be mapped 
+     * The {@link Pair#getLeft()} is to allow lazy sorting, {@link 
Pair#getRight()} contains the object to be mapped
      */
-    private final List<Pair<Integer, FailableBiConsumer<ResultSet, T, 
TorqueException>>> tasks;
-    
-    private boolean allSet;
+    private final List<Pair<Long, FailableBiConsumer<ResultSet, T, 
TorqueException>>> tasks;
 
-    public MappingStrategy()
-    {
+    public MappingStrategy() {
         this.tasks = new ArrayList<>();
-        this.allSet = false;
     }
 
-    public void addColumn(int offset, FailableBiConsumer<ResultSet, T, 
TorqueException> setter)
+    /**
+     * Add a column to be set at the given offset (column).
+     * @param offset the column offset inside a Resultset at which the value 
can be found.
+     * @param setter a method or lambda which actually sets the value.
+     */
+    public void addColumn(long offset, FailableBiConsumer<ResultSet, T, 
TorqueException> setter)
     {
         this.tasks.add(Pair.of(offset, setter));
     }
 
     /**
-     * Last finishing steps before execute.
-     * 
-     * @param num_fields the total column size of the object
-     * @param sort <code>true</code> explicitely sort with {@link 
Pair#getLeft()} of the {@link #tasks}.
+     * Last finishing steps before this strategy can be executed.
+     *
+     * @param sort <code>true</code> explicitly sort with {@link 
Pair#getLeft()} of the {@link #tasks}.
      */
-    public void finish(int num_fields, boolean sort)
+    public void finish(boolean sort)
     {
         // The list should already be in the correct order because Criteria 
loops through the columns
         // in the same order in which they are added to the SQL statement but 
just in case something weird
@@ -69,7 +69,6 @@
         if (sort) {
             this.tasks.sort(Comparator.comparing(Pair::getLeft));
         }
-        this.allSet = this.tasks.size() == num_fields;
     }
 
     public boolean isEmpty()
@@ -77,10 +76,6 @@
         return this.tasks.isEmpty();
     }
 
-    public boolean isAllSet() {
-        return this.allSet;
-    }
-    
     public void reset()
     {
         // to use this and to use only a single strategy we might need to 
Collections.sync(ArrayList).
@@ -89,14 +84,14 @@
 
     /**
      * Iterates through the {@link #tasks} list and executes each task.
-     * 
+     *
      * @param result Resultset
      * @param instance target object
-     * 
+     *
      */
     public void execute(ResultSet result, T instance)
     {
-        this.tasks.stream().forEach( strategy ->
+        this.tasks.forEach( strategy ->
            {
             try
             {
Index: 
torque-templates/src/main/resources/org/apache/torque/templates/om/templates/recordmapper/base/mappingStrategyPath.vm
<+>ISO-8859-1
===================================================================
diff --git 
a/torque-templates/src/main/resources/org/apache/torque/templates/om/templates/recordmapper/base/mappingStrategyPath.vm
 
b/torque-templates/src/main/resources/org/apache/torque/templates/om/templates/recordmapper/base/mappingStrategyPath.vm
new file mode 100644
--- /dev/null   (date 1759877905980)
+++ 
b/torque-templates/src/main/resources/org/apache/torque/templates/om/templates/recordmapper/base/mappingStrategyPath.vm
     (date 1759877905980)
@@ -0,0 +1,133 @@
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##   http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied.  See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+######
+##
+## version $Id: mappingStrategyPath.vm TIMESTAMP mwriedt $
+##
+## Creates the MappingStrategy flow for a RecordMapper.
+##
+## This template expects the current source element to be a "table" element
+## from the torque schema.
+## The schema needs to be processed by the OMTransformer.
+## The attributes of the current source element must be set
+## as velocity variables.
+##
+
+    @Override
+    public MappingStrategy<$dbObjectClassName> generateStrategy(long 
rowOffset, Criteria criteria) {
+        if (criteria == null || criteria.isComposite())
+        {
+            return null;
+        }
+        List<Column> selectColumns = criteria.getSelectColumns();
+        List<Column> columnsWithoutOffset = 
selectColumns.stream().skip(rowOffset).toList();
+        ## The selectColumns should not be empty here if we are called by the 
usual ResultsetSpliterator.
+        if (columnsWithoutOffset.isEmpty())
+        {
+            return null;
+        }
+        MappingStrategy<$dbObjectClassName> strategy = new MappingStrategy<>();
+
+        Set<String> columnsMapped = new HashSet<>();
+        long physicalColumnIndex = rowOffset + 1;
+        for (Column column : columnsWithoutOffset)
+        {
+            #set ( $else = "" )
+            #foreach ($columnElement in $torqueGen.getChildren("column"))
+                #set ( $setter = $columnElement.getAttribute("setter") )
+                #set ( $getter = $columnElement.getAttribute("getter") )
+                #set ( $peerColumnName = 
$columnElement.getAttribute("peerColumnName") )
+                ${else}if 
(!columnsMapped.contains(${peerColumnName}_EXPRESSION ) && 
${peerColumnName}_EXPRESSION.equals(
+                    column.getSqlExpression()))
+            {
+                int currentIndex = Math.toIntExact(physicalColumnIndex);
+                strategy.addColumn(physicalColumnIndex,
+                        (res, inst) -> inst.${setter}(${getter}(res, 
currentIndex)));
+                columnsMapped.add( ${peerColumnName}_EXPRESSION );
+            }
+                #set ( $else = "else " )
+            #end
+            physicalColumnIndex++;
+        }
+        if (columnsMapped.isEmpty())
+        {
+            return null;
+        }
+        return strategy;
+    }
+
+    /**
+    * Constructs the object from the current row in the resultSet.
+    * Implementing methods can be sure that the resultSet contains a row,
+    * but they must only operate on the current row, i.e they must not call
+    * resultSet.next().
+    * This version may also receive a MappingStrategy object to speed up
+    * the processing. However, the default implementation disregards any
+    * passed strategy and simply forwards to processRow(ResultSet, int, 
Criteria). //FIXME: Fix doclink!
+    *
+    * @param resultSet the resultSet to operate on, already pointing
+    *        to the correct row. Not null.
+    * @param offset a possible offset in the columns to be considered
+    *        (if previous columns contain other objects), or 0 for no offset.
+    * @param criteria the Criteria which contains the query to process,
+    *        or null if not known or the query was not produced by a Criteria.
+    *        Can be used by the RecordMapper to determine the columns
+    *        contained in the result set.
+    * @param strategy the MappingStrategy which tells the RecordMapper which
+    *        offsets in the resultSet correspond to which fields of T.
+    *        Nay be null if not known.
+    *
+    * @return the mapped object, not null.
+    *
+    * @throws TorqueException when the mapping fails.
+    */
+    @Override
+    public ${dbObjectClassName} processRow(
+        ResultSet resultSet,
+        int offset,
+        Criteria criteria,
+        MappingStrategy<${dbObjectClassName}> strategy)
+            throws TorqueException {
+        if (strategy != null)
+        {
+#set ( $inheritanceBaseColumnElement = 
$torqueGen.getChild("inheritance-column") )
+#if ($inheritanceBaseColumnElement)
+    $torqueGen.mergepoint("createDbObjectInstanceFromInheritance")
+#else
+    $torqueGen.mergepoint("createDbObjectInstance")
+#end
+
+#if ($torqueGen.booleanOption("torque.om.trackLoading"))
+            try
+            {
+            ${field}.setLoading(true);
+#end
+            strategy.execute(resultSet, ${field});
+            ${field}.setNew(false);
+            ${field}.setModified(false);
+#if ($torqueGen.booleanOption("torque.om.trackLoading"))
+            }
+            finally
+            {
+                ${field}.setLoading(false);
+            }
+#end
+            return ${field};
+        }
+        return this.processRow(resultSet, offset, criteria);
+    }
\ No newline at end of file
Index: 
torque-runtime/src/main/java/org/apache/torque/om/mapper/RecordMapper.java
<+>ISO-8859-1
===================================================================
diff --git 
a/torque-runtime/src/main/java/org/apache/torque/om/mapper/RecordMapper.java 
b/torque-runtime/src/main/java/org/apache/torque/om/mapper/RecordMapper.java
--- 
a/torque-runtime/src/main/java/org/apache/torque/om/mapper/RecordMapper.java    
    (revision 1928982)
+++ 
b/torque-runtime/src/main/java/org/apache/torque/om/mapper/RecordMapper.java    
    (date 1760554387817)
@@ -62,4 +62,43 @@
             int rowOffset,
             Criteria criteria)
                     throws TorqueException;
+
+    /**
+     * Constructs the object from the current row in the resultSet.
+     * Implementing methods can be sure that the resultSet contains a row,
+     * but they must only operate on the current row, i.e they must not call
+     * resultSet.next().
+     * This version may also receive a MappingStrategy object to speed up
+     * the processing. However, the default implementation disregards any
+     * passed strategy and simply forwards to processRow(ResultSet, int, 
Criteria).
+     *
+     * @param resultSet the resultSet to operate on, already pointing
+     *        to the correct row. Not null.
+     * @param rowOffset a possible offset in the columns to be considered
+     *        (if previous columns contain other objects), or 0 for no offset.
+     * @param criteria the Criteria which contains the query to process,
+     *        or null if not known or the query was not produced by a Criteria.
+     *        Can be used by the RecordMapper to determine the columns
+     *        contained in the result set.
+     * @param strategy the MappingStrategy which tells the RecordMapper which
+     *        offsets in the resultSet correspond to which fields of T.
+     *        Nay be null if not known.
+     *
+     * @return the mapped object, not null.
+     *
+     * @throws TorqueException when the mapping fails.
+     */
+    default T processRow(
+            ResultSet resultSet,
+            int rowOffset,
+            Criteria criteria,
+            MappingStrategy<T> strategy)
+                    throws TorqueException {
+        return this.processRow(resultSet, rowOffset, criteria);
+    }
+
+    default MappingStrategy<T> generateStrategy(long rowOffset, Criteria 
criteria)
+    {
+        return null;
+    }
 }
Index: 
torque-templates/src/main/resources/org/apache/torque/templates/om/outlets/recordMapper.xml
<+>UTF-8
===================================================================
diff --git 
a/torque-templates/src/main/resources/org/apache/torque/templates/om/outlets/recordMapper.xml
 
b/torque-templates/src/main/resources/org/apache/torque/templates/om/outlets/recordMapper.xml
--- 
a/torque-templates/src/main/resources/org/apache/torque/templates/om/outlets/recordMapper.xml
       (revision 1928982)
+++ 
b/torque-templates/src/main/resources/org/apache/torque/templates/om/outlets/recordMapper.xml
       (date 1758222925809)
@@ -48,6 +48,10 @@
           element="column"
           outlet="torque.om.recordmapper.base.dbObjectFieldGetter"/>
     </mergepoint>
+    <mergepoint name="mappingStrategyPath">
+        <action xsi:type="applyAction"
+                outlet="torque.om.recordmapper.base.mappingStrategyPath"/>
+    </mergepoint>
   </outlet>
 
   <outlet name="torque.om.recordmapper.base.dbObjectFieldGetter"
@@ -59,4 +63,18 @@
       xsi:type="velocityOutlet"
       path="recordmapper/base/createDbObjectInstanceFromInheritance.vm">
   </outlet>
+
+  <outlet name="torque.om.recordmapper.base.mappingStrategyPath"
+      xsi:type="velocityOutlet"
+      path="recordmapper/base/mappingStrategyPath.vm">
+      <mergepoint name="createDbObjectInstance">
+          <action xsi:type="applyAction"
+                  outlet="torque.om.createDbObjectInstance"/>
+      </mergepoint>
+      <mergepoint name="createDbObjectInstanceFromInheritance">
+          <action xsi:type="traverseAllAction"
+                  element="inheritance-column"
+                  
outlet="torque.om.recordmapper.basecreateDbObjectInstanceFromInheritance"/>
+      </mergepoint>
+  </outlet>
 </outlets>
\ No newline at end of file
Index: 
torque-templates/src/main/resources/org/apache/torque/templates/om/templates/recordmapper/base/recordMapperBase.vm
<+>ISO-8859-1
===================================================================
diff --git 
a/torque-templates/src/main/resources/org/apache/torque/templates/om/templates/recordmapper/base/recordMapperBase.vm
 
b/torque-templates/src/main/resources/org/apache/torque/templates/om/templates/recordmapper/base/recordMapperBase.vm
--- 
a/torque-templates/src/main/resources/org/apache/torque/templates/om/templates/recordmapper/base/recordMapperBase.vm
        (revision 1928982)
+++ 
b/torque-templates/src/main/resources/org/apache/torque/templates/om/templates/recordmapper/base/recordMapperBase.vm
        (date 1760553838523)
@@ -19,21 +19,23 @@
 ##
 ## version $Id: recordMapperBase.vm 1927854 2025-08-18 09:52:58Z mwriedt $
 ##
-## Creates the base peer's mapper class. 
-## 
-## This template expects the current source element to be a "table" element 
+## Creates the base peer's mapper class.
+##
+## This template expects the current source element to be a "table" element
 ## from the torque schema.
 ## The schema needs to be processed by the OMTransformer.
 ## The attributes of the current source element must be set
-## as velocity variables.  
+## as velocity variables.
 ##
 package ${baseRecordMapperPackage};
 
+import java.io.Serial;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -44,11 +46,11 @@
 import org.apache.torque.om.mapper.MappingStrategy;
 
 #foreach ($columnElement in 
$torqueGen.getSourceElement().getChildren("column"))
-  #set ($colEnumPackage = $columnElement.getAttribute("enumPackage"))
-  #set ($colEnumClassName = $columnElement.getAttribute("enumClassName"))
-  #if ($columnElement.getAttribute("isEnum") == "true" && $colEnumPackage != 
$baseRecordMapperPackage) 
-import ${colEnumPackage}.${colEnumClassName};
-  #end
+    #set ($colEnumPackage = $columnElement.getAttribute("enumPackage"))
+    #set ($colEnumClassName = $columnElement.getAttribute("enumClassName"))
+    #if ($columnElement.getAttribute("isEnum") == "true" && $colEnumPackage != 
$baseRecordMapperPackage)
+    import ${colEnumPackage}.${colEnumClassName};
+    #end
 #end
 #if (${baseRecordMapperPackage} != $dbObjectPackage)
 import ${dbObjectPackage}.${dbObjectClassName};
@@ -63,176 +65,123 @@
 #set ( $inheritanceBaseColumnElements = 
$torqueGen.getChildren("inheritance-column"))
 ## there should at most be one inheritance-column in each table
 #foreach ($inheritanceBaseColumnElement in $inheritanceBaseColumnElements)
-  #set ( $columnElement = $inheritanceBaseColumnElement.getChild("column") )
-  #set ( $inheritanceElements = $columnElement.getChildren("inheritance"))
-  #if ($inheritanceElements.size() > 0)
-    #if (${baseRecordMapperPackage} != $dbObjectPackage)
-      #foreach ($inheritanceElement in $inheritanceElements)
-        #set ( $inheritanceClassName = 
$inheritanceElement.getAttribute("className") )
-import ${dbObjectPackage}.${inheritanceClassName};
-      #end
-    #end
-  #end
+    #set ( $columnElement = $inheritanceBaseColumnElement.getChild("column") )
+    #set ( $inheritanceElements = $columnElement.getChildren("inheritance"))
+    #if ($inheritanceElements.size() > 0)
+        #if (${baseRecordMapperPackage} != $dbObjectPackage)
+            #foreach ($inheritanceElement in $inheritanceElements)
+                #set ( $inheritanceClassName = 
$inheritanceElement.getAttribute("className") )
+            import ${dbObjectPackage}.${inheritanceClassName};
+            #end
+        #end
+    #end
 #end
 /**
  * Maps ResultSet rows into ${dbObjectClassName} objects.
  *
-#if ($torqueGen.booleanOption("torque.om.addTimeStamp"))
- * The skeleton for this class was autogenerated by Torque on:
- *
- * [${torqueGen.now()}]
- *
-#end
+    #if ($torqueGen.booleanOption("torque.om.addTimeStamp"))
+     * The skeleton for this class was autogenerated by Torque on:
+     *
+     * [${torqueGen.now()}]
+     *
+    #end
  */
 @SuppressWarnings("unused")
 public class ${baseRecordMapperClassName} implements 
RecordMapper<${dbObjectClassName}>
 {
-#if ($torqueGen.booleanOption("torque.om.addTimeStamp"))
     /** Serial version */
+@Serial
+    #if ($torqueGen.booleanOption("torque.om.addTimeStamp"))
     private static final long serialVersionUID = ${torqueGen.now().Time}L;
 
-#else
-    /** Serial version */
-    private static final long serialVersionUID = 1L;
+    #else
+        private static final long serialVersionUID = 1L;
 
-#end
+    #end
     /** The class log. */
-    private static Log log
+    private static final Log log
             = LogFactory.getLog(${baseRecordMapperClassName}.class);
-            
+
     ## TORQUE-364: Cached SQL expressions to speed up looking for columns 
selected by a given Criteria
-#foreach ($columnElement in $torqueGen.getChildren("column"))
-    #set ( $peerColumnName = $columnElement.getAttribute("peerColumnName") )
-    private static final String ${peerColumnName}_EXPRESSION = 
${basePeerClassName}.${peerColumnName}.getSqlExpression();
-#end
+    #foreach ($columnElement in $torqueGen.getChildren("column"))
+        #set ( $peerColumnName = $columnElement.getAttribute("peerColumnName") 
)
+        private static final String ${peerColumnName}_EXPRESSION = 
${basePeerClassName}.${peerColumnName}.getSqlExpression();
+    #end
 
-    ## TORQUE-364: Removed strategy instance variable as this be cached per 
RecordMapper (Thread safety/Multi query safety?)
-    
-    ## TORQUE-364: init a new Strategy implementation
-    ## TORQUE-372: init is not Thread save cause Array 
ConcurrentModificationException
-    public MappingStrategy<${dbObjectClassName}> initStrategy()
-    {
-        #if("${useMappingStrategy}" == "true")
-        return new MappingStrategy<${dbObjectClassName}>();
-        #else
-        // no MappingStrategy since useMappingStrategy not set
-        return null;
-        #end
-    }
-#if("${simpleMapping}" == "true") #set( $useSimpleMapping=1 ) #end
-
     /**
      * Constructs the object from the current row in the resultSet.
      *
      * @param resultSet the resultSet to operate on, already pointing
-     *        to the correct row. Not null.
+     * to the correct row. Not null.
      * @param offset a possible offset in the columns to be considered
-     *        (if previous columns contain other objects),
-     *        or 0 for no offset.
+     * (if previous columns contain other objects),
+     * or 0 for no offset.
      * @param criteria The criteria which created the result set.
-     *        If set, the attributes to set in the data object
-     *        are determined from the select columns in the criteria;
-     *        if no matching column can be found, null is returned.
-     *        If not set, all of the table's columns are read from the
-     *        result set in the order defined in the table definition.
+     * If set, the attributes to set in the data object
+     * are determined from the select columns in the criteria;
+     * if no matching column can be found, null is returned.
+     * If not set, all of the table's columns are read from the
+     * result set in the order defined in the table definition.
      *
      * @return the mapped object, not null.
      *
      * @throws TorqueException when reading fields from the RecordSet fails
-     *         or if a Criteria is passed which contains select columns other
-     *         than the columns in the ${name} table.
+     * or if a Criteria is passed which contains select columns other
+     * than the columns in the ${name} table.
      */
     public $dbObjectClassName processRow(
-                ResultSet resultSet,
-                int offset,
-                Criteria criteria)
+        ResultSet resultSet,
+    int offset,
+    Criteria criteria)
             throws TorqueException
     {
-#set ( $inheritanceBaseColumnElement = 
$torqueGen.getChild("inheritance-column") )
-#if ($inheritanceBaseColumnElement)
-$torqueGen.mergepoint("createDbObjectInstanceFromInheritance")
-#else
-$torqueGen.mergepoint("createDbObjectInstance")
-#end
-#if ($torqueGen.booleanOption("torque.om.trackLoading"))
-        try 
+        #set ( $inheritanceBaseColumnElement = 
$torqueGen.getChild("inheritance-column") )
+        #if ($inheritanceBaseColumnElement)
+            $torqueGen.mergepoint("createDbObjectInstanceFromInheritance")
+        #else
+            $torqueGen.mergepoint("createDbObjectInstance")
+        #end
+        #if ($torqueGen.booleanOption("torque.om.trackLoading"))
+        try
         {
             ${field}.setLoading(true);
-#end
-        ## TORQUE-364 - simple mapping if we got the exact column size we 
assume the full table is used in criteria - skipping the mapping of every column
-        if (criteria == null #if ($useSimpleMapping) || 
(criteria.getJoins().isEmpty() && criteria.getSelectColumns().size() - offset 
==  ${basePeerClassName}.numColumns) #end)
+        #end
+        if (criteria == null)
         {
-#set ( $n = 1 )
-#foreach ($columnElement in $torqueGen.getChildren("column"))
-  #set ( $setter = $columnElement.getAttribute("setter") )
-  #set ( $getter = $columnElement.getAttribute("getter") )
+            #set ( $n = 1 )
+            #foreach ($columnElement in $torqueGen.getChildren("column"))
+                #set ( $setter = $columnElement.getAttribute("setter") )
+                #set ( $getter = $columnElement.getAttribute("getter") )
                 ${field}.${setter}(
-                        ${getter}(resultSet, offset + $n));
-  #set ( $n = $n + 1 )
-#end
-            }
-            else
-            {
-                ## TORQUE-372: thread save
-                MappingStrategy<${dbObjectClassName}> strategyLoc= 
initStrategy(); 
-
-                // try to get columns to be mapped
-                // from criteria's select columns
-                int totalOffset = offset + 1;
-                List<Column> selectColumns = criteria.getSelectColumns();
-                List<Column> columnsWithoutOffset = selectColumns.subList(
-                        offset, 
-                        selectColumns.size());
-                Set<String> columnsMapped = new HashSet<String>();
-                for (Column column : columnsWithoutOffset)
-                {
-                    final int nextOffset = totalOffset; ## leaking assignment
-#set ( $else = "" )
-#foreach ($columnElement in $torqueGen.getChildren("column"))
-  #set ( $setter = $columnElement.getAttribute("setter") )
-  #set ( $getter = $columnElement.getAttribute("getter") )
-  #set ( $peerColumnName = $columnElement.getAttribute("peerColumnName") )
-                    ${else}
-                    if (!columnsMapped.contains(${peerColumnName}_EXPRESSION ) 
&& ${peerColumnName}_EXPRESSION.equals(
-                            column.getSqlExpression()))
-                    {
-                        if (strategyLoc!=null) 
-                        {
-                            strategyLoc.addColumn(nextOffset, 
-                                (res, inst) -> inst.${setter}( ${getter}(res, 
nextOffset)));
-                        } else
-                        {
-                            ${field}.${setter}( ${getter}(resultSet, 
totalOffset));
-                        }
-                        columnsMapped.add( ${peerColumnName}_EXPRESSION );
-                    }
-  #set ( $else = "else ")
-#end
-                    totalOffset++;
-                }
-                if (columnsMapped.isEmpty())
-                {
-                    log.debug("no columns to map found in criteria, "
+                ${getter}(resultSet, offset + $n));
+                #set ( $n = $n + 1 )
+            #end
+        }
+        else
+        {
+            ## This is the slow path, but we can reuse the strategy generation 
logic
+            ## to avoid duplicating the mapping code.
+            MappingStrategy<${dbObjectClassName}> strategy = 
generateStrategy(offset, criteria);
+            if (strategy == null)
+            {
+                log.debug("Could not generate a mapping strategy for the given 
criteria, "
                         + "returning null");
-                    return null;
-                }
-                if (strategyLoc!=null)
-                {
-                  strategyLoc.finish($torqueGen.getChildren("column").size(), 
#if("${mappingStrategySort}"=="true")true#{else}false#end);
-                  strategyLoc.execute(resultSet, $field);
-                }
-            }
-            ${field}.setNew(false);
-            ${field}.setModified(false);
-#if ($torqueGen.booleanOption("torque.om.trackLoading"))
+                return null;
+            }
+            strategy.execute(resultSet, ${field});
+        }
+        ${field}.setNew(false);
+        ${field}.setModified(false);
+        #if ($torqueGen.booleanOption("torque.om.trackLoading"))
         }
         finally
         {
             ${field}.setLoading(false);
         }
-#end
+        #end
         return ${field};
     }
 
-$torqueGen.mergepoint("dbObjectFieldGetters")
-}
+    $torqueGen.mergepoint("mappingStrategyPath")
+    $torqueGen.mergepoint("dbObjectFieldGetters")
+}
\ No newline at end of file
Index: 
torque-runtime/src/main/java/org/apache/torque/util/ResultsetSpliterator.java
<+>ISO-8859-1
===================================================================
diff --git 
a/torque-runtime/src/main/java/org/apache/torque/util/ResultsetSpliterator.java 
b/torque-runtime/src/main/java/org/apache/torque/util/ResultsetSpliterator.java
--- 
a/torque-runtime/src/main/java/org/apache/torque/util/ResultsetSpliterator.java 
    (revision 1928982)
+++ 
b/torque-runtime/src/main/java/org/apache/torque/util/ResultsetSpliterator.java 
    (date 1758653092330)
@@ -31,6 +31,7 @@
 import org.apache.torque.TorqueException;
 import org.apache.torque.TorqueRuntimeException;
 import org.apache.torque.criteria.Criteria;
+import org.apache.torque.om.mapper.MappingStrategy;
 import org.apache.torque.om.mapper.RecordMapper;
 
 /**
@@ -41,6 +42,7 @@
 public class ResultsetSpliterator<T> extends AbstractSpliterator<T> implements 
Runnable
 {
     private final RecordMapper<T> recordMapper;
+    private final MappingStrategy<T> mappingStrategy;
     private final Criteria criteria;
     private final Statement statement;
     private final ResultSet resultSet;
@@ -50,16 +52,17 @@
     private long rowNumber;
 
     /**
-     * Constructor
+     * Constructor with an explicit MappingStrategy.
      *
      * @param recordMapper a RecordMapper to map ResultSet rows to entities of 
type T
      * @param criteria     a Criteria
      * @param statement    the statement that created the ResultSet
      * @param resultSet    the JDBC result set
+     * @param mappingStrategy     a MappingStrategy to use
      * @throws TorqueException backend database exception
      */
     public ResultsetSpliterator(RecordMapper<T> recordMapper, Criteria 
criteria,
-            Statement statement, ResultSet resultSet) throws TorqueException
+            Statement statement, ResultSet resultSet, MappingStrategy<T> 
mappingStrategy) throws TorqueException
     {
         super(Long.MAX_VALUE, Spliterator.ORDERED);
 
@@ -67,6 +70,8 @@
         this.criteria = criteria;
         this.statement = statement;
         this.resultSet = resultSet;
+        MappingStrategy<T> myMappingStrategy = mappingStrategy;
+
         this.offset = 0; //database takes care of offset
         this.limit = -1; //database takes care of limit
         this.rowNumber = 0;
@@ -91,7 +96,28 @@
                     limit = offset + criteria.getLimit();
                 }
             }
+
+            // generate a strategy, if not already provided
+            if (mappingStrategy == null) {
+                myMappingStrategy = recordMapper.generateStrategy(offset, 
criteria);
+            }
         }
+        this.mappingStrategy = myMappingStrategy;
+    }
+
+    /**
+     * Constructor
+     *
+     * @param recordMapper a RecordMapper to map ResultSet rows to entities of 
type T
+     * @param criteria     a Criteria
+     * @param statement    the statement that created the ResultSet
+     * @param resultSet    the JDBC result set
+     * @throws TorqueException backend database exception
+     */
+    public ResultsetSpliterator(RecordMapper<T> recordMapper, Criteria 
criteria,
+                                Statement statement, ResultSet resultSet) 
throws TorqueException
+    {
+        this(recordMapper, criteria, statement, resultSet, null);
     }
 
     /* (non-Javadoc)
@@ -117,7 +143,7 @@
                 }
 
                 rowNumber++;
-                T result = recordMapper.processRow(resultSet, 0, criteria);
+                T result = recordMapper.processRow(resultSet, 0, criteria, 
mappingStrategy);
                 action.accept(result);
                 return true;
             }
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to