You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by sa...@apache.org on 2011/08/03 18:13:26 UTC

svn commit: r1153560 [11/15] - in /ofbiz/branches/jackrabbit20100709: ./ applications/accounting/config/ applications/accounting/data/ applications/accounting/entitydef/ applications/accounting/script/org/ofbiz/accounting/invoice/ applications/accounti...

Modified: ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/jdbc/SqlJdbcUtil.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/jdbc/SqlJdbcUtil.java?rev=1153560&r1=1153559&r2=1153560&view=diff
==============================================================================
--- ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/jdbc/SqlJdbcUtil.java (original)
+++ ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/jdbc/SqlJdbcUtil.java Wed Aug  3 16:12:58 2011
@@ -52,6 +52,7 @@ import org.ofbiz.entity.GenericEntityExc
 import org.ofbiz.entity.GenericModelException;
 import org.ofbiz.entity.GenericNotImplementedException;
 import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entity.condition.EntityCondition;
 import org.ofbiz.entity.condition.EntityConditionParam;
 import org.ofbiz.entity.condition.OrderByList;
 import org.ofbiz.entity.config.DatasourceInfo;
@@ -73,7 +74,7 @@ public class SqlJdbcUtil {
     public static final int CHAR_BUFFER_SIZE = 4096;
 
     /** Makes the FROM clause and when necessary the JOIN clause(s) as well */
-    public static String makeFromClause(ModelEntity modelEntity, DatasourceInfo datasourceInfo) throws GenericEntityException {
+    public static String makeFromClause(ModelEntity modelEntity, ModelFieldTypeReader modelFieldTypeReader, DatasourceInfo datasourceInfo) throws GenericEntityException {
         StringBuilder sql = new StringBuilder(" FROM ");
 
         if (modelEntity instanceof ModelViewEntity) {
@@ -119,7 +120,7 @@ public class SqlJdbcUtil {
 
                     if (i == 0) {
                         // this is the first referenced member alias, so keep track of it for future use...
-                        restOfStatement.append(makeViewTable(linkEntity, datasourceInfo));
+                        restOfStatement.append(makeViewTable(linkEntity, modelFieldTypeReader, datasourceInfo));
                         //another possible one that some dbs might need, but not sure of any yet: restOfStatement.append(" AS ");
                         restOfStatement.append(" ");
                         restOfStatement.append(viewLink.getEntityAlias());
@@ -140,7 +141,7 @@ public class SqlJdbcUtil {
                         restOfStatement.append(" INNER JOIN ");
                     }
 
-                    restOfStatement.append(makeViewTable(relLinkEntity, datasourceInfo));
+                    restOfStatement.append(makeViewTable(relLinkEntity, modelFieldTypeReader, datasourceInfo));
                     //another possible one that some dbs might need, but not sure of any yet: restOfStatement.append(" AS ");
                     restOfStatement.append(" ");
                     restOfStatement.append(viewLink.getRelEntityAlias());
@@ -165,20 +166,26 @@ public class SqlJdbcUtil {
 
                         condBuffer.append(viewLink.getEntityAlias());
                         condBuffer.append(".");
-                        condBuffer.append(filterColName(linkField.getColName()));
+                        condBuffer.append(linkField.getColName());
 
                         condBuffer.append(" = ");
 
                         condBuffer.append(viewLink.getRelEntityAlias());
                         condBuffer.append(".");
-                        condBuffer.append(filterColName(relLinkField.getColName()));
+                        condBuffer.append(relLinkField.getColName());
                     }
                     if (condBuffer.length() == 0) {
                         throw new GenericModelException("No view-link/join key-maps found for the " + viewLink.getEntityAlias() + " and the " + viewLink.getRelEntityAlias() + " member-entities of the " + modelViewEntity.getEntityName() + " view-entity.");
                     }
 
-                    // TODO add expression from entity-condition on view-link
-
+                    ModelViewEntity.ViewEntityCondition viewEntityCondition = viewLink.getViewEntityCondition();
+                    if (viewEntityCondition != null) {
+                        EntityCondition whereCondition = viewEntityCondition.getWhereCondition(modelFieldTypeReader, null);
+                        if (whereCondition != null) {
+                            condBuffer.append(" AND ");
+                            condBuffer.append(whereCondition.makeWhereString(modelEntity, null, datasourceInfo));
+                        }
+                    }
 
                     restOfStatement.append(condBuffer.toString());
 
@@ -198,7 +205,7 @@ public class SqlJdbcUtil {
                         if (!fromEmpty) sql.append(", ");
                         fromEmpty = false;
 
-                        sql.append(makeViewTable(fromEntity, datasourceInfo));
+                        sql.append(makeViewTable(fromEntity, modelFieldTypeReader, datasourceInfo));
                         sql.append(" ");
                         sql.append(aliasName);
                     }
@@ -213,7 +220,7 @@ public class SqlJdbcUtil {
                     String aliasName = meIter.next();
                     ModelEntity fromEntity = modelViewEntity.getMemberModelEntity(aliasName);
 
-                    sql.append(makeViewTable(fromEntity, datasourceInfo));
+                    sql.append(makeViewTable(fromEntity, modelFieldTypeReader, datasourceInfo));
                     sql.append(" ");
                     sql.append(aliasName);
                     if (meIter.hasNext()) sql.append(", ");
@@ -258,7 +265,7 @@ public class SqlJdbcUtil {
             ModelField modelField = null;
             if (item instanceof ModelField) {
                 modelField = (ModelField) item;
-                sb.append(modelField.getColName());
+                sb.append(modelField.getColValue());
                 name = modelField.getName();
             } else {
                 sb.append(item);
@@ -347,7 +354,7 @@ public class SqlJdbcUtil {
                         }
                         whereString.append(viewLink.getEntityAlias());
                         whereString.append(".");
-                        whereString.append(filterColName(linkField.getColName()));
+                        whereString.append(linkField.getColName());
 
                         // check to see whether the left or right members are optional, if so:
                         // oracle: use the (+) on the optional side
@@ -362,7 +369,7 @@ public class SqlJdbcUtil {
 
                         whereString.append(viewLink.getRelEntityAlias());
                         whereString.append(".");
-                        whereString.append(filterColName(relLinkField.getColName()));
+                        whereString.append(relLinkField.getColName());
                    }
                 }
             } else {
@@ -394,7 +401,7 @@ public class SqlJdbcUtil {
         return sql.toString();
     }
 
-    public static String makeViewTable(ModelEntity modelEntity, DatasourceInfo datasourceInfo) throws GenericEntityException {
+    public static String makeViewTable(ModelEntity modelEntity, ModelFieldTypeReader modelFieldTypeReader, DatasourceInfo datasourceInfo) throws GenericEntityException {
         if (modelEntity instanceof ModelViewEntity) {
             StringBuilder sql = new StringBuilder("(SELECT ");
             Iterator<ModelField> fieldsIter = modelEntity.getFieldsIterator();
@@ -402,16 +409,16 @@ public class SqlJdbcUtil {
                 ModelField curField = fieldsIter.next();
                 sql.append(curField.getColValue());
                 sql.append(" AS ");
-                sql.append(filterColName(curField.getColName()));
+                sql.append(curField.getColName());
                 while (fieldsIter.hasNext()) {
                     curField = fieldsIter.next();
                     sql.append(", ");
                     sql.append(curField.getColValue());
                     sql.append(" AS ");
-                    sql.append(filterColName(curField.getColName()));
+                    sql.append(curField.getColName());
                 }
             }
-            sql.append(makeFromClause(modelEntity, datasourceInfo));
+            sql.append(makeFromClause(modelEntity, modelFieldTypeReader, datasourceInfo));
             String viewWhereClause = makeViewWhereClause(modelEntity, datasourceInfo.joinStyle);
             if (UtilValidate.isNotEmpty(viewWhereClause)) {
                 sql.append(" WHERE ");

Modified: ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/model/DynamicViewEntity.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/model/DynamicViewEntity.java?rev=1153560&r1=1153559&r2=1153560&view=diff
==============================================================================
--- ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/model/DynamicViewEntity.java (original)
+++ ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/model/DynamicViewEntity.java Wed Aug  3 16:12:58 2011
@@ -20,6 +20,7 @@ package org.ofbiz.entity.model;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -228,8 +229,16 @@ public class DynamicViewEntity {
         return this.memberModelMemberEntities.entrySet().iterator();
     }
 
+    /**
+     * @deprecated use {@link #addAliasAll(String, String, Collection<String>)}
+     */
+    @Deprecated
     public void addAliasAll(String entityAlias, String prefix) {
-        ModelAliasAll aliasAll = new ModelAliasAll(entityAlias, prefix);
+        addAliasAll(entityAlias, prefix, null);
+    }
+
+    public void addAliasAll(String entityAlias, String prefix, Collection<String> excludes) {
+        ModelAliasAll aliasAll = new ModelAliasAll(entityAlias, prefix, false, null, null, excludes);
         this.aliasAlls.add(aliasAll);
     }
 
@@ -243,10 +252,14 @@ public class DynamicViewEntity {
 
     /** Add an alias, full detail. All parameters can be null except entityAlias and name. */
     public void addAlias(String entityAlias, String name, String field, String colAlias, Boolean primKey, Boolean groupBy, String function) {
-        addAlias(entityAlias, name, field, colAlias, primKey, groupBy, function, null);
+        addAlias(entityAlias, name, field, colAlias, primKey, groupBy, function, null, null);
     }
 
     public void addAlias(String entityAlias, String name, String field, String colAlias, Boolean primKey, Boolean groupBy, String function, ComplexAliasMember complexAliasMember) {
+        addAlias(entityAlias, name, field, colAlias, primKey, groupBy, function, null, complexAliasMember);
+    }
+
+    public void addAlias(String entityAlias, String name, String field, String colAlias, Boolean primKey, Boolean groupBy, String function, String fieldSet, ComplexAliasMember complexAliasMember) {
         if (entityAlias == null && complexAliasMember == null) {
             throw new IllegalArgumentException("entityAlias cannot be null if this is not a complex alias in call to DynamicViewEntity.addAlias");
         }
@@ -254,7 +267,7 @@ public class DynamicViewEntity {
             throw new IllegalArgumentException("name cannot be null in call to DynamicViewEntity.addAlias");
         }
 
-        ModelAlias alias = new ModelAlias(entityAlias, name, field, colAlias, primKey, groupBy, function);
+        ModelAlias alias = new ModelAlias(entityAlias, name, field, colAlias, primKey, groupBy, function, fieldSet);
         if (complexAliasMember != null) {
             alias.setComplexAliasMember(complexAliasMember);
         }
@@ -266,7 +279,7 @@ public class DynamicViewEntity {
     }
 
     public void addViewLink(String entityAlias, String relEntityAlias, Boolean relOptional, List<ModelKeyMap> modelKeyMaps) {
-        ModelViewLink modelViewLink = new ModelViewLink(entityAlias, relEntityAlias, relOptional, modelKeyMaps);
+        ModelViewLink modelViewLink = new ModelViewLink(entityAlias, relEntityAlias, relOptional, null, modelKeyMaps);
         this.viewLinks.add(modelViewLink);
     }
 

Modified: ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/model/ModelField.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/model/ModelField.java?rev=1153560&r1=1153559&r2=1153560&view=diff
==============================================================================
--- ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/model/ModelField.java (original)
+++ ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/model/ModelField.java Wed Aug  3 16:12:58 2011
@@ -55,6 +55,9 @@ public class ModelField extends ModelChi
     protected boolean isAutoCreatedInternal = false;
     protected boolean enableAuditLog = false;
 
+    /** when any field in the same set is selected in a query, all fields in that set will be selected */
+    protected String fieldSet = "";
+
     /** validators to be called when an update is done */
     protected List<String> validators = new ArrayList<String>();
 
@@ -85,6 +88,7 @@ public class ModelField extends ModelChi
         this.description = UtilXml.childElementValue(fieldElement, "description");
         this.enableAuditLog = UtilXml.checkBoolean(fieldElement.getAttribute("enable-audit-log"), false);
         this.isNotNull = UtilXml.checkBoolean(fieldElement.getAttribute("not-null"), false);
+        this.fieldSet = UtilXml.checkEmpty(fieldElement.getAttribute("field-set")).intern();
 
         NodeList validateList = fieldElement.getElementsByTagName("validate");
 
@@ -184,6 +188,14 @@ public class ModelField extends ModelChi
         this.isAutoCreatedInternal = isAutoCreatedInternal;
     }
 
+    public String getFieldSet() {
+        return fieldSet;
+    }
+
+    public void setFieldSet(String fieldSet) {
+        this.fieldSet = fieldSet;
+    }
+
     /** validators to be called when an update is done */
     public String getValidator(int index) {
         return this.validators.get(index);

Modified: ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/model/ModelViewEntity.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/model/ModelViewEntity.java?rev=1153560&r1=1153559&r2=1153560&view=diff
==============================================================================
--- ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/model/ModelViewEntity.java (original)
+++ ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/model/ModelViewEntity.java Wed Aug  3 16:12:58 2011
@@ -21,6 +21,7 @@ package org.ofbiz.entity.model;
 import java.io.Serializable;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -92,7 +93,7 @@ public class ModelViewEntity extends Mod
     /** List of field names to group by */
     protected List<String> groupByFields = FastList.newInstance();
 
-    protected Map<String, Map<String, ModelConversion>> conversions = FastMap.newInstance();
+    protected Map<String, ModelConversion[]> conversions = FastMap.newInstance();
 
     protected ViewEntityCondition viewEntityCondition = null;
 
@@ -107,7 +108,7 @@ public class ModelViewEntity extends Mod
             String alias = UtilXml.checkEmpty(memberEntityElement.getAttribute("entity-alias")).intern();
             String name = UtilXml.checkEmpty(memberEntityElement.getAttribute("entity-name")).intern();
             if (name.length() <= 0 || alias.length() <= 0) {
-                Debug.logError("[new ModelViewEntity] entity-alias or entity-name missing on member-entity element of the view-entity " + this.entityName, module);
+                Debug.logError("[new ModelViewEntity]: entity-alias or entity-name missing on member-entity element of the view-entity " + this.entityName, module);
             } else {
                 ModelMemberEntity modelMemberEntity = new ModelMemberEntity(alias, name);
                 this.addMemberModelMemberEntity(modelMemberEntity);
@@ -377,14 +378,14 @@ public class ModelViewEntity extends Mod
     public ModelEntity getAliasedEntity(String entityAlias, ModelReader modelReader) {
         ModelMemberEntity modelMemberEntity = this.memberModelMemberEntities.get(entityAlias);
         if (modelMemberEntity == null) {
-            Debug.logError("No member entity with alias " + entityAlias + " found in view-entity " + this.getEntityName() + "; this view-entity will NOT be usable...", module);
+            Debug.logError("[" + this.getEntityName() + "]: No member entity with alias " + entityAlias + " found; this view-entity will NOT be usable...", module);
             return null;
         }
 
         String aliasedEntityName = modelMemberEntity.getEntityName();
         ModelEntity aliasedEntity = modelReader.getModelEntityNoCheck(aliasedEntityName);
         if (aliasedEntity == null) {
-            Debug.logError("[ModelViewEntity.populateFields] ERROR: could not find ModelEntity for entity name: " + aliasedEntityName, module);
+            Debug.logError("[" + this.getEntityName() + "]: [ModelViewEntity.populateFields] ERROR: could not find ModelEntity for entity name: " + aliasedEntityName, module);
             return null;
         }
 
@@ -394,7 +395,7 @@ public class ModelViewEntity extends Mod
     public ModelField getAliasedField(ModelEntity aliasedEntity, String field, ModelReader modelReader) {
         ModelField aliasedField = aliasedEntity.getField(field);
         if (aliasedField == null) {
-            Debug.logError("[ModelViewEntity.populateFields] ERROR: could not find ModelField for entity name: " + aliasedEntity.getEntityName() + " and field: " + field, module);
+            Debug.logError("[" + this.getEntityName() + "]: [ModelViewEntity.populateFields] ERROR: could not find ModelField for entity name: " + aliasedEntity.getEntityName() + " and field: " + field, module);
             return null;
         }
         return aliasedField;
@@ -446,7 +447,7 @@ public class ModelViewEntity extends Mod
 
             // show a warning if function is specified and groupBy is true
             if (UtilValidate.isNotEmpty(alias.function) && alias.groupBy) {
-                Debug.logWarning("The view-entity alias with name=" + alias.name + " has a function value and is specified as a group-by field; this may be an error, but is not necessarily.", module);
+                Debug.logWarning("[" + this.getEntityName() + "]: The view-entity alias with name=" + alias.name + " has a function value and is specified as a group-by field; this may be an error, but is not necessarily.", module);
             }
 
             if (alias.isComplexAlias()) {
@@ -458,11 +459,12 @@ public class ModelViewEntity extends Mod
                 field.colName = ModelUtil.javaNameToDbName(alias.name);
                 field.type = fieldTypeBuffer.toString();
                 field.isPk = false;
+                field.fieldSet = alias.getFieldSet();
             } else {
                 ModelEntity aliasedEntity = getAliasedEntity(alias.entityAlias, modelReader);
                 ModelField aliasedField = getAliasedField(aliasedEntity, alias.field, modelReader);
                 if (aliasedField == null) {
-                    Debug.logError("[ModelViewEntity.populateFields (" + this.getEntityName() + ")] ERROR: could not find ModelField for field name \"" +
+                    Debug.logError("[" + this.getEntityName() + "]: [populateFields] ERROR: could not find ModelField for field name \"" +
                         alias.field + "\" on entity with name: " + aliasedEntity.getEntityName(), module);
                     continue;
                 }
@@ -478,10 +480,26 @@ public class ModelViewEntity extends Mod
                 field.type = aliasedField.type;
                 field.validators = aliasedField.validators;
 
-                field.colName = alias.entityAlias + "." + SqlJdbcUtil.filterColName(aliasedField.colName);
+                field.colValue = alias.entityAlias + "." + SqlJdbcUtil.filterColName(aliasedField.colName);
+                field.colName = SqlJdbcUtil.filterColName(field.colValue);
                 if (UtilValidate.isEmpty(field.description)) {
                     field.description = aliasedField.description;
                 }
+                if (UtilValidate.isEmpty(alias.getFieldSet())) {
+                    String aliasedFieldSet = aliasedField.getFieldSet();
+                    if (UtilValidate.isNotEmpty(aliasedFieldSet)) {
+                        StringBuilder fieldSetBuffer = new StringBuilder(alias.entityAlias);
+                        fieldSetBuffer.append("_");
+                        fieldSetBuffer.append(Character.toUpperCase(aliasedFieldSet.charAt(0)));
+                        fieldSetBuffer.append(aliasedFieldSet.substring(1));
+                        field.fieldSet = fieldSetBuffer.toString().intern();
+                        Debug.logInfo("[" + this.getEntityName() + "]: copied field set on [" + field.name + "]: " + field.fieldSet, module);
+                    } else {
+                        field.fieldSet = "";
+                    }
+                } else {
+                    field.fieldSet = alias.getFieldSet();
+                }
             }
 
             this.fields.add(field);
@@ -499,7 +517,7 @@ public class ModelViewEntity extends Mod
             if (UtilValidate.isNotEmpty(alias.function)) {
                 String prefix = functionPrefixMap.get(alias.function);
                 if (prefix == null) {
-                    Debug.logWarning("Specified alias function [" + alias.function + "] not valid; must be: min, max, sum, avg, count or count-distinct; using a column name with no function function", module);
+                    Debug.logWarning("[" + this.getEntityName() + "]: Specified alias function [" + alias.function + "] not valid; must be: min, max, sum, avg, count or count-distinct; using a column name with no function function", module);
                 } else {
                     field.colValue = prefix + field.getColValue() + ")";
                 }
@@ -511,20 +529,26 @@ public class ModelViewEntity extends Mod
         ModelEntity member = getMemberModelEntity(aliasName);
         if (member == null) {
             String errMsg = "No member found for aliasName - " + aliasName;
-            Debug.logWarning(errMsg, module);
-            throw new RuntimeException("Cannot create View Entity: " + errMsg);
+            Debug.logWarning("[" + this.getEntityName() + "]: " + errMsg, module);
+            throw new RuntimeException("[" + this.getEntityName() + "]: Cannot create View Entity: " + errMsg);
         }
 
-        Map<String, ModelConversion> aliasConversions = conversions.get(member.getEntityName());
-        if (aliasConversions == null) {
-            aliasConversions = FastMap.newInstance();
-            conversions.put(member.getEntityName(), aliasConversions);
-        }
-        ModelConversion conversion = aliasConversions.get(aliasName);
-        if (conversion == null) {
-            conversion = new ModelConversion(aliasName, member);
-            aliasConversions.put(aliasName, conversion);
-        }
+        ModelConversion[] allConversions = conversions.get(member.getEntityName());
+        if (allConversions == null) {
+            ModelConversion conversion = new ModelConversion(aliasName, member);
+            conversions.put(member.getEntityName(), new ModelConversion[] {conversion});
+            return conversion;
+        }
+        for (ModelConversion conversion: allConversions) {
+            if (conversion.aliasName.equals(aliasName)) {
+                return conversion;
+            }
+        }
+        ModelConversion[] newConversions = new ModelConversion[allConversions.length + 1];
+        System.arraycopy(allConversions, 0, newConversions, 0, allConversions.length);
+        ModelConversion conversion = new ModelConversion(aliasName, member);
+        newConversions[allConversions.length] = conversion;
+        conversions.put(member.getEntityName(), newConversions);
         return conversion;
     }
 
@@ -535,7 +559,7 @@ public class ModelViewEntity extends Mod
             ModelViewEntity.ModelAlias alias = it.next();
             if (alias.isComplexAlias()) {
                 // TODO: conversion for complex-alias needs to be implemented for cache and in-memory eval stuff to work correctly
-                Debug.logWarning("Conversion for complex-alias needs to be implemented for cache and in-memory eval stuff to work correctly, will not work for alias: " + alias.getName() + " of view-entity " + this.getEntityName(), module);
+                Debug.logWarning("[" + this.getEntityName() + "]: Conversion for complex-alias needs to be implemented for cache and in-memory eval stuff to work correctly, will not work for alias: " + alias.getName(), module);
             } else {
                 ModelConversion conversion = getOrCreateModelConversion(alias.getEntityAlias());
                 conversion.addConversion(alias.getField(), alias.getName());
@@ -571,15 +595,11 @@ public class ModelViewEntity extends Mod
         int[] maxIndex = new int[conversions.size()];
         ModelConversion[][] allConversions = new ModelConversion[conversions.size()][];
         int i = 0;
-        for (Map<String, ModelConversion> aliasConversions: conversions.values()) {
+        for (ModelConversion[] aliasConversions: conversions.values()) {
             currentIndex[i] = 0;
-            maxIndex[i] = aliasConversions.size();
-            allConversions[i] = new ModelConversion[aliasConversions.size()];
-            int j = 0;
-            for (ModelConversion conversion: aliasConversions.values()) {
-                allConversions[i][j] = conversion;
-                j++;
-            }
+            maxIndex[i] = aliasConversions.length;
+            allConversions[i] = new ModelConversion[aliasConversions.length];
+            System.arraycopy(aliasConversions, 0, allConversions[i], 0, aliasConversions.length);
             i++;
         }
         int ptr = 0;
@@ -605,10 +625,10 @@ public class ModelViewEntity extends Mod
     }
 
     public List<Map<String, Object>> convert(String fromEntityName, Map<String, ? extends Object> data) {
-        Map<String, ModelConversion> conversions = this.conversions.get(fromEntityName);
+        ModelConversion[] conversions = this.conversions.get(fromEntityName);
         if (conversions == null) return null;
         List<Map<String, Object>> values = FastList.newInstance();
-        for (ModelConversion conversion: conversions.values()) {
+        for (ModelConversion conversion: conversions) {
             conversion.convert(values, data);
         }
         return values;
@@ -619,26 +639,28 @@ public class ModelViewEntity extends Mod
      */
     private void expandAllAliasAlls(ModelReader modelReader) {
         for (ModelAliasAll aliasAll: aliasAlls) {
+            String entityAlias = aliasAll.getEntityAlias();
             String prefix = aliasAll.getPrefix();
             String function = aliasAll.getFunction();
             boolean groupBy = aliasAll.getGroupBy();
+            String aliasAllFieldSet = aliasAll.getFieldSet();
 
-            ModelMemberEntity modelMemberEntity = memberModelMemberEntities.get(aliasAll.getEntityAlias());
+            ModelMemberEntity modelMemberEntity = memberModelMemberEntities.get(entityAlias);
             if (modelMemberEntity == null) {
-                Debug.logError("Member entity referred to in alias-all not found, ignoring: " + aliasAll.getEntityAlias(), module);
+                Debug.logError("[" + this.getEntityName() + "]: Member entity referred to in alias-all not found, ignoring: " + entityAlias, module);
                 continue;
             }
 
             String aliasedEntityName = modelMemberEntity.getEntityName();
             ModelEntity aliasedEntity = modelReader.getModelEntityNoCheck(aliasedEntityName);
             if (aliasedEntity == null) {
-                Debug.logError("Entity referred to in member-entity " + aliasAll.getEntityAlias() + " not found, ignoring: " + aliasedEntityName, module);
+                Debug.logError("[" + this.getEntityName() + "]: Entity referred to in member-entity " + entityAlias + " not found, ignoring: " + aliasedEntityName, module);
                 continue;
             }
 
             List<String> entFieldList = aliasedEntity.getAllFieldNames();
             if (entFieldList == null) {
-                Debug.logError("Entity referred to in member-entity " + aliasAll.getEntityAlias() + " has no fields, ignoring: " + aliasedEntityName, module);
+                Debug.logError("[" + this.getEntityName() + "]: Entity referred to in member-entity " + entityAlias + " has no fields, ignoring: " + aliasedEntityName, module);
                 continue;
             }
 
@@ -662,6 +684,27 @@ public class ModelViewEntity extends Mod
                     newAliasBuffer.append(aliasName.substring(1));
                     aliasName = newAliasBuffer.toString();
                 }
+                String fieldSet;
+                if (UtilValidate.isEmpty(aliasAllFieldSet)) {
+                    String aliasedFieldSet = modelField.getFieldSet();
+                    if (UtilValidate.isNotEmpty(aliasedFieldSet)) {
+                        StringBuilder fieldSetBuffer = new StringBuilder(entityAlias);
+                        if (UtilValidate.isNotEmpty(prefix)) {
+                            fieldSetBuffer.append(Character.toUpperCase(prefix.charAt(0)));
+                            fieldSetBuffer.append(prefix.substring(1));
+                        }
+                        fieldSetBuffer.append(Character.toUpperCase(aliasedFieldSet.charAt(0)));
+                        fieldSetBuffer.append(aliasedFieldSet.substring(1));
+                        fieldSet = fieldSetBuffer.toString();
+                    } else {
+                        fieldSet = "";
+                    }
+                } else {
+                    fieldSet = aliasAllFieldSet;
+                }
+                if (UtilValidate.isNotEmpty(fieldSet)) {
+                    Debug.logInfo("[" + this.getEntityName() + "]: set field-set on [" + aliasName + "]: " + fieldSet, module);
+                }
 
                 ModelAlias existingAlias = this.getAlias(aliasName);
                 if (existingAlias != null) {
@@ -689,7 +732,7 @@ public class ModelViewEntity extends Mod
                     }
 
                     //already exists, oh well... probably an override, but log just in case
-                    String warnMsg = "Throwing out field alias in view entity " + this.getEntityName() + " because one already exists with the alias name [" + aliasName + "] and field name [" + modelMemberEntity.getEntityAlias() + "(" + aliasedEntity.getEntityName() + ")." + fieldName + "], existing field name is [" + existingAlias.getEntityAlias() + "." + existingAlias.getField() + "]";
+                    String warnMsg = "[" + this.getEntityName() + "]: Throwing out field alias in view entity because one already exists with the alias name [" + aliasName + "] and field name [" + modelMemberEntity.getEntityAlias() + "(" + aliasedEntity.getEntityName() + ")." + fieldName + "], existing field name is [" + existingAlias.getEntityAlias() + "." + existingAlias.getField() + "]";
                     if (isInViewLink) {
                         Debug.logVerbose(warnMsg, module);
                     } else {
@@ -698,15 +741,8 @@ public class ModelViewEntity extends Mod
                     continue;
                 }
 
-                ModelAlias expandedAlias = new ModelAlias();
-                expandedAlias.name = aliasName;
-                expandedAlias.field = fieldName;
-                expandedAlias.entityAlias = aliasAll.getEntityAlias();
-                expandedAlias.isFromAliasAll = true;
-                expandedAlias.colAlias = ModelUtil.javaNameToDbName(UtilXml.checkEmpty(expandedAlias.name));
-                expandedAlias.function = function;
-                expandedAlias.groupBy = groupBy;
-                expandedAlias.description = modelField.getDescription();
+                ModelAlias expandedAlias = new ModelAlias(aliasAll.getEntityAlias(), aliasName, fieldName, ModelUtil.javaNameToDbName(UtilXml.checkEmpty(aliasName)), null, groupBy, function, fieldSet, true);
+                expandedAlias.setDescription(modelField.getDescription());
 
                 aliases.add(expandedAlias);
             }
@@ -718,9 +754,9 @@ public class ModelViewEntity extends Mod
         return "ModelViewEntity[" + getEntityName() + "]";
     }
 
-    public static class ModelMemberEntity implements Serializable {
-        protected String entityAlias = "";
-        protected String entityName = "";
+    public static final class ModelMemberEntity implements Serializable {
+        protected final String entityAlias;
+        protected final String entityName;
 
         public ModelMemberEntity(String entityAlias, String entityName) {
             this.entityAlias = entityAlias;
@@ -736,19 +772,37 @@ public class ModelViewEntity extends Mod
         }
     }
 
-    public static class ModelAliasAll implements Serializable, Iterable<String> {
-        protected String entityAlias = "";
-        protected String prefix = "";
-        protected Set<String> fieldsToExclude = null;
-        protected boolean groupBy = false;
+    public static final class ModelAliasAll implements Serializable, Iterable<String> {
+        protected final String entityAlias;
+        protected final String prefix;
+        protected final Set<String> fieldsToExclude;
+        protected final boolean groupBy;
         // is specified this alias is a calculated value; can be: min, max, sum, avg, count, count-distinct
-        protected String function = null;
-
-        protected ModelAliasAll() {}
+        protected final String function;
+        protected final String fieldSet;
 
+        @Deprecated
         public ModelAliasAll(String entityAlias, String prefix) {
+            this(entityAlias, prefix, false, null, null, null);
+        }
+
+        @Deprecated
+        public ModelAliasAll(String entityAlias, String prefix, boolean groupBy, String function, Collection<String> excludes) {
+            this(entityAlias, prefix, groupBy, function, null, excludes);
+        }
+
+        public ModelAliasAll(String entityAlias, String prefix, boolean groupBy, String function, String fieldSet, Collection<String> excludes) {
             this.entityAlias = entityAlias;
             this.prefix = prefix;
+            this.groupBy = groupBy;
+            this.function = function;
+            this.fieldSet = fieldSet;
+            if (UtilValidate.isNotEmpty(excludes)) {
+                this.fieldsToExclude = new HashSet<String>(excludes.size());
+                this.fieldsToExclude.addAll(excludes);
+            } else {
+                this.fieldsToExclude = null;
+            }
         }
 
         public ModelAliasAll(Element aliasAllElement) {
@@ -756,6 +810,7 @@ public class ModelViewEntity extends Mod
             this.prefix = UtilXml.checkEmpty(aliasAllElement.getAttribute("prefix")).intern();
             this.groupBy = "true".equals(UtilXml.checkEmpty(aliasAllElement.getAttribute("group-by")));
             this.function = UtilXml.checkEmpty(aliasAllElement.getAttribute("function"));
+            this.fieldSet = UtilXml.checkEmpty(aliasAllElement.getAttribute("field-set")).intern();
 
             List<? extends Element> excludes = UtilXml.childElementList(aliasAllElement, "exclude");
             if (UtilValidate.isNotEmpty(excludes)) {
@@ -763,6 +818,8 @@ public class ModelViewEntity extends Mod
                 for (Element excludeElement: excludes) {
                     this.fieldsToExclude.add(excludeElement.getAttribute("field").intern());
                 }
+            } else {
+                this.fieldsToExclude = null;
             }
 
         }
@@ -783,6 +840,10 @@ public class ModelViewEntity extends Mod
             return this.function;
         }
 
+        public String getFieldSet() {
+            return this.fieldSet;
+        }
+
         public boolean shouldExclude(String fieldName) {
             if (this.fieldsToExclude == null) {
                 return false;
@@ -792,27 +853,30 @@ public class ModelViewEntity extends Mod
         }
 
         public Iterator<String> iterator() {
-            return fieldsToExclude.iterator();
+            if (this.fieldsToExclude == null) {
+                return Collections.<String>emptySet().iterator();
+            } else {
+                return fieldsToExclude.iterator();
+            }
         }
     }
 
-    public static class ModelAlias implements Serializable {
-        protected String entityAlias = "";
-        protected String name = "";
-        protected String field = "";
-        protected String colAlias = "";
+    public static final class ModelAlias implements Serializable {
+        protected final String entityAlias;
+        protected final String name;
+        protected final String field;
+        protected final String colAlias;
         // this is a Boolean object for a tri-state: null, true or false
-        protected Boolean isPk = null;
-        protected boolean groupBy = false;
+        protected final Boolean isPk;
+        protected final boolean groupBy;
         // is specified this alias is a calculated value; can be: min, max, sum, avg, count, count-distinct
-        protected String function = null;
-        protected boolean isFromAliasAll = false;
-        protected ComplexAliasMember complexAliasMember = null;
+        protected final String function;
+        protected final String fieldSet;
+        protected final boolean isFromAliasAll;
+        protected ComplexAliasMember complexAliasMember;
         // The description for documentation purposes
         protected String description = "";
 
-        protected ModelAlias() {}
-
         public ModelAlias(Element aliasElement) {
             this.entityAlias = UtilXml.checkEmpty(aliasElement.getAttribute("entity-alias")).intern();
             this.name = UtilXml.checkEmpty(aliasElement.getAttribute("name")).intern();
@@ -827,6 +891,8 @@ public class ModelViewEntity extends Mod
             }
             this.groupBy = "true".equals(UtilXml.checkEmpty(aliasElement.getAttribute("group-by")));
             this.function = UtilXml.checkEmpty(aliasElement.getAttribute("function")).intern();
+            this.fieldSet = UtilXml.checkEmpty(aliasElement.getAttribute("field-set")).intern();
+            this.isFromAliasAll = false;
             this.description = UtilXml.checkEmpty(UtilXml.childElementValue(aliasElement, "description")).intern();
 
             Element complexAliasElement = UtilXml.firstChildElement(aliasElement, "complex-alias");
@@ -835,7 +901,16 @@ public class ModelViewEntity extends Mod
             }
         }
 
+        @Deprecated
         public ModelAlias(String entityAlias, String name, String field, String colAlias, Boolean isPk, Boolean groupBy, String function) {
+            this(entityAlias, name, field, colAlias, isPk, groupBy, function, null, false);
+        }
+
+        public ModelAlias(String entityAlias, String name, String field, String colAlias, Boolean isPk, Boolean groupBy, String function, String fieldSet) {
+            this(entityAlias, name, field, colAlias, isPk, groupBy, function, fieldSet, false);
+        }
+
+        protected ModelAlias(String entityAlias, String name, String field, String colAlias, Boolean isPk, Boolean groupBy, String function, String fieldSet, boolean isFromAliasAll) {
             this.entityAlias = entityAlias;
             this.name = name;
             this.field = UtilXml.checkEmpty(field, this.name);
@@ -847,6 +922,8 @@ public class ModelViewEntity extends Mod
                 this.groupBy = false;
             }
             this.function = function;
+            this.fieldSet = UtilXml.checkEmpty(fieldSet).intern();
+            this.isFromAliasAll = isFromAliasAll;
         }
 
         public void setComplexAliasMember(ComplexAliasMember complexAliasMember) {
@@ -891,6 +968,10 @@ public class ModelViewEntity extends Mod
             return this.function;
         }
 
+        public String getFieldSet() {
+            return fieldSet;
+        }
+
         public String getDescription() {
             return this.description;
         }
@@ -908,9 +989,9 @@ public class ModelViewEntity extends Mod
         public void makeAliasColName(StringBuilder colNameBuffer, StringBuilder fieldTypeBuffer, ModelViewEntity modelViewEntity, ModelReader modelReader);
     }
 
-    public static class ComplexAlias implements ComplexAliasMember {
-        protected List<ComplexAliasMember> complexAliasMembers = FastList.newInstance();
-        protected String operator;
+    public static final class ComplexAlias implements ComplexAliasMember {
+        protected final List<ComplexAliasMember> complexAliasMembers = FastList.newInstance();
+        protected final String operator;
 
         public ComplexAlias(String operator) {
             this.operator = operator;
@@ -960,11 +1041,11 @@ public class ModelViewEntity extends Mod
         }
     }
 
-    public static class ComplexAliasField implements ComplexAliasMember {
-        protected String entityAlias = "";
-        protected String field = "";
-        protected String defaultValue = null;
-        protected String function = null;
+    public static final class ComplexAliasField implements ComplexAliasMember {
+        protected final String entityAlias;
+        protected final String field;
+        protected final String defaultValue;
+        protected final String function;
 
         public ComplexAliasField(Element complexAliasFieldElement) {
             this.entityAlias = complexAliasFieldElement.getAttribute("entity-alias").intern();
@@ -996,7 +1077,7 @@ public class ModelViewEntity extends Mod
             if (UtilValidate.isNotEmpty(function)) {
                 String prefix = functionPrefixMap.get(function);
                 if (prefix == null) {
-                    Debug.logWarning("Specified alias function [" + function + "] not valid; must be: min, max, sum, avg, count or count-distinct; using a column name with no function function", module);
+                    Debug.logWarning("[" + modelViewEntity.getEntityName() + "]: Specified alias function [" + function + "] not valid; must be: min, max, sum, avg, count or count-distinct; using a column name with no function function", module);
                 } else {
                     colName = prefix + colName + ")";
                 }
@@ -1011,14 +1092,12 @@ public class ModelViewEntity extends Mod
         }
     }
 
-    public static class ModelViewLink implements Serializable, Iterable<ModelKeyMap> {
-        protected String entityAlias = "";
-        protected String relEntityAlias = "";
-        protected boolean relOptional = false;
-        protected List<ModelKeyMap> keyMaps = FastList.newInstance();
-        protected ViewEntityCondition viewEntityCondition = null;
-
-        protected ModelViewLink() {}
+    public static final class ModelViewLink implements Serializable, Iterable<ModelKeyMap> {
+        protected final String entityAlias;
+        protected final String relEntityAlias;
+        protected final boolean relOptional;
+        protected final List<ModelKeyMap> keyMaps = FastList.newInstance();
+        protected final ViewEntityCondition viewEntityCondition;
 
         public ModelViewLink(ModelViewEntity modelViewEntity, Element viewLinkElement) {
             this.entityAlias = UtilXml.checkEmpty(viewLinkElement.getAttribute("entity-alias")).intern();
@@ -1037,20 +1116,35 @@ public class ModelViewEntity extends Mod
             Element entityConditionElement = UtilXml.firstChildElement(viewLinkElement, "entity-condition");
             if (entityConditionElement != null) {
                 this.viewEntityCondition = new ViewEntityCondition(modelViewEntity, this, entityConditionElement);
+            } else {
+                this.viewEntityCondition = null;
             }
         }
 
+        @Deprecated
         public ModelViewLink(String entityAlias, String relEntityAlias, Boolean relOptional, ModelKeyMap... keyMaps) {
-            this(entityAlias, relEntityAlias, relOptional, Arrays.asList(keyMaps));
+            this(entityAlias, relEntityAlias, relOptional, null, Arrays.asList(keyMaps));
         }
 
+        @Deprecated
         public ModelViewLink(String entityAlias, String relEntityAlias, Boolean relOptional, List<ModelKeyMap> keyMaps) {
+            this(entityAlias, relEntityAlias, relOptional, null, keyMaps);
+        }
+
+        public ModelViewLink(String entityAlias, String relEntityAlias, Boolean relOptional, ViewEntityCondition viewEntityCondition, ModelKeyMap... keyMaps) {
+            this(entityAlias, relEntityAlias, relOptional, viewEntityCondition, Arrays.asList(keyMaps));
+        }
+
+        public ModelViewLink(String entityAlias, String relEntityAlias, Boolean relOptional, ViewEntityCondition viewEntityCondition, List<ModelKeyMap> keyMaps) {
             this.entityAlias = entityAlias;
             this.relEntityAlias = relEntityAlias;
             if (relOptional != null) {
                 this.relOptional = relOptional.booleanValue();
+            } else {
+                this.relOptional = false;
             }
             this.keyMaps.addAll(keyMaps);
+            this.viewEntityCondition = viewEntityCondition;
         }
 
         public String getEntityAlias() {
@@ -1086,13 +1180,17 @@ public class ModelViewEntity extends Mod
             newList.addAll(this.keyMaps);
             return newList;
         }
+
+        public ViewEntityCondition getViewEntityCondition() {
+            return this.viewEntityCondition;
+        }
     }
 
-    public class ModelConversion implements Serializable {
-        protected String aliasName;
-        protected ModelEntity fromModelEntity;
-        protected Map<String, String> fieldMap = FastMap.newInstance();
-        protected Set<String> wildcards = new HashSet<String>();
+    public final class ModelConversion implements Serializable {
+        protected final String aliasName;
+        protected final ModelEntity fromModelEntity;
+        protected final Map<String, String> fieldMap = FastMap.newInstance();
+        protected final Set<String> wildcards = new HashSet<String>();
 
         public ModelConversion(String aliasName, ModelEntity fromModelEntity) {
             this.aliasName = aliasName;
@@ -1151,15 +1249,16 @@ public class ModelViewEntity extends Mod
         }
     }
 
-    public static class ViewEntityCondition {
-        protected ModelViewEntity modelViewEntity;
-        protected ModelViewLink modelViewLink;
-        protected boolean filterByDate;
-        protected boolean distinct;
-        protected List<String> orderByList;
-        protected ViewCondition whereCondition;
-        protected ViewCondition havingCondition;
+    public static final class ViewEntityCondition {
+        protected final ModelViewEntity modelViewEntity;
+        protected final ModelViewLink modelViewLink;
+        protected final boolean filterByDate;
+        protected final boolean distinct;
+        protected final List<String> orderByList;
+        protected final ViewCondition whereCondition;
+        protected final ViewCondition havingCondition;
 
+        // FIXME: add programatic constructor
         public ViewEntityCondition(ModelViewEntity modelViewEntity, ModelViewLink modelViewLink, Element element) {
             this.modelViewEntity = modelViewEntity;
             this.modelViewLink = modelViewLink;
@@ -1172,6 +1271,8 @@ public class ModelViewEntity extends Mod
                 for (Element orderByElement: orderByElementList) {
                     orderByList.add(orderByElement.getAttribute("field-name"));
                 }
+            } else {
+                orderByList = null;
             }
 
             Element conditionExprElement = UtilXml.firstChildElement(element, "condition-expr");
@@ -1180,11 +1281,15 @@ public class ModelViewEntity extends Mod
                 this.whereCondition = new ViewConditionExpr(this, conditionExprElement);
             } else if (conditionListElement != null) {
                 this.whereCondition = new ViewConditionList(this, conditionListElement);
+            } else {
+                this.whereCondition = null;
             }
 
             Element havingConditionListElement = UtilXml.firstChildElement(element, "having-condition-list");
             if (havingConditionListElement != null) {
                 this.havingCondition = new ViewConditionList(this, havingConditionListElement);
+            } else {
+                this.havingCondition = null;
             }
         }
 
@@ -1213,46 +1318,50 @@ public class ModelViewEntity extends Mod
         public EntityCondition createCondition(ModelFieldTypeReader modelFieldTypeReader, List<String> entityAliasStack);
     }
 
-    public static class ViewConditionExpr implements ViewCondition {
-        protected ViewEntityCondition viewEntityCondition;
-        protected String entityAlias;
-        protected String fieldName;
-        protected String operator;
-        protected String relEntityAlias;
-        protected String relFieldName;
-        protected Object value;
-        protected boolean ignoreCase;
+    public static final class ViewConditionExpr implements ViewCondition {
+        protected final ViewEntityCondition viewEntityCondition;
+        protected final String entityAlias;
+        protected final String fieldName;
+        protected final EntityComparisonOperator<?, ?> operator;
+        protected final String relEntityAlias;
+        protected final String relFieldName;
+        protected final Object value;
+        protected final boolean ignoreCase;
 
+        // FIXME: add programatic constructor
         public ViewConditionExpr(ViewEntityCondition viewEntityCondition, Element conditionExprElement) {
             this.viewEntityCondition = viewEntityCondition;
-            this.entityAlias = conditionExprElement.getAttribute("entity-alias");
+            String entityAlias = conditionExprElement.getAttribute("entity-alias");
             this.fieldName = conditionExprElement.getAttribute("field-name");
 
-            this.operator = UtilFormatOut.checkEmpty(conditionExprElement.getAttribute("operator"), "equals");
-            this.relEntityAlias = conditionExprElement.getAttribute("rel-entity-alias");
+            String operator = UtilFormatOut.checkEmpty(conditionExprElement.getAttribute("operator"), "equals");
+            try {
+                this.operator = EntityOperator.lookupComparison(operator);
+            } catch (IllegalArgumentException e) {
+                throw new IllegalArgumentException("[" + this.viewEntityCondition.modelViewEntity.getEntityName() + "]: Could not find an entity operator for the name: " + this.operator);
+            }
+            String relEntityAlias = conditionExprElement.getAttribute("rel-entity-alias");
             this.relFieldName = conditionExprElement.getAttribute("rel-field-name");
             this.value = conditionExprElement.getAttribute("value");
             this.ignoreCase = "true".equals(conditionExprElement.getAttribute("ignore-case"));
 
             // if we are in a view-link, default to the entity-alias and rel-entity-alias there
             if (this.viewEntityCondition.modelViewLink != null) {
-                if (UtilValidate.isEmpty(this.entityAlias)) {
-                    this.entityAlias = this.viewEntityCondition.modelViewLink.getEntityAlias();
+                if (UtilValidate.isEmpty(entityAlias)) {
+                    entityAlias = this.viewEntityCondition.modelViewLink.getEntityAlias();
                 }
-                if (UtilValidate.isEmpty(this.relEntityAlias)) {
-                    this.relEntityAlias = this.viewEntityCondition.modelViewLink.getRelEntityAlias();
+                if (UtilValidate.isEmpty(relEntityAlias)) {
+                    relEntityAlias = this.viewEntityCondition.modelViewLink.getRelEntityAlias();
                 }
             }
+            this.entityAlias = entityAlias;
+            this.relEntityAlias = relEntityAlias;
         }
 
         public EntityCondition createCondition(ModelFieldTypeReader modelFieldTypeReader, List<String> entityAliasStack) {
-            EntityOperator<?,?,?> operator = EntityOperator.lookup(this.operator);
-            if (operator == null) {
-                throw new IllegalArgumentException("Could not find an entity operator for the name: " + this.operator);
-            }
-
+            Object value = this.value;
             // If IN or BETWEEN operator, see if value is a literal list and split it
-            if ((operator.equals(EntityOperator.IN) || operator.equals(EntityOperator.BETWEEN))
+            if ((this.operator == EntityOperator.IN || this.operator == EntityOperator.BETWEEN)
                     && value instanceof String) {
                 String delim = null;
                 if (((String)value).indexOf("|") >= 0) {
@@ -1265,20 +1374,21 @@ public class ModelViewEntity extends Mod
                 }
             }
 
-            if (this.viewEntityCondition.modelViewEntity.getField(fieldName) == null) {
-                throw new IllegalArgumentException("Error in Entity Find: could not find field [" + fieldName + "] in entity with name [" + this.viewEntityCondition.modelViewEntity.getEntityName() + "]");
+            EntityConditionValue lhs = EntityFieldValue.makeFieldValue(this.fieldName, this.entityAlias, entityAliasStack, this.viewEntityCondition.modelViewEntity);
+            ModelField lhsField = lhs.getModelField(this.viewEntityCondition.modelViewEntity);
+            if (lhsField == null) {
+                throw new IllegalArgumentException("[" + this.viewEntityCondition.modelViewEntity.getEntityName() + "]: Error in Entity Find: could not find field [" + fieldName + "]");
             }
 
             // don't convert the field to the desired type if this is an IN or BETWEEN operator and we have a Collection
-            if (!((operator.equals(EntityOperator.IN) || operator.equals(EntityOperator.BETWEEN))
+            if (!((this.operator == EntityOperator.IN || this.operator == EntityOperator.BETWEEN)
                     && value instanceof Collection<?>)) {
                 // now to a type conversion for the target fieldName
-                value = this.viewEntityCondition.modelViewEntity.convertFieldValue(this.viewEntityCondition.modelViewEntity.getField(fieldName), value, modelFieldTypeReader, FastMap.<String, Object>newInstance());
+                value = this.viewEntityCondition.modelViewEntity.convertFieldValue(lhsField, value, modelFieldTypeReader, FastMap.<String, Object>newInstance());
             }
 
-            if (Debug.verboseOn()) Debug.logVerbose("Got value for fieldName [" + fieldName + "]: " + value, module);
+            if (Debug.verboseOn()) Debug.logVerbose("[" + this.viewEntityCondition.modelViewEntity.getEntityName() + "]: Got value for fieldName [" + fieldName + "]: " + value, module);
 
-            EntityConditionValue lhs = EntityFieldValue.makeFieldValue(this.fieldName, this.entityAlias, entityAliasStack, this.viewEntityCondition.modelViewEntity);
             Object rhs = null;
             if (value != null) {
                 rhs = value;
@@ -1286,41 +1396,46 @@ public class ModelViewEntity extends Mod
                 rhs = EntityFieldValue.makeFieldValue(this.relFieldName, this.relEntityAlias, entityAliasStack, this.viewEntityCondition.modelViewEntity);
             }
 
-            if (operator.equals(EntityOperator.NOT_EQUAL) && value != null) {
+            if (this.operator == EntityOperator.NOT_EQUAL && value != null) {
                 // since some databases don't consider nulls in != comparisons, explicitly include them
                 // this makes more sense logically, but if anyone ever needs it to not behave this way we should add an "or-null" attribute that is true by default
                 if (ignoreCase) {
                     return EntityCondition.makeCondition(
-                            EntityCondition.makeCondition(EntityFunction.UPPER(lhs), UtilGenerics.<EntityComparisonOperator<?,?>>cast(operator), EntityFunction.UPPER(rhs)),
+                            EntityCondition.makeCondition(EntityFunction.UPPER(lhs), this.operator, EntityFunction.UPPER(rhs)),
                             EntityOperator.OR,
                             EntityCondition.makeCondition(lhs, EntityOperator.EQUALS, null));
                 } else {
                     return EntityCondition.makeCondition(
-                            EntityCondition.makeCondition(lhs, UtilGenerics.<EntityComparisonOperator<?,?>>cast(operator), rhs),
+                            EntityCondition.makeCondition(lhs, this.operator, rhs),
                             EntityOperator.OR,
                             EntityCondition.makeCondition(lhs, EntityOperator.EQUALS, null));
                 }
-            } else if ( value == null && (operator.equals(EntityOperator.EQUALS) || operator.equals(EntityOperator.NOT_EQUAL))) {
-                return EntityCondition.makeCondition(lhs, UtilGenerics.<EntityComparisonOperator<?,?>>cast(operator), null);
+            } else if ( value == null && this.relFieldName == null && (this.operator == EntityOperator.EQUALS || this.operator == EntityOperator.NOT_EQUAL)) {
+                return EntityCondition.makeCondition(lhs, this.operator, null);
             } else {
                 if (ignoreCase) {
                     // use the stuff to upper case both sides
-                    return EntityCondition.makeCondition(EntityFunction.UPPER(lhs), UtilGenerics.<EntityComparisonOperator<?,?>>cast(operator), EntityFunction.UPPER(rhs));
+                    return EntityCondition.makeCondition(EntityFunction.UPPER(lhs), this.operator, EntityFunction.UPPER(rhs));
                 } else {
-                    return EntityCondition.makeCondition(lhs, UtilGenerics.<EntityComparisonOperator<?,?>>cast(operator), rhs);
+                    return EntityCondition.makeCondition(lhs, this.operator, rhs);
                 }
             }
         }
     }
 
-    public static class ViewConditionList implements ViewCondition {
-        protected ViewEntityCondition viewEntityCondition;
-        List<ViewCondition> conditionList = new LinkedList<ViewCondition>();
-        String combine;
+    public static final class ViewConditionList implements ViewCondition {
+        protected final ViewEntityCondition viewEntityCondition;
+        protected final List<ViewCondition> conditionList = new LinkedList<ViewCondition>();
+        protected final EntityJoinOperator operator;
 
         public ViewConditionList(ViewEntityCondition viewEntityCondition, Element conditionListElement) {
             this.viewEntityCondition = viewEntityCondition;
-            this.combine = conditionListElement.getAttribute("combine");
+            String combine = conditionListElement.getAttribute("combine");
+            try {
+                this.operator = EntityOperator.lookupJoin(combine);
+            } catch (IllegalArgumentException e) {
+                throw new IllegalArgumentException("[" + this.viewEntityCondition.modelViewEntity.getEntityName() + "]: Could not find an entity operator for the name: " + combine);
+            }
 
             List<? extends Element> subElements = UtilXml.childElementList(conditionListElement);
             for (Element subElement: subElements) {
@@ -1329,11 +1444,23 @@ public class ModelViewEntity extends Mod
                 } else if ("condition-list".equals(subElement.getNodeName())) {
                     conditionList.add(new ViewConditionList(this.viewEntityCondition, subElement));
                 } else {
-                    throw new IllegalArgumentException("Invalid element with name [" + subElement.getNodeName() + "] found under a condition-list element.");
+                    throw new IllegalArgumentException("[" + this.viewEntityCondition.modelViewEntity.getEntityName() + "]: Invalid element with name [" + subElement.getNodeName() + "] found under a condition-list element.");
                 }
             }
         }
 
+        public ViewConditionList(ViewEntityCondition viewEntityCondition, String combine, List<ViewCondition> conditionList) {
+            this.viewEntityCondition = viewEntityCondition;
+            try {
+                this.operator = EntityOperator.lookupJoin(combine);
+            } catch (IllegalArgumentException e) {
+                throw new IllegalArgumentException("[" + this.viewEntityCondition.modelViewEntity.getEntityName() + "]: Could not find an entity operator for the name: " + combine);
+            }
+            if (UtilValidate.isNotEmpty(conditionList)) {
+                this.conditionList.addAll(conditionList);
+            }
+        }
+
         public EntityCondition createCondition(ModelFieldTypeReader modelFieldTypeReader, List<String> entityAliasStack) {
             if (this.conditionList.size() == 0) {
                 return null;
@@ -1351,12 +1478,7 @@ public class ModelViewEntity extends Mod
                 }
             }
 
-            EntityOperator<?,?,?> operator = EntityOperator.lookup(this.combine);
-            if (operator == null) {
-                throw new IllegalArgumentException("Could not find an entity operator for the name: " + operator);
-            }
-
-            return EntityCondition.makeCondition(entityConditionList, UtilGenerics.<EntityJoinOperator>cast(operator));
+            return EntityCondition.makeCondition(entityConditionList, this.operator);
         }
     }
 }

Modified: ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/sql/EntityPlanner.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/sql/EntityPlanner.java?rev=1153560&r1=1153559&r2=1153560&view=diff
==============================================================================
--- ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/sql/EntityPlanner.java (original)
+++ ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/sql/EntityPlanner.java Wed Aug  3 16:12:58 2011
@@ -74,25 +74,38 @@ public class EntityPlanner extends Plann
     public EntitySelectPlan planSelect(SQLSelect selectStatement) {
         DynamicViewEntity dve = new DynamicViewEntity();
         Unioned unioned = selectStatement.getUnioned();
-        if (unioned.getOperator() != null || unioned.getNext() != null) {
+        if (unioned != null) {
             throw new IllegalArgumentException("union views not yet supported");
         }
         SelectGroup selectGroup = unioned.getGroup();
         Table table = selectGroup.getTable();
         addMember(dve, table.getTableName());
         addJoined(dve, table.getTableName().getAlias(), table.getJoined());
-        for (FieldAll fieldAll: selectGroup.getFieldAlls()) {
-            dve.addAliasAll(fieldAll.getAlias(), null);
+        if (selectGroup.getFieldAlls() != null) {
+            for (FieldAll fieldAll: selectGroup.getFieldAlls()) {
+                List<String> excludes = FastList.newInstance();
+                for (String exclude: fieldAll) {
+                    excludes.add(exclude);
+                }
+                if (excludes.isEmpty()) {
+                    excludes = null;
+                }
+                dve.addAliasAll(fieldAll.getAlias(), null, excludes);
+            }
         }
-        for (Relation relation: selectStatement.getRelations().values()) {
-            dve.addRelation(relation.getType(), relation.getTitle(), relation.getEntityName(), buildKeyMaps(relation));
+        if (selectStatement.getRelations() != null) {
+            for (Relation relation: selectStatement.getRelations().values()) {
+                dve.addRelation(relation.getType(), relation.getTitle(), relation.getEntityName(), buildKeyMaps(relation));
+            }
         }
         List<String> groupBy = selectGroup.getGroupBy();
         if (groupBy == null) {
             groupBy = Collections.emptyList();
         }
-        for (FieldDef fieldDef: selectGroup.getFieldDefs()) {
-            addFieldDef(dve, groupBy, fieldDef.getAlias(), fieldDef);
+        if (selectGroup.getFieldDefs() != null) {
+            for (FieldDef fieldDef: selectGroup.getFieldDefs()) {
+                addFieldDef(dve, groupBy, fieldDef.getAlias(), fieldDef);
+            }
         }
         List<String> orderBy;
         if (selectStatement.getOrderBy() == null) {

Modified: ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/sql/EntitySelectPlan.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/sql/EntitySelectPlan.java?rev=1153560&r1=1153559&r2=1153560&view=diff
==============================================================================
--- ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/sql/EntitySelectPlan.java (original)
+++ ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/sql/EntitySelectPlan.java Wed Aug  3 16:12:58 2011
@@ -60,7 +60,7 @@ public final class EntitySelectPlan exte
     }
 
     public List<GenericValue> getAll(final Delegator delegator, final Map<String, ? extends Object> params) throws GenericEntityException {
-        return TransactionUtil.doTransaction("sql select", new Callable<List<GenericValue>>() {
+        return TransactionUtil.doTransaction(new Callable<List<GenericValue>>() {
             public List<GenericValue> call() throws Exception {
                 EntityListIterator it = null;
                 try {
@@ -70,7 +70,7 @@ public final class EntitySelectPlan exte
                     if (it != null) it.close();
                 }
             }
-        });
+        }, "sql select", 0, true);
     }
 
     public DynamicViewEntity getDynamicViewEntity() {

Modified: ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/transaction/TransactionUtil.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/transaction/TransactionUtil.java?rev=1153560&r1=1153559&r2=1153560&view=diff
==============================================================================
--- ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/transaction/TransactionUtil.java (original)
+++ ofbiz/branches/jackrabbit20100709/framework/entity/src/org/ofbiz/entity/transaction/TransactionUtil.java Wed Aug  3 16:12:58 2011
@@ -75,52 +75,46 @@ public class TransactionUtil implements 
     private static ThreadLocal<Timestamp> transactionStartStamp = new ThreadLocal<Timestamp>();
     private static ThreadLocal<Timestamp> transactionLastNowStamp = new ThreadLocal<Timestamp>();
 
+    @Deprecated
     public static <V> V doNewTransaction(String ifErrorMessage, Callable<V> callable) throws GenericEntityException {
-        return doNewTransaction(ifErrorMessage, true, callable);
+        return inTransaction(noTransaction(callable), ifErrorMessage, 0, true).call();
     }
 
+    @Deprecated
     public static <V> V doNewTransaction(String ifErrorMessage, boolean printException, Callable<V> callable) throws GenericEntityException {
-        Transaction tx = TransactionUtil.suspend();
-        try {
-            return doTransaction(ifErrorMessage, printException, callable);
-        } finally {
-            TransactionUtil.resume(tx);
-        }
+        return inTransaction(noTransaction(callable), ifErrorMessage, 0, printException).call();
     }
 
+    public static <V> V doNewTransaction(Callable<V> callable, String ifErrorMessage, int timeout, boolean printException) throws GenericEntityException {
+        return inTransaction(noTransaction(callable), ifErrorMessage, timeout, printException).call();
+    }
+
+    @Deprecated
     public static <V> V doTransaction(String ifErrorMessage, Callable<V> callable) throws GenericEntityException {
-        return doTransaction(ifErrorMessage, true, callable);
+        return inTransaction(callable, ifErrorMessage, 0, true).call();
     }
 
+    @Deprecated
     public static <V> V doTransaction(String ifErrorMessage, boolean printException, Callable<V> callable) throws GenericEntityException {
-        boolean tx = TransactionUtil.begin();
-        Throwable transactionAbortCause = null;
-        try {
-            try {
-                return callable.call();
-            } catch (Throwable t) {
-                while (t.getCause() != null) {
-                    t = t.getCause();
-                }
-                throw t;
-            }
-        } catch (Error e) {
-            transactionAbortCause = e;
-            throw e;
-        } catch (RuntimeException e) {
-            transactionAbortCause = e;
-            throw e;
-        } catch (Throwable t) {
-            transactionAbortCause = t;
-            throw new GenericEntityException(t);
-        } finally {
-            if (transactionAbortCause == null) {
-                TransactionUtil.commit(tx);
-            } else {
-                if (printException) transactionAbortCause.printStackTrace();
-                TransactionUtil.rollback(tx, ifErrorMessage, transactionAbortCause);
-            }
-        }
+        return inTransaction(callable, ifErrorMessage, 0, printException).call();
+    }
+
+    public static <V> V doTransaction(Callable<V> callable, String ifErrorMessage, int timeout, boolean printException) throws GenericEntityException {
+        return inTransaction(callable, ifErrorMessage, timeout, printException).call();
+    }
+
+    public static <V> Callable<V> noTransaction(Callable<V> callable) {
+        return new NoTransaction<V>(callable);
+    }
+
+    // This syntax is groovy compatible, with the primary(callable) as the first arg.
+    // You could do:
+    // use (TransactionUtil) {
+    //   Callable callable = ....
+    //   Object result = callable.noTransaction().inTransaction(ifError, timeout, print).call()
+    // }
+    public static <V> InTransaction<V> inTransaction(Callable<V> callable, String ifErrorMessage, int timeout, boolean printException) {
+        return new InTransaction<V>(callable, ifErrorMessage, timeout, printException);
     }
 
     /** Begins a transaction in the current thread IF transactions are available; only
@@ -142,9 +136,13 @@ public class TransactionUtil implements 
         if (ut != null) {
             try {
                 int currentStatus = ut.getStatus();
-                if (Debug.verboseOn()) Debug.logVerbose("[TransactionUtil.begin] current status : " + getTransactionStateString(currentStatus), module);
+                if (Debug.verboseOn()) {
+                    Debug.logVerbose("[TransactionUtil.begin] current status : " + getTransactionStateString(currentStatus), module);
+                }
                 if (currentStatus == Status.STATUS_ACTIVE) {
-                    if (Debug.verboseOn()) Debug.logVerbose("[TransactionUtil.begin] active transaction in place, so no transaction begun", module);
+                    if (Debug.verboseOn()) {
+                        Debug.logVerbose("[TransactionUtil.begin] active transaction in place, so no transaction begun", module);
+                    }
                     return false;
                 } else if (currentStatus == Status.STATUS_MARKED_ROLLBACK) {
                     Exception e = getTransactionBeginStack();
@@ -200,12 +198,16 @@ public class TransactionUtil implements 
         // set the timeout for THIS transaction
         if (timeout > 0) {
             ut.setTransactionTimeout(timeout);
-            if (Debug.verboseOn()) Debug.logVerbose("[TransactionUtil.begin] set transaction timeout to : " + timeout + " seconds", module);
+            if (Debug.verboseOn()) {
+                Debug.logVerbose("[TransactionUtil.begin] set transaction timeout to : " + timeout + " seconds", module);
+            }
         }
 
         // begin the transaction
         ut.begin();
-        if (Debug.verboseOn()) Debug.logVerbose("[TransactionUtil.begin] transaction begun", module);
+        if (Debug.verboseOn()) {
+            Debug.logVerbose("[TransactionUtil.begin] transaction begun", module);
+        }
 
         // reset the timeout to the default
         if (timeout > 0) {
@@ -372,7 +374,9 @@ public class TransactionUtil implements 
 
                 if (status != STATUS_NO_TRANSACTION) {
                     if (status != STATUS_MARKED_ROLLBACK) {
-                        if (Debug.warningOn()) Debug.logWarning(new Exception(causeMessage), "[TransactionUtil.setRollbackOnly] Calling transaction setRollbackOnly; this stack trace shows where this is happening:", module);
+                        if (Debug.warningOn()) {
+                            Debug.logWarning(new Exception(causeMessage), "[TransactionUtil.setRollbackOnly] Calling transaction setRollbackOnly; this stack trace shows where this is happening:", module);
+                        }
                         ut.setRollbackOnly();
                         setSetRollbackOnlyCause(causeMessage, causeThrowable);
                     } else {
@@ -416,9 +420,11 @@ public class TransactionUtil implements 
     }
 
     public static void resume(Transaction parentTx) throws GenericTransactionException {
-        if (parentTx == null) return;
+        if (parentTx == null) {
+            return;
+        }
+        TransactionManager txMgr = TransactionFactory.getTransactionManager();
         try {
-            TransactionManager txMgr = TransactionFactory.getTransactionManager();
             if (txMgr != null) {
                 setTransactionBeginStack(popTransactionBeginStackSave());
                 setSetRollbackOnlyCause(popSetRollbackOnlyCauseSave());
@@ -428,12 +434,12 @@ public class TransactionUtil implements 
         } catch (InvalidTransactionException e) {
             /* NOTE: uncomment this for Weblogic Application Server
             // this is a work-around for non-standard Weblogic functionality; for more information see: http://www.onjava.com/pub/a/onjava/2005/07/20/transactions.html?page=3
-            if (parentTx instanceof weblogic.transaction.ClientTransactionManager) {
+            if (txMgr instanceof weblogic.transaction.ClientTransactionManager) {
                 // WebLogic 8 and above
-                ((weblogic.transaction.ClientTransactionManager) parentTx).forceResume(transaction);
-            } else if (parentTx instanceof weblogic.transaction.TransactionManager) {
+                ((weblogic.transaction.ClientTransactionManager) txMgr).forceResume(parentTx);
+            } else if (txMgr instanceof weblogic.transaction.TransactionManager) {
                 // WebLogic 7
-                ((weblogic.transaction.TransactionManager) parentTx).forceResume(transaction);
+                ((weblogic.transaction.TransactionManager) txMgr).forceResume(parentTx);
             } else {
                 throw new GenericTransactionException("System error, could not resume transaction", e);
             }
@@ -580,10 +586,12 @@ public class TransactionUtil implements 
         clearTransactionStartStampStack();
         return num;
     }
+
     public static boolean suspendedTransactionsHeld() {
         List<Transaction> tl = suspendedTxStack.get();
         return UtilValidate.isNotEmpty(tl);
     }
+
     public static List<Transaction> getSuspendedTxStack() {
         List<Transaction> tl = suspendedTxStack.get();
         if (tl == null) {
@@ -592,6 +600,7 @@ public class TransactionUtil implements 
         }
         return tl;
     }
+
     public static List<Exception> getSuspendedTxLocationsStack() {
         List<Exception> tl = suspendedTxLocationStack.get();
         if (tl == null) {
@@ -600,6 +609,7 @@ public class TransactionUtil implements 
         }
         return tl;
     }
+
     protected static void pushSuspendedTransaction(Transaction t) {
         List<Transaction> tl = getSuspendedTxStack();
         tl.add(0, t);
@@ -608,24 +618,30 @@ public class TransactionUtil implements 
         // save the current transaction start stamp
         pushTransactionStartStamp(t);
     }
+
     protected static Transaction popSuspendedTransaction() {
         List<Transaction> tl = suspendedTxStack.get();
         if (UtilValidate.isNotEmpty(tl)) {
             // restore the transaction start stamp
             popTransactionStartStamp();
             List<Exception> stls = suspendedTxLocationStack.get();
-            if (UtilValidate.isNotEmpty(stls)) stls.remove(0);
+            if (UtilValidate.isNotEmpty(stls)) {
+                stls.remove(0);
+            }
             return tl.remove(0);
         } else {
             return null;
         }
     }
+
     protected static void removeSuspendedTransaction(Transaction t) {
         List<Transaction> tl = suspendedTxStack.get();
         if (UtilValidate.isNotEmpty(tl)) {
             tl.remove(t);
             List<Exception> stls = suspendedTxLocationStack.get();
-            if (UtilValidate.isNotEmpty(stls)) stls.remove(0);
+            if (UtilValidate.isNotEmpty(stls)) {
+                stls.remove(0);
+            }
             popTransactionStartStamp(t);
         }
     }
@@ -650,6 +666,7 @@ public class TransactionUtil implements 
         }
         ctEl.add(0, e);
     }
+
     private static Exception popTransactionBeginStackSave() {
         // do the unofficial all threads Map one first, and don't do a real return
         Long curThreadId = Thread.currentThread().getId();
@@ -666,6 +683,7 @@ public class TransactionUtil implements 
             return null;
         }
     }
+
     public static int getTransactionBeginStackSaveSize() {
         List<Exception> el = transactionBeginStackSave.get();
         if (el != null) {
@@ -674,18 +692,21 @@ public class TransactionUtil implements 
             return 0;
         }
     }
+
     public static List<Exception> getTransactionBeginStackSave() {
         List<Exception> el = transactionBeginStackSave.get();
         List<Exception> elClone = FastList.newInstance();
         elClone.addAll(el);
         return elClone;
     }
+
     public static Map<Long, List<Exception>> getAllThreadsTransactionBeginStackSave() {
         Map<Long, List<Exception>> attbssMap = allThreadsTransactionBeginStackSave;
         Map<Long, List<Exception>> attbssMapClone = FastMap.newInstance();
         attbssMapClone.putAll(attbssMap);
         return attbssMapClone;
     }
+
     public static void printAllThreadsTransactionBeginStacks() {
         if (!Debug.infoOn()) {
             return;
@@ -714,6 +735,7 @@ public class TransactionUtil implements 
         Exception e = new Exception("Tx Stack Placeholder");
         setTransactionBeginStack(e);
     }
+
     private static void setTransactionBeginStack(Exception newExc) {
         if (transactionBeginStack.get() != null) {
             Exception e = transactionBeginStack.get();
@@ -725,6 +747,7 @@ public class TransactionUtil implements 
         Long curThreadId = Thread.currentThread().getId();
         allThreadsTransactionBeginStack.put(curThreadId, newExc);
     }
+
     private static Exception clearTransactionBeginStack() {
         Long curThreadId = Thread.currentThread().getId();
         allThreadsTransactionBeginStack.remove(curThreadId);
@@ -739,6 +762,7 @@ public class TransactionUtil implements 
             return e;
         }
     }
+
     public static Exception getTransactionBeginStack() {
         Exception e = transactionBeginStack.get();
         if (e == null) {
@@ -754,14 +778,26 @@ public class TransactionUtil implements 
     private static class RollbackOnlyCause {
         protected String causeMessage;
         protected Throwable causeThrowable;
+
         public RollbackOnlyCause(String causeMessage, Throwable causeThrowable) {
             this.causeMessage = causeMessage;
             this.causeThrowable = causeThrowable;
         }
-        public String getCauseMessage() { return this.causeMessage + (this.causeThrowable == null ? "" : this.causeThrowable.toString()); }
-        public Throwable getCauseThrowable() { return this.causeThrowable; }
-        public void logError(String message) { Debug.logError(this.getCauseThrowable(), (message == null ? "" : message) + this.getCauseMessage(), module); }
-        public boolean isEmpty() { return (UtilValidate.isEmpty(this.getCauseMessage()) && this.getCauseThrowable() == null); }
+        public String getCauseMessage() {
+            return this.causeMessage + (this.causeThrowable == null ? "" : this.causeThrowable.toString());
+        }
+
+        public Throwable getCauseThrowable() {
+            return this.causeThrowable;
+        }
+
+        public void logError(String message) {
+            Debug.logError(this.getCauseThrowable(), (message == null ? "" : message) + this.getCauseMessage(), module);
+        }
+
+        public boolean isEmpty() {
+            return (UtilValidate.isEmpty(this.getCauseMessage()) && this.getCauseThrowable() == null);
+        }
     }
 
     private static void pushSetRollbackOnlyCauseSave(RollbackOnlyCause e) {
@@ -772,6 +808,7 @@ public class TransactionUtil implements 
         }
         el.add(0, e);
     }
+
     private static RollbackOnlyCause popSetRollbackOnlyCauseSave() {
         List<RollbackOnlyCause> el = setRollbackOnlyCauseSave.get();
         if (UtilValidate.isNotEmpty(el)) {
@@ -785,6 +822,7 @@ public class TransactionUtil implements 
         RollbackOnlyCause roc = new RollbackOnlyCause(causeMessage, causeThrowable);
         setSetRollbackOnlyCause(roc);
     }
+
     private static void setSetRollbackOnlyCause(RollbackOnlyCause newRoc) {
         if (setRollbackOnlyCause.get() != null) {
             RollbackOnlyCause roc = setRollbackOnlyCause.get();
@@ -794,6 +832,7 @@ public class TransactionUtil implements 
         }
         setRollbackOnlyCause.set(newRoc);
     }
+
     private static RollbackOnlyCause clearSetRollbackOnlyCause() {
         RollbackOnlyCause roc = setRollbackOnlyCause.get();
         if (roc == null) {
@@ -845,7 +884,6 @@ public class TransactionUtil implements 
         }
     }
 
-
     /**
     * Method called when the suspended stack gets cleaned by {@link #cleanSuspendedTransactions()}.
     */
@@ -926,4 +964,68 @@ public class TransactionUtil implements 
         public void beforeCompletion() {
         }
     }
+
+    public static final class NoTransaction<V> implements Callable<V> {
+        private final Callable<V> callable;
+
+        protected NoTransaction(Callable<V> callable) {
+            this.callable = callable;
+        }
+
+        public V call() throws Exception {
+            Transaction suspended = TransactionUtil.suspend();
+            try {
+                return callable.call();
+            } finally {
+                TransactionUtil.resume(suspended);
+            }
+        }
+    }
+
+    public static final class InTransaction<V> implements Callable<V> {
+        private final Callable<V> callable;
+        private final String ifErrorMessage;
+        private final int timeout;
+        private final boolean printException;
+
+        protected InTransaction(Callable<V> callable, String ifErrorMessage, int timeout, boolean printException) {
+            this.callable = callable;
+            this.ifErrorMessage = ifErrorMessage;
+            this.timeout = timeout;
+            this.printException = printException;
+        }
+
+        public V call() throws GenericEntityException {
+            boolean tx = TransactionUtil.begin(timeout);
+            Throwable transactionAbortCause = null;
+            try {
+                try {
+                    return callable.call();
+                } catch (Throwable t) {
+                    while (t.getCause() != null) {
+                        t = t.getCause();
+                    }
+                    throw t;
+                }
+            } catch (Error e) {
+                transactionAbortCause = e;
+                throw e;
+            } catch (RuntimeException e) {
+                transactionAbortCause = e;
+                throw e;
+            } catch (Throwable t) {
+                transactionAbortCause = t;
+                throw new GenericEntityException(t);
+            } finally {
+                if (transactionAbortCause == null) {
+                    TransactionUtil.commit(tx);
+                } else {
+                    if (printException) {
+                        transactionAbortCause.printStackTrace();
+                    }
+                    TransactionUtil.rollback(tx, ifErrorMessage, transactionAbortCause);
+                }
+            }
+        }
+    }
 }