=== modified file 'src/com/goldencode/p2j/persist/SortCriterion.java'
--- old/src/com/goldencode/p2j/persist/SortCriterion.java	2022-07-27 21:57:13 +0000
+++ new/src/com/goldencode/p2j/persist/SortCriterion.java	2022-12-14 09:19:25 +0000
@@ -97,6 +97,7 @@
 **     OM  20210704          Added specialized copy constructor.
 **     AL2 20220706          Added isCompatible to filter out the sort clauses not belonging to the buffer.
 **     OM  20220727          FieldId and PropertyId are different for denormalized extent fields.
+**     RAA 20221213          Added a cache for storing the result of toSortExpression() method.
 */
 
 /*
@@ -225,6 +226,9 @@
    /** Lock for cache */
    private static final Object cacheLock = new Object();
    
+   /** Cache for storing the result of {@code toSortExpression} method. */
+   private final Vector<String> toStringCache; 
+   
    /** <code>true</code> if sort is ascending, else it is descending */
    private final boolean ascending;
    
@@ -253,13 +257,13 @@
    private final String originalName;
    
    /** Sort property name, usually qualified */
-   private String name;
+   private final String name;
    
    /** Subscript value, if any */
-   private int subscript = -1;
+   private final int subscript;
    
    /** SQL dialect which used this sort criterion; used for rtrim calls on character properties */
-   private Dialect dialect = null;
+   private final Dialect dialect;
    
    /**
     * Constructor which parses original sort component text, deriving the property name, sort
@@ -347,10 +351,11 @@
       // parse the test and extract alias, property and optional extent
       int spacePos = text.indexOf(' ');
       String rawName = ((spacePos != -1) ? text.substring(0, spacePos) : text).trim();
-      name = rawName;
+      String actualName = rawName;
       originalName = internStrings ? rawName.intern() : rawName;
       int bracketPos = rawName.indexOf("[");
       int endPos = -1;
+      int actualSubscript = -1;
       if (bracketPos >= 0)
       {
          endPos = rawName.indexOf("]");
@@ -360,17 +365,17 @@
          }
          
          // extract base name
-         name = rawName.substring(0, bracketPos);
+         actualName = rawName.substring(0, bracketPos);
       }
       
-      int dotPos = name.lastIndexOf(".");
+      int dotPos = actualName.lastIndexOf(".");
       if (dotPos < 0)
       {
          throw new PersistenceException("Sort property may not be unqualified:  " + rawName);
       }
       
-      String propName = name.substring(dotPos + 1);
-      String alias = name.substring(0, dotPos);
+      String propName = actualName.substring(dotPos + 1);
+      String alias = actualName.substring(0, dotPos);
       DmoMeta dmoMeta = DmoMetadataManager.getDmoInfo(dmoClass);
       Property propMeta = dmoMeta.getFieldInfo(propName);
       this.propertyName = propMeta.name; // already interned, anyway
@@ -385,20 +390,20 @@
             String sub = rawName.substring(bracketPos + 1, endPos);
             try
             {
-               subscript = Integer.parseInt(sub);
+               actualSubscript = Integer.parseInt(sub);
             }
             catch (NumberFormatException exc)
             {
                throw new PersistenceException("Unsupported subscript:  " + sub);
             }
             
-            name = alias + "." + propertyName;
+            actualName = alias + "." + propertyName;
          }
       }
       
       if (internStrings)
       {
-         name = name.intern();
+         actualName = actualName.intern();
       }
       
       // determine sort direction
@@ -448,6 +453,11 @@
          ignoreCase = isCharacter && !caseSens;
          computedColumnPrefix = ccPrefix;
       }
+      
+      name = actualName;
+      subscript = actualSubscript;
+      toStringCache = new Vector<>(16);
+      toStringCache.setSize(16);
    }
    
    /**
@@ -475,6 +485,8 @@
       this.originalName = other.originalName;
       this.name = alias + "." + propertyName + 
                   (subscript < 0 ? "" : "[" + subscript + "]") + (ascending ? " asc" : " desc");
+      toStringCache = new Vector<>(16);
+      toStringCache.setSize(16);
    }
    
    /**
@@ -930,6 +942,19 @@
       // ASC:   ASC + NULLS LAST    (CASE WHEN [Order] IS NULL THEN 1 ELSE 0 END), [Order] ASC  
       // DESC:  DESC + NULLS FIRST  (CASE WHEN [Order] IS NULL THEN 0 ELSE 1 END), [Order] DESC
       
+      int cacheKey = 0;
+      cacheKey += (invert ? 1 << 3 : 0);
+      cacheKey += (useTableAlias ? 1 << 2 : 0);
+      cacheKey += (uniqueAlias ? 1 << 1 : 0);
+      cacheKey += (doSpecificProcessing ? 1 : 0);
+      String cacheResult = null;
+      cacheResult = toStringCache.get(cacheKey);
+      
+      if (cacheResult != null)
+      {
+         return cacheResult;
+      }
+      
       boolean mssqlFix = dialect instanceof P2JSQLServer2008Dialect;
       StringBuilder buf = new StringBuilder();
       StringBuilder sortExpr = mssqlFix ? new StringBuilder() : null;
@@ -983,7 +1008,9 @@
          buf.append(ascending ? "asc" : "desc");
       }
       
-      return buf.toString();
+      String result = buf.toString();
+      toStringCache.setElementAt(result, cacheKey);
+      return result;
    }
    
    /**

