You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by jl...@apache.org on 2012/12/15 12:20:15 UTC

svn commit: r1422221 - in /ofbiz/trunk/framework/entity: dtd/ src/org/ofbiz/entity/config/ src/org/ofbiz/entity/datasource/ src/org/ofbiz/entity/test/ src/org/ofbiz/entity/util/

Author: jleroux
Date: Sat Dec 15 11:20:13 2012
New Revision: 1422221

URL: http://svn.apache.org/viewvc?rev=1422221&view=rev
Log:
A patch from Shi Jinghai for "Support MySQL and Postgres's LIMIT and OFFSET options" https://issues.apache.org/jira/browse/OFBIZ-4346

Change the existing code to support the different syntax variations, and add an attribute to the datasource element in the entityengine.xml file so that the proper variation can be chosen for each database.
Inspired by Moqui code. Test successful

Modified:
    ofbiz/trunk/framework/entity/dtd/entity-config.xsd
    ofbiz/trunk/framework/entity/src/org/ofbiz/entity/config/DatasourceInfo.java
    ofbiz/trunk/framework/entity/src/org/ofbiz/entity/datasource/GenericDAO.java
    ofbiz/trunk/framework/entity/src/org/ofbiz/entity/test/EntityTestSuite.java
    ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityFindOptions.java

Modified: ofbiz/trunk/framework/entity/dtd/entity-config.xsd
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/dtd/entity-config.xsd?rev=1422221&r1=1422220&r2=1422221&view=diff
==============================================================================
--- ofbiz/trunk/framework/entity/dtd/entity-config.xsd (original)
+++ ofbiz/trunk/framework/entity/dtd/entity-config.xsd Sat Dec 15 11:20:13 2012
@@ -399,6 +399,15 @@ under the License.
                 </xs:restriction>
             </xs:simpleType>
         </xs:attribute>
+        <xs:attribute name="offset-style" default="none">
+            <xs:simpleType>
+                <xs:restriction base="xs:token">
+                    <xs:enumeration value="none"/>
+                    <xs:enumeration value="fetch"/>
+                    <xs:enumeration value="limit"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
         <xs:attribute type="xs:string" name="table-type"/>
         <xs:attribute type="xs:string" name="character-set"/>
         <xs:attribute type="xs:string" name="collate"/>

Modified: ofbiz/trunk/framework/entity/src/org/ofbiz/entity/config/DatasourceInfo.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/config/DatasourceInfo.java?rev=1422221&r1=1422220&r2=1422221&view=diff
==============================================================================
--- ofbiz/trunk/framework/entity/src/org/ofbiz/entity/config/DatasourceInfo.java (original)
+++ ofbiz/trunk/framework/entity/src/org/ofbiz/entity/config/DatasourceInfo.java Sat Dec 15 11:20:13 2012
@@ -72,6 +72,7 @@ public class DatasourceInfo extends Name
     public boolean dropFkUseForeignKeyKeyword = false;
     public boolean useBinaryTypeForBlob = false;
     public boolean useOrderByNulls = false;
+    public String offsetStyle = null;
     public String tableType = null;
     public String characterSet = null;
     public String collate = null;
@@ -158,7 +159,8 @@ public class DatasourceInfo extends Name
             this.dropFkUseForeignKeyKeyword = "true".equals(datasourceElement.getAttribute("drop-fk-use-foreign-key-keyword"));
             this.useBinaryTypeForBlob = "true".equals(datasourceElement.getAttribute("use-binary-type-for-blob"));
             this.useOrderByNulls = "true".equals(datasourceElement.getAttribute("use-order-by-nulls"));
-
+            
+            this.offsetStyle = datasourceElement.getAttribute("offset-style");
             this.tableType = datasourceElement.getAttribute("table-type");
             this.characterSet = datasourceElement.getAttribute("character-set");
             this.collate = datasourceElement.getAttribute("collate");

Modified: ofbiz/trunk/framework/entity/src/org/ofbiz/entity/datasource/GenericDAO.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/datasource/GenericDAO.java?rev=1422221&r1=1422220&r2=1422221&view=diff
==============================================================================
--- ofbiz/trunk/framework/entity/src/org/ofbiz/entity/datasource/GenericDAO.java (original)
+++ ofbiz/trunk/framework/entity/src/org/ofbiz/entity/datasource/GenericDAO.java Sat Dec 15 11:20:13 2012
@@ -763,6 +763,9 @@ public class GenericDAO {
         }
         sqlBuffer.append(SqlJdbcUtil.makeOrderByClause(modelEntity, orderByExpanded, datasourceInfo));
 
+        // OFFSET clause
+        makeOffsetString(sqlBuffer, findOptions);
+        
         // make the final SQL String
         String sql = sqlBuffer.toString();
 
@@ -884,6 +887,29 @@ public class GenericDAO {
         return havingString;
     }
 
+    protected StringBuilder makeOffsetString(StringBuilder offsetString, EntityFindOptions findOptions) {
+        if (UtilValidate.isNotEmpty(datasourceInfo.offsetStyle)) {
+            if (datasourceInfo.offsetStyle.equals("limit")) {
+                // use the LIMIT/OFFSET style
+                if (findOptions.getLimit() > -1) {
+                    offsetString.append(" LIMIT " + findOptions.getLimit());
+                    if (findOptions.getOffset() > -1) {
+                        offsetString.append(" OFFSET " + findOptions.getOffset());
+                    }
+                }
+            } else if (datasourceInfo.offsetStyle.equals("fetch")) {
+                // use SQL2008 OFFSET/FETCH style by default
+                if (findOptions.getOffset() > -1) {
+                    offsetString.append(" OFFSET ").append(findOptions.getOffset()).append(" ROWS");
+                    if (findOptions.getLimit() > -1) {
+                        offsetString.append(" FETCH FIRST ").append(findOptions.getLimit()).append(" ROWS ONLY");
+                    }
+                }
+            }
+        }
+        return offsetString;
+    }
+
     public List<GenericValue> selectByMultiRelation(GenericValue value, ModelRelation modelRelationOne, ModelEntity modelEntityOne,
         ModelRelation modelRelationTwo, ModelEntity modelEntityTwo, List<String> orderBy) throws GenericEntityException {
         SQLProcessor sqlP = new SQLProcessor(helperInfo);

Modified: ofbiz/trunk/framework/entity/src/org/ofbiz/entity/test/EntityTestSuite.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/test/EntityTestSuite.java?rev=1422221&r1=1422220&r2=1422221&view=diff
==============================================================================
--- ofbiz/trunk/framework/entity/src/org/ofbiz/entity/test/EntityTestSuite.java (original)
+++ ofbiz/trunk/framework/entity/src/org/ofbiz/entity/test/EntityTestSuite.java Sat Dec 15 11:20:13 2012
@@ -31,6 +31,7 @@ import java.util.Map;
 import org.ofbiz.base.util.Debug;
 import org.ofbiz.base.util.UtilDateTime;
 import org.ofbiz.base.util.UtilMisc;
+import org.ofbiz.base.util.UtilValidate;
 import org.ofbiz.base.util.UtilXml;
 import org.ofbiz.entity.Delegator;
 import org.ofbiz.entity.DelegatorFactory;
@@ -44,6 +45,8 @@ import org.ofbiz.entity.condition.Entity
 import org.ofbiz.entity.condition.EntityOperator;
 import org.ofbiz.entity.config.DatasourceInfo;
 import org.ofbiz.entity.config.EntityConfigUtil;
+import org.ofbiz.entity.datasource.GenericHelperDAO;
+import org.ofbiz.entity.model.ModelEntity;
 import org.ofbiz.entity.testtools.EntityTestCase;
 import org.ofbiz.entity.transaction.GenericTransactionException;
 import org.ofbiz.entity.transaction.TransactionUtil;
@@ -604,4 +607,94 @@ public class EntityTestSuite extends Ent
         strBufTemp.append(iNum);
         return strBufTemp.toString();
     }
+    
+    /*
+     * This test will verify that the LIMIT and OFFSET options can work properly.
+     */
+    public void testLimitOffsetOptions() throws Exception {
+        String entityName = "Content";
+        DatasourceInfo datasourceInfo = EntityConfigUtil.getDatasourceInfo(delegator.getEntityHelper(entityName).getHelperName());
+        if (UtilValidate.isEmpty(datasourceInfo.offsetStyle) || datasourceInfo.offsetStyle.equals("none")) {
+            Debug.logInfo("The offset-stype configured in datasource is " + datasourceInfo.offsetStyle +  ", this test is skipped.", module);
+            return;
+        } else {
+            Debug.logInfo("The offset-stype configured in datasource is " + datasourceInfo.offsetStyle +  ".", module);
+        }
+        try {
+            EntityFindOptions findOptions = new EntityFindOptions();
+            long count = delegator.findCountByCondition("Content", null, null, null);
+            Debug.logInfo("Content entity has " + count + " rows", module);
+            int rowsPerPage = 10;
+            // use rows/page as limit option
+            findOptions.setLimit(rowsPerPage);
+            int pages = (int) count/rowsPerPage;
+            if (count > pages * rowsPerPage) {
+                pages += 1;
+            }
+            Debug.logInfo("These rows will be displayed in " + pages + " pages, each page has " + rowsPerPage + " rows.", module);
+            ModelEntity modelEntity = delegator.getModelEntity(entityName);
+
+            long start = UtilDateTime.nowTimestamp().getTime();
+            for (int page = 1; page <= pages; page++) {
+                Debug.logInfo("Page " + page + ":", module);
+                // set offset option
+                findOptions.setOffset((page - 1) * rowsPerPage);
+                EntityListIterator iterator = null;
+                try {
+                    iterator = delegator.getEntityHelper(entityName).findListIteratorByCondition(modelEntity, null, null, null, UtilMisc.toList("lastUpdatedStamp DESC"), findOptions);
+                    while (iterator != null) {
+                        GenericValue gv = iterator.next();
+                        if (gv == null) {
+                            break;
+                        }
+                        Debug.logInfo(gv.getString("contentId") + ": " + gv.getString("contentName") + "       (updated: " + gv.getTimestamp("lastUpdatedStamp") + ")", module);
+                    }
+                } catch (GenericEntityException e) {
+                    Debug.logError(e, module);
+                } finally {
+                    if (iterator != null) {
+                        iterator.close();
+                    }
+                }
+            }
+            long end = UtilDateTime.nowTimestamp().getTime();
+            long time1 = end - start;
+            Debug.logInfo("Time consumed WITH limit and offset option (ms): " + time1, module);
+            
+            start = UtilDateTime.nowTimestamp().getTime();
+            for (int page = 1; page <= pages; page++) {
+                Debug.logInfo("Page " + page + ":", module);
+                EntityListIterator iterator = null;
+                try {
+                    iterator = ((GenericHelperDAO) delegator.getEntityHelper(entityName)).findListIteratorByCondition(modelEntity, null, null, null, UtilMisc.toList("lastUpdatedStamp DESC"), null);
+                    if (iterator == null) {
+                        continue;
+                    }
+                    iterator.setDelegator(delegator);
+                    List<GenericValue> gvs = iterator.getCompleteList();
+                    int fromIndex = (page - 1) * rowsPerPage;
+                    int toIndex = fromIndex + rowsPerPage;
+                    if (toIndex > count) {
+                        toIndex = (int) count;
+                    }
+                    gvs = gvs.subList(fromIndex, toIndex);
+                    for (GenericValue gv : gvs) {
+                        Debug.logInfo(gv.getString("contentId") + ": " + gv.getString("contentName") + "       (updated: " + gv.getTimestamp("lastUpdatedStamp") + ")", module);
+                    }
+                } catch (GenericEntityException e) {
+                    Debug.logError(e, module);
+                } finally {
+                    if (iterator != null) {
+                        iterator.close();
+                    }
+                }
+            }
+            end = UtilDateTime.nowTimestamp().getTime();
+            long time2 = end - start;
+            Debug.logInfo("Time consumed WITHOUT limit and offset option (ms): " + time2, module);
+            Debug.logInfo("Time saved (ms): " + (time2 - time1), module);
+        } catch (GenericEntityException e) {
+            Debug.logError(e, module);
+        }
+    }
 }

Modified: ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityFindOptions.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityFindOptions.java?rev=1422221&r1=1422220&r2=1422221&view=diff
==============================================================================
--- ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityFindOptions.java (original)
+++ ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityFindOptions.java Sat Dec 15 11:20:13 2012
@@ -48,6 +48,12 @@ public class EntityFindOptions implement
     protected int maxRows = -1;
     protected boolean distinct = false;
 
+    /** LIMIT option */
+    protected int limit = -1;
+    
+    /** OFFSET option */
+    protected int offset = -1;
+
     /** Default constructor. Defaults are as follows:
      *      specifyTypeAndConcur = true
      *      resultSetType = TYPE_FORWARD_ONLY
@@ -145,4 +151,25 @@ public class EntityFindOptions implement
     public void setDistinct(boolean distinct) {
         this.distinct = distinct;
     }
+
+
+    /** Get the LIMIT number. */
+    public int getLimit() {
+        return limit;
+    }
+
+    /** Specifies the LIMIT number. */
+    public void setLimit(int limit) {
+        this.limit = limit;
+    }
+
+    /** Get the OFFSET number. */
+    public int getOffset() {
+        return offset;
+    }
+
+    /** Specifies the OFFSET number. */
+    public void setOffset(int offset) {
+        this.offset = offset;
+    }
 }



Re: svn commit: r1422221 - in /ofbiz/trunk/framework/entity: dtd/ src/org/ofbiz/entity/config/ src/org/ofbiz/entity/datasource/ src/org/ofbiz/entity/test/ src/org/ofbiz/entity/util/

Posted by Adrian Crum <ad...@sandglass-software.com>.
This commit makes the framework dependent on the Content component.

-Adrian

On 12/15/2012 11:20 AM, jleroux@apache.org wrote:
> Author: jleroux
> Date: Sat Dec 15 11:20:13 2012
> New Revision: 1422221
>
> URL: http://svn.apache.org/viewvc?rev=1422221&view=rev
> Log:
> A patch from Shi Jinghai for "Support MySQL and Postgres's LIMIT and OFFSET options" https://issues.apache.org/jira/browse/OFBIZ-4346
>
> Change the existing code to support the different syntax variations, and add an attribute to the datasource element in the entityengine.xml file so that the proper variation can be chosen for each database.
> Inspired by Moqui code. Test successful
>
> Modified:
>      ofbiz/trunk/framework/entity/dtd/entity-config.xsd
>      ofbiz/trunk/framework/entity/src/org/ofbiz/entity/config/DatasourceInfo.java
>      ofbiz/trunk/framework/entity/src/org/ofbiz/entity/datasource/GenericDAO.java
>      ofbiz/trunk/framework/entity/src/org/ofbiz/entity/test/EntityTestSuite.java
>      ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityFindOptions.java
>
> Modified: ofbiz/trunk/framework/entity/dtd/entity-config.xsd
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/dtd/entity-config.xsd?rev=1422221&r1=1422220&r2=1422221&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/entity/dtd/entity-config.xsd (original)
> +++ ofbiz/trunk/framework/entity/dtd/entity-config.xsd Sat Dec 15 11:20:13 2012
> @@ -399,6 +399,15 @@ under the License.
>                   </xs:restriction>
>               </xs:simpleType>
>           </xs:attribute>
> +        <xs:attribute name="offset-style" default="none">
> +            <xs:simpleType>
> +                <xs:restriction base="xs:token">
> +                    <xs:enumeration value="none"/>
> +                    <xs:enumeration value="fetch"/>
> +                    <xs:enumeration value="limit"/>
> +                </xs:restriction>
> +            </xs:simpleType>
> +        </xs:attribute>
>           <xs:attribute type="xs:string" name="table-type"/>
>           <xs:attribute type="xs:string" name="character-set"/>
>           <xs:attribute type="xs:string" name="collate"/>
>
> Modified: ofbiz/trunk/framework/entity/src/org/ofbiz/entity/config/DatasourceInfo.java
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/config/DatasourceInfo.java?rev=1422221&r1=1422220&r2=1422221&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/entity/src/org/ofbiz/entity/config/DatasourceInfo.java (original)
> +++ ofbiz/trunk/framework/entity/src/org/ofbiz/entity/config/DatasourceInfo.java Sat Dec 15 11:20:13 2012
> @@ -72,6 +72,7 @@ public class DatasourceInfo extends Name
>       public boolean dropFkUseForeignKeyKeyword = false;
>       public boolean useBinaryTypeForBlob = false;
>       public boolean useOrderByNulls = false;
> +    public String offsetStyle = null;
>       public String tableType = null;
>       public String characterSet = null;
>       public String collate = null;
> @@ -158,7 +159,8 @@ public class DatasourceInfo extends Name
>               this.dropFkUseForeignKeyKeyword = "true".equals(datasourceElement.getAttribute("drop-fk-use-foreign-key-keyword"));
>               this.useBinaryTypeForBlob = "true".equals(datasourceElement.getAttribute("use-binary-type-for-blob"));
>               this.useOrderByNulls = "true".equals(datasourceElement.getAttribute("use-order-by-nulls"));
> -
> +
> +            this.offsetStyle = datasourceElement.getAttribute("offset-style");
>               this.tableType = datasourceElement.getAttribute("table-type");
>               this.characterSet = datasourceElement.getAttribute("character-set");
>               this.collate = datasourceElement.getAttribute("collate");
>
> Modified: ofbiz/trunk/framework/entity/src/org/ofbiz/entity/datasource/GenericDAO.java
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/datasource/GenericDAO.java?rev=1422221&r1=1422220&r2=1422221&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/entity/src/org/ofbiz/entity/datasource/GenericDAO.java (original)
> +++ ofbiz/trunk/framework/entity/src/org/ofbiz/entity/datasource/GenericDAO.java Sat Dec 15 11:20:13 2012
> @@ -763,6 +763,9 @@ public class GenericDAO {
>           }
>           sqlBuffer.append(SqlJdbcUtil.makeOrderByClause(modelEntity, orderByExpanded, datasourceInfo));
>   
> +        // OFFSET clause
> +        makeOffsetString(sqlBuffer, findOptions);
> +
>           // make the final SQL String
>           String sql = sqlBuffer.toString();
>   
> @@ -884,6 +887,29 @@ public class GenericDAO {
>           return havingString;
>       }
>   
> +    protected StringBuilder makeOffsetString(StringBuilder offsetString, EntityFindOptions findOptions) {
> +        if (UtilValidate.isNotEmpty(datasourceInfo.offsetStyle)) {
> +            if (datasourceInfo.offsetStyle.equals("limit")) {
> +                // use the LIMIT/OFFSET style
> +                if (findOptions.getLimit() > -1) {
> +                    offsetString.append(" LIMIT " + findOptions.getLimit());
> +                    if (findOptions.getOffset() > -1) {
> +                        offsetString.append(" OFFSET " + findOptions.getOffset());
> +                    }
> +                }
> +            } else if (datasourceInfo.offsetStyle.equals("fetch")) {
> +                // use SQL2008 OFFSET/FETCH style by default
> +                if (findOptions.getOffset() > -1) {
> +                    offsetString.append(" OFFSET ").append(findOptions.getOffset()).append(" ROWS");
> +                    if (findOptions.getLimit() > -1) {
> +                        offsetString.append(" FETCH FIRST ").append(findOptions.getLimit()).append(" ROWS ONLY");
> +                    }
> +                }
> +            }
> +        }
> +        return offsetString;
> +    }
> +
>       public List<GenericValue> selectByMultiRelation(GenericValue value, ModelRelation modelRelationOne, ModelEntity modelEntityOne,
>           ModelRelation modelRelationTwo, ModelEntity modelEntityTwo, List<String> orderBy) throws GenericEntityException {
>           SQLProcessor sqlP = new SQLProcessor(helperInfo);
>
> Modified: ofbiz/trunk/framework/entity/src/org/ofbiz/entity/test/EntityTestSuite.java
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/test/EntityTestSuite.java?rev=1422221&r1=1422220&r2=1422221&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/entity/src/org/ofbiz/entity/test/EntityTestSuite.java (original)
> +++ ofbiz/trunk/framework/entity/src/org/ofbiz/entity/test/EntityTestSuite.java Sat Dec 15 11:20:13 2012
> @@ -31,6 +31,7 @@ import java.util.Map;
>   import org.ofbiz.base.util.Debug;
>   import org.ofbiz.base.util.UtilDateTime;
>   import org.ofbiz.base.util.UtilMisc;
> +import org.ofbiz.base.util.UtilValidate;
>   import org.ofbiz.base.util.UtilXml;
>   import org.ofbiz.entity.Delegator;
>   import org.ofbiz.entity.DelegatorFactory;
> @@ -44,6 +45,8 @@ import org.ofbiz.entity.condition.Entity
>   import org.ofbiz.entity.condition.EntityOperator;
>   import org.ofbiz.entity.config.DatasourceInfo;
>   import org.ofbiz.entity.config.EntityConfigUtil;
> +import org.ofbiz.entity.datasource.GenericHelperDAO;
> +import org.ofbiz.entity.model.ModelEntity;
>   import org.ofbiz.entity.testtools.EntityTestCase;
>   import org.ofbiz.entity.transaction.GenericTransactionException;
>   import org.ofbiz.entity.transaction.TransactionUtil;
> @@ -604,4 +607,94 @@ public class EntityTestSuite extends Ent
>           strBufTemp.append(iNum);
>           return strBufTemp.toString();
>       }
> +
> +    /*
> +     * This test will verify that the LIMIT and OFFSET options can work properly.
> +     */
> +    public void testLimitOffsetOptions() throws Exception {
> +        String entityName = "Content";
> +        DatasourceInfo datasourceInfo = EntityConfigUtil.getDatasourceInfo(delegator.getEntityHelper(entityName).getHelperName());
> +        if (UtilValidate.isEmpty(datasourceInfo.offsetStyle) || datasourceInfo.offsetStyle.equals("none")) {
> +            Debug.logInfo("The offset-stype configured in datasource is " + datasourceInfo.offsetStyle +  ", this test is skipped.", module);
> +            return;
> +        } else {
> +            Debug.logInfo("The offset-stype configured in datasource is " + datasourceInfo.offsetStyle +  ".", module);
> +        }
> +        try {
> +            EntityFindOptions findOptions = new EntityFindOptions();
> +            long count = delegator.findCountByCondition("Content", null, null, null);
> +            Debug.logInfo("Content entity has " + count + " rows", module);
> +            int rowsPerPage = 10;
> +            // use rows/page as limit option
> +            findOptions.setLimit(rowsPerPage);
> +            int pages = (int) count/rowsPerPage;
> +            if (count > pages * rowsPerPage) {
> +                pages += 1;
> +            }
> +            Debug.logInfo("These rows will be displayed in " + pages + " pages, each page has " + rowsPerPage + " rows.", module);
> +            ModelEntity modelEntity = delegator.getModelEntity(entityName);
> +
> +            long start = UtilDateTime.nowTimestamp().getTime();
> +            for (int page = 1; page <= pages; page++) {
> +                Debug.logInfo("Page " + page + ":", module);
> +                // set offset option
> +                findOptions.setOffset((page - 1) * rowsPerPage);
> +                EntityListIterator iterator = null;
> +                try {
> +                    iterator = delegator.getEntityHelper(entityName).findListIteratorByCondition(modelEntity, null, null, null, UtilMisc.toList("lastUpdatedStamp DESC"), findOptions);
> +                    while (iterator != null) {
> +                        GenericValue gv = iterator.next();
> +                        if (gv == null) {
> +                            break;
> +                        }
> +                        Debug.logInfo(gv.getString("contentId") + ": " + gv.getString("contentName") + "       (updated: " + gv.getTimestamp("lastUpdatedStamp") + ")", module);
> +                    }
> +                } catch (GenericEntityException e) {
> +                    Debug.logError(e, module);
> +                } finally {
> +                    if (iterator != null) {
> +                        iterator.close();
> +                    }
> +                }
> +            }
> +            long end = UtilDateTime.nowTimestamp().getTime();
> +            long time1 = end - start;
> +            Debug.logInfo("Time consumed WITH limit and offset option (ms): " + time1, module);
> +
> +            start = UtilDateTime.nowTimestamp().getTime();
> +            for (int page = 1; page <= pages; page++) {
> +                Debug.logInfo("Page " + page + ":", module);
> +                EntityListIterator iterator = null;
> +                try {
> +                    iterator = ((GenericHelperDAO) delegator.getEntityHelper(entityName)).findListIteratorByCondition(modelEntity, null, null, null, UtilMisc.toList("lastUpdatedStamp DESC"), null);
> +                    if (iterator == null) {
> +                        continue;
> +                    }
> +                    iterator.setDelegator(delegator);
> +                    List<GenericValue> gvs = iterator.getCompleteList();
> +                    int fromIndex = (page - 1) * rowsPerPage;
> +                    int toIndex = fromIndex + rowsPerPage;
> +                    if (toIndex > count) {
> +                        toIndex = (int) count;
> +                    }
> +                    gvs = gvs.subList(fromIndex, toIndex);
> +                    for (GenericValue gv : gvs) {
> +                        Debug.logInfo(gv.getString("contentId") + ": " + gv.getString("contentName") + "       (updated: " + gv.getTimestamp("lastUpdatedStamp") + ")", module);
> +                    }
> +                } catch (GenericEntityException e) {
> +                    Debug.logError(e, module);
> +                } finally {
> +                    if (iterator != null) {
> +                        iterator.close();
> +                    }
> +                }
> +            }
> +            end = UtilDateTime.nowTimestamp().getTime();
> +            long time2 = end - start;
> +            Debug.logInfo("Time consumed WITHOUT limit and offset option (ms): " + time2, module);
> +            Debug.logInfo("Time saved (ms): " + (time2 - time1), module);
> +        } catch (GenericEntityException e) {
> +            Debug.logError(e, module);
> +        }
> +    }
>   }
>
> Modified: ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityFindOptions.java
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityFindOptions.java?rev=1422221&r1=1422220&r2=1422221&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityFindOptions.java (original)
> +++ ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityFindOptions.java Sat Dec 15 11:20:13 2012
> @@ -48,6 +48,12 @@ public class EntityFindOptions implement
>       protected int maxRows = -1;
>       protected boolean distinct = false;
>   
> +    /** LIMIT option */
> +    protected int limit = -1;
> +
> +    /** OFFSET option */
> +    protected int offset = -1;
> +
>       /** Default constructor. Defaults are as follows:
>        *      specifyTypeAndConcur = true
>        *      resultSetType = TYPE_FORWARD_ONLY
> @@ -145,4 +151,25 @@ public class EntityFindOptions implement
>       public void setDistinct(boolean distinct) {
>           this.distinct = distinct;
>       }
> +
> +
> +    /** Get the LIMIT number. */
> +    public int getLimit() {
> +        return limit;
> +    }
> +
> +    /** Specifies the LIMIT number. */
> +    public void setLimit(int limit) {
> +        this.limit = limit;
> +    }
> +
> +    /** Get the OFFSET number. */
> +    public int getOffset() {
> +        return offset;
> +    }
> +
> +    /** Specifies the OFFSET number. */
> +    public void setOffset(int offset) {
> +        this.offset = offset;
> +    }
>   }
>
>