[cvs] expresso commit by lhamel: cosmetic reformatting

JCorporate Ltd jcorp at jcorporate.com
Thu Jan 27 03:09:39 UTC 2005


Log Message:
-----------
cosmetic reformatting

Modified Files:
--------------
    expresso/expresso-web/WEB-INF/src/com/jcorporate/expresso/core/dbobj:
        MultiDBObject.java

Revision Data
-------------
Index: MultiDBObject.java
===================================================================
RCS file: /home/javacorp/.cvs/expresso/expresso/expresso-web/WEB-INF/src/com/jcorporate/expresso/core/dbobj/MultiDBObject.java,v
retrieving revision 1.65
retrieving revision 1.66
diff -Lexpresso-web/WEB-INF/src/com/jcorporate/expresso/core/dbobj/MultiDBObject.java -Lexpresso-web/WEB-INF/src/com/jcorporate/expresso/core/dbobj/MultiDBObject.java -u -r1.65 -r1.66
--- expresso-web/WEB-INF/src/com/jcorporate/expresso/core/dbobj/MultiDBObject.java
+++ expresso-web/WEB-INF/src/com/jcorporate/expresso/core/dbobj/MultiDBObject.java
@@ -94,9 +94,12 @@
 
 /**
  * This class handles SQL joins with java coding only.  It is not deprectated, but
- * @see com.jcorporate.expresso.core.dataobjects.jdbc.JoinedDataObject for a more flexible approach to joins
- * which uses xml files.
  *
+ * @author Michael Nash
+ * @author Larry Hamel
+ * @see com.jcorporate.expresso.core.dataobjects.jdbc.JoinedDataObject for a more flexible approach to joins
+ *      which uses xml files.
+ *      <p/>
  *      <p/>
  *      A MultiDBObject is a group of dbobjects that are "related" - e.g. defined
  *      as being part of a foreign-key/primary-key relationship. This may be
@@ -121,397 +124,382 @@
  *      SecurDBObject will get UserID automatically from request registry.
  *      <p/>
  *      Creation date: (9/18/00 11:32:03 AM)
- *      @author  Michael Nash
- *      @author Larry Hamel
  */
 public class MultiDBObject {
 
-  // ----------------------------------------------------
-  // 2003-11-05 /ebn-ma: added...
-  /**
-   * Holds the shortName of these DBObjects, which have to be ignored in the
-   * where-clause because they are still used in a on-clause of a join.
-   * Ex.: ignoreInWhereClause = ",bt,,an,,qu,";
-   * if (ignoreInWhereClause.indexOf(",bt,") >= 0) { continue; }
-   */
-  private String ignoreInWhereClause = "";
-
-  /**
-   * Hold the join-conditions in "original" form.
-   * For the getThisMultiDBObj method and for to create the from-clause
-   * at execution-time.
-   */
-  private Vector originalJoins = new Vector();
-
-  /**
-   * If we are using a custom from clause for this query, it's stored here.
-   * If null, then build the from clause with the join or the foreign-key
-   * definitions.
-   * Do not include the sql-keyword FROM.
-   * Note: If you define a customFromClause, you will usually have to define
-   * a custemWhereClause too.
-   */
-  private String customFromClause = null;
-  // 2003-11-05 /ebn-ma: ...added
-  // ----------------------------------------------------
-
-  /**
-   * Use a DISTINCT keyword right after SELECT keyword
-   */
-  private boolean selectDistinct = false;
-
-  /**
-   * Attributes of this DB Object
-   */
-  private HashMap attributes = null;
-
-  /**
-   * The number of records we must skip over before we start reading
-   * the <code>ResultSet</code> proper in a searchAndRetrieve.
-   * 0 means no limit
-   * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
-   */
-  transient protected int offsetRecord = 0;
-
-  /**
-   * If we are using a custom where clause for this query, this flag will
-   * signify if we need to append the custom where clause to that generated
-   * by the setForeignKey() calls
-   */
-  private boolean appendCustomWhereClause = false;
-
-  /**
-   * Static variables for join type
-   */
-  protected static final int INNER_JOIN = 0;
-  protected static final int RIGHT_JOIN = 1;
-  protected static final int LEFT_JOIN = 2;
-
-  /**
-   * Constant for shortName DBObject attribute
-   */
-  protected static final String SHORT_NAME = "shortName";
-
-  /**
-   * 'FROM' clause formed by calls to setJoin().
-   */
-  private String fromClause = null;
-
-  /**
-   * If set to true, the shortname for the DBObject will be used as an alias
-   * in the query; defaults to false for backward-compatiblity
-   */
-  private boolean shortNameAsAlias = false;
-
-  /**
-   * Return an "attribute". Attributes are temporary (e.g. not stored in the DBMS)
-   * values associated with this particular DB object instance.
-   *
-   * @param attribName The attribute name to check
-   * @return the object associated with this attribute
-   */
-  public Object getAttribute(String attribName) {
-    if (attributes != null) {
-      return attributes.get(attribName);
-    }
-    else {
-      return null;
-    }
-  }
-
-  /* getAttribute(String) */
-
-  /**
-   * Set an attribute. Attributes are temporary (e.g. not stored in the DBMS) values
-   * associated with this particular DB object instance.
-   *
-   * @param attribName  The name of the attribute being defined
-   * @param attribValue The object to store under this attribute name
-   */
-  public synchronized void setAttribute(String attribName,
-                                        Object attribValue) {
-    if (attributes == null) {
-      attributes = new HashMap();
-    }
-
-    attributes.put(attribName, attribValue);
-  }
-
-  /* setAttribute(String, Object) */
-
-  /**
-   * A DB Object can be told to only retrieve a certain number of records. If a
-   * "max records" value has been specified, this method provides access to it.
-   *
-   * @return The maximum number of records that should be retrieved, or zero
-   *         if no max has been set
-   */
-  public int getMaxRecords() {
-    return maxRecords;
-  }
-
-  /* getMaxRecords() */
-
-  /**
-   * Specifies the number of records that should be skipped over
-   * before any data from the <code>ResultSet</code>
-   * is retrieved in any subsequent
-   * searchAndRetrieve() call. Records will be skipped over (in the specified
-   * sort order) until the record counts is equal to or greater
-   * than the offset record. Specifying zero indicates that no
-   * records should be skipped over and the
-   * <code>ResultSet</code> immediately from the start.
-   *
-   * @param newOffset The maximum number of records to retrieve.
-   * @throws DBException If the max number is less than 0
-   *                     <p/>
-   *                     author Peter Pilgrim <peterp  at  xenonsoft dot demon dot co dot  uk>
-   *                     date Tue Feb 05 23:06:38 GMT 2002
-   */
-  public synchronized void setOffsetRecord(int newOffset) throws DBException {
-    if (newOffset < 0) {
-      throw new DBException("Offset records can't be less than 0");
-    }
-
-    offsetRecord = newOffset;
-  }
-
-  /* setOffsetRecord(int) */
-
-  /**
-   * Gets the number of records that be skipped. The offset records.
-   * A DB Object can be told to skip a certain number of
-   * records, before reading records from the <code>ResultSet</code>.
-   * <p/>
-   * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
-   *
-   * @return The maximum number of records that should be
-   *         skipped over before reading the data records.
-   * @see #setOffsetRecord(int)
-   */
-  public int getOffsetRecord() {
-    return offsetRecord;
-  }
-
-  /* getOffsetRecord() */
-
-  // ----------------------------------------------------
-  // 2003-11-05 /ebn-ma: added...
-  /**
-   * Specify a custom "from" clause for the SQL used to retrieve
-   * records for this object.
-   *
-   * @param newCustomFrom java.lang.String
-   */
-  public synchronized void setCustomFromClause(String newCustomFrom) {
-    customFromClause = newCustomFrom;
-  }
-
-  /**
-   * Gets the customFromClause
-   *
-   * @return customFromClause
-   */
-  public String getCustomFromClause() {
-    return customFromClause;
-  }
-
-  /**
-   * Build a string consisting of an SQL 'from' clause using the
-   * customFromClause, the foreign-key or the join definitions.
-   * The result will be found in property fromClause.
-   *
-   * @return true = success.
-   *         <p/>
-   *         Modify by Yves Henri AMAIZO <amy_amaizo at compuserve.com>
-   * @since $DatabaseSchema  $Date$
-   */
-  public boolean buildFromClause() throws DBException {
-    boolean success = false;
-    FastStringBuffer fsb = new FastStringBuffer(256);
-    int dboCount = myDBObjects.size();
-
-    try {
-      // ----------------------------------------------------
-      if (customFromClause != null &&
-          customFromClause.trim().length() > 0) {
-        // As long as a customFromClause is defined , we will use it.
-        fromClause = customFromClause;
-        success = true;
-      }
-      // ----------------------------------------------------
-      else if (!originalJoins.isEmpty()) {
-        if (!originalRelations.isEmpty()) {
-          throw new DBException(thisClass + "count()" +
-                                "You cannot mix set???Join() with setForeignKey() definitions." +
-                                "Use setInnerJoin() instead of setForeignKey().");
+    // ----------------------------------------------------
+    // 2003-11-05 /ebn-ma: added...
+    /**
+     * Holds the shortName of these DBObjects, which have to be ignored in the
+     * where-clause because they are still used in a on-clause of a join.
+     * Ex.: ignoreInWhereClause = ",bt,,an,,qu,";
+     * if (ignoreInWhereClause.indexOf(",bt,") >= 0) { continue; }
+     */
+    private String ignoreInWhereClause = "";
+
+    /**
+     * Hold the join-conditions in "original" form.
+     * For the getThisMultiDBObj method and for to create the from-clause
+     * at execution-time.
+     */
+    private Vector originalJoins = new Vector();
+
+    /**
+     * If we are using a custom from clause for this query, it's stored here.
+     * If null, then build the from clause with the join or the foreign-key
+     * definitions.
+     * Do not include the sql-keyword FROM.
+     * Note: If you define a customFromClause, you will usually have to define
+     * a custemWhereClause too.
+     */
+    private String customFromClause = null;
+    // 2003-11-05 /ebn-ma: ...added
+    // ----------------------------------------------------
+
+    /**
+     * Use a DISTINCT keyword right after SELECT keyword
+     */
+    private boolean selectDistinct = false;
+
+    /**
+     * Attributes of this DB Object
+     */
+    private HashMap attributes = null;
+
+    /**
+     * The number of records we must skip over before we start reading
+     * the <code>ResultSet</code> proper in a searchAndRetrieve.
+     * 0 means no limit
+     * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
+     */
+    transient protected int offsetRecord = 0;
+
+    /**
+     * If we are using a custom where clause for this query, this flag will
+     * signify if we need to append the custom where clause to that generated
+     * by the setForeignKey() calls
+     */
+    private boolean appendCustomWhereClause = false;
+
+    /**
+     * Static variables for join type
+     */
+    protected static final int INNER_JOIN = 0;
+    protected static final int RIGHT_JOIN = 1;
+    protected static final int LEFT_JOIN = 2;
+
+    /**
+     * Constant for shortName DBObject attribute
+     */
+    protected static final String SHORT_NAME = "shortName";
+
+    /**
+     * 'FROM' clause formed by calls to setJoin().
+     */
+    private String fromClause = null;
+
+    /**
+     * If set to true, the shortname for the DBObject will be used as an alias
+     * in the query; defaults to false for backward-compatiblity
+     */
+    private boolean shortNameAsAlias = false;
+
+    /**
+     * Return an "attribute". Attributes are temporary (e.g. not stored in the DBMS)
+     * values associated with this particular DB object instance.
+     *
+     * @param attribName The attribute name to check
+     * @return the object associated with this attribute
+     */
+    public Object getAttribute(String attribName) {
+        if (attributes != null) {
+            return attributes.get(attribName);
+        } else {
+            return null;
         }
+    }
 
-        // Build the from clause with the join definitions...
-        fromClause = null;
-        String oneJoin = null;
+    /* getAttribute(String) */
 
-        for (Enumeration n = originalJoins.elements();
-             n.hasMoreElements(); ) {
-          oneJoin = (String) n.nextElement();
+    /**
+     * Set an attribute. Attributes are temporary (e.g. not stored in the DBMS) values
+     * associated with this particular DB object instance.
+     *
+     * @param attribName  The name of the attribute being defined
+     * @param attribValue The object to store under this attribute name
+     */
+    public synchronized void setAttribute(String attribName,
+                                          Object attribValue) {
+        if (attributes == null) {
+            attributes = new HashMap();
+        }
 
-          StringTokenizer stk = new StringTokenizer(oneJoin, "|");
-          String leftDbo = stk.nextToken();
-          String leftField = stk.nextToken();
-          String rightDbo = stk.nextToken();
-          String rightField = stk.nextToken();
-          String joinTyp = stk.nextToken().toLowerCase();
-
-          if ("left".equals(joinTyp)) {
-            buildJoin(leftDbo, leftField, rightDbo, rightField, LEFT_JOIN);
-          }
-          else if ("inner".equals(joinTyp)) {
-            buildJoin(leftDbo, leftField, rightDbo, rightField, INNER_JOIN);
-          }
-          else if ("right".equals(joinTyp)) {
-            buildJoin(leftDbo, leftField, rightDbo, rightField, RIGHT_JOIN);
-          }
-          else {
-            throw new DBException(thisClass + "count()" +
-                                  "Missing join-type in '" + oneJoin + "'");
-          }
-        }
-        success = true;
-      }
-      // ----------------------------------------------------
-      else if (!originalRelations.isEmpty() || dboCount == 1) {
-        // To build the relations with the foreign-key definitions,
-        // we need to have all the DBObejcts in the from clause.
-        // If this is not the special case, where our Object has only
-        // one embedded DBObject: We have to link them later in the
-        // where clause.
-        boolean needComma = false;
-        DBObject oneObj = null;
+        attributes.put(attribName, attribValue);
+    }
 
-        for (Enumeration eachObj = myDBObjects.elements();
-             eachObj.hasMoreElements(); ) {
-          oneObj = (DBObject) eachObj.nextElement();
+    /* setAttribute(String, Object) */
+
+    /**
+     * A DB Object can be told to only retrieve a certain number of records. If a
+     * "max records" value has been specified, this method provides access to it.
+     *
+     * @return The maximum number of records that should be retrieved, or zero
+     *         if no max has been set
+     */
+    public int getMaxRecords() {
+        return maxRecords;
+    }
+
+    /* getMaxRecords() */
+
+    /**
+     * Specifies the number of records that should be skipped over
+     * before any data from the <code>ResultSet</code>
+     * is retrieved in any subsequent
+     * searchAndRetrieve() call. Records will be skipped over (in the specified
+     * sort order) until the record counts is equal to or greater
+     * than the offset record. Specifying zero indicates that no
+     * records should be skipped over and the
+     * <code>ResultSet</code> immediately from the start.
+     *
+     * @param newOffset The maximum number of records to retrieve.
+     * @throws DBException If the max number is less than 0
+     *                     <p/>
+     *                     author Peter Pilgrim <peterp  at  xenonsoft dot demon dot co dot  uk>
+     *                     date Tue Feb 05 23:06:38 GMT 2002
+     */
+    public synchronized void setOffsetRecord(int newOffset) throws DBException {
+        if (newOffset < 0) {
+            throw new DBException("Offset records can't be less than 0");
+        }
+
+        offsetRecord = newOffset;
+    }
 
-          if (needComma) {
-            fsb.append(",");
-          }
+    /* setOffsetRecord(int) */
+
+    /**
+     * Gets the number of records that be skipped. The offset records.
+     * A DB Object can be told to skip a certain number of
+     * records, before reading records from the <code>ResultSet</code>.
+     * <p/>
+     * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
+     *
+     * @return The maximum number of records that should be
+     *         skipped over before reading the data records.
+     * @see #setOffsetRecord(int)
+     */
+    public int getOffsetRecord() {
+        return offsetRecord;
+    }
+
+    /* getOffsetRecord() */
+
+    // ----------------------------------------------------
+    // 2003-11-05 /ebn-ma: added...
+    /**
+     * Specify a custom "from" clause for the SQL used to retrieve
+     * records for this object.
+     *
+     * @param newCustomFrom java.lang.String
+     */
+    public synchronized void setCustomFromClause(String newCustomFrom) {
+        customFromClause = newCustomFrom;
+    }
+
+    /**
+     * Gets the customFromClause
+     *
+     * @return customFromClause
+     */
+    public String getCustomFromClause() {
+        return customFromClause;
+    }
+
+    /**
+     * Build a string consisting of an SQL 'from' clause using the
+     * customFromClause, the foreign-key or the join definitions.
+     * The result will be found in property fromClause.
+     *
+     * @return true = success.
+     *         <p/>
+     *         Modify by Yves Henri AMAIZO <amy_amaizo at compuserve.com>
+     * @since $DatabaseSchema  $Date$
+     */
+    public boolean buildFromClause() throws DBException {
+        boolean success = false;
+        FastStringBuffer fsb = new FastStringBuffer(256);
+        int dboCount = myDBObjects.size();
+
+        try {
+            // ----------------------------------------------------
+            if (customFromClause != null &&
+                    customFromClause.trim().length() > 0) {
+                // As long as a customFromClause is defined , we will use it.
+                fromClause = customFromClause;
+                success = true;
+            }
+            // ----------------------------------------------------
+            else if (!originalJoins.isEmpty()) {
+                if (!originalRelations.isEmpty()) {
+                    throw new DBException(thisClass + "count()" +
+                            "You cannot mix set???Join() with setForeignKey() definitions." +
+                            "Use setInnerJoin() instead of setForeignKey().");
+                }
+
+                // Build the from clause with the join definitions...
+                fromClause = null;
+                String oneJoin = null;
+
+                for (Enumeration n = originalJoins.elements();
+                     n.hasMoreElements();) {
+                    oneJoin = (String) n.nextElement();
+
+                    StringTokenizer stk = new StringTokenizer(oneJoin, "|");
+                    String leftDbo = stk.nextToken();
+                    String leftField = stk.nextToken();
+                    String rightDbo = stk.nextToken();
+                    String rightField = stk.nextToken();
+                    String joinTyp = stk.nextToken().toLowerCase();
+
+                    if ("left".equals(joinTyp)) {
+                        buildJoin(leftDbo, leftField, rightDbo, rightField, LEFT_JOIN);
+                    } else if ("inner".equals(joinTyp)) {
+                        buildJoin(leftDbo, leftField, rightDbo, rightField, INNER_JOIN);
+                    } else if ("right".equals(joinTyp)) {
+                        buildJoin(leftDbo, leftField, rightDbo, rightField, RIGHT_JOIN);
+                    } else {
+                        throw new DBException(thisClass + "count()" +
+                                "Missing join-type in '" + oneJoin + "'");
+                    }
+                }
+                success = true;
+            }
+            // ----------------------------------------------------
+            else if (!originalRelations.isEmpty() || dboCount == 1) {
+                // To build the relations with the foreign-key definitions,
+                // we need to have all the DBObejcts in the from clause.
+                // If this is not the special case, where our Object has only
+                // one embedded DBObject: We have to link them later in the
+                // where clause.
+                boolean needComma = false;
+                DBObject oneObj = null;
+
+                for (Enumeration eachObj = myDBObjects.elements();
+                     eachObj.hasMoreElements();) {
+                    oneObj = (DBObject) eachObj.nextElement();
+
+                    if (needComma) {
+                        fsb.append(",");
+                    }
 //Edit by Christian Reibnegger 2004-12-03
 //Including the DB-Name in the SQL-FROM-Statement
 //***********************************************
-           try {
-             String dbKey = oneObj.getDataContext();
-             String dataContext = oneObj.getMappedDataContext();
-
-             if (!dbKey.equals(dataContext)) {
-
-               // Get The DataBaseName from the ConfigContext
-               com.jcorporate.expresso.core.misc.ConfigContext ctx =
-                   ConfigManager.getContext(oneObj.getMappedDataContext());
-               ConfigJdbc confJdbc = ctx.getJdbc();
-               String serverURL = confJdbc.getUrl();
-               int lastIndex = serverURL.lastIndexOf("/");
-               String dbName = serverURL.substring(lastIndex + 1);
-
-               // append the DatabaseName to the SQL-FROM-Statement
-               fsb.append(dbName + ".");
-             }
-           }
-           catch (IndexOutOfBoundsException iOOBex) {
-             throw new DBException(this +" IndexOutOfBoundsException: " +
-                                   iOOBex.getMessage());
-           }
-
-          String targetTable = oneObj.getJDBCMetaData().getTargetSQLTable(
-              oneObj.getDataContext());
-          String tableName = getTableName(oneObj);
-          fsb.append(targetTable);
-          // AS keyword to set table short name is not standard and not supported by all
-          // SQL manipulation languages. This need to be set properly with each driver
-          // Keyword to be set in expresso config file for table alias name syntax
-
-          if (!targetTable.equals(tableName)) {
-            if (ConfigManager.getJdbcRequired(getDataContext()).
-                getTableAliasKeyword() != null) {
-              fsb.append(
-                  " " +
-                  ConfigManager.getJdbcRequired(getDataContext()).
-                  getTableAliasKeyword() +
-                  " " + tableName);
-            }
-            else {
-              //fsb.append(" AS " + tableName);
-              // AS keyword to set table short name is not standard and not supported by all
-              // SQL manipulation languages. This need to be set properly with each driver
-              // Standard SQL implementation is space. AS keyword should be added in expresso config
-              fsb.append(" " + tableName);
-            }
-          }
-
-          needComma = true;
-        }
-
-        fromClause = fsb.toString();
-        success = true;
-      }
-      // ----------------------------------------------------
-      else {
-        // build clause
-        boolean needComma = false;
+                    try {
+                        String dbKey = oneObj.getDataContext();
+                        String dataContext = oneObj.getMappedDataContext();
+
+                        if (!dbKey.equals(dataContext)) {
+
+                            // Get The DataBaseName from the ConfigContext
+                            com.jcorporate.expresso.core.misc.ConfigContext ctx =
+                                    ConfigManager.getContext(oneObj.getMappedDataContext());
+                            ConfigJdbc confJdbc = ctx.getJdbc();
+                            String serverURL = confJdbc.getUrl();
+                            int lastIndex = serverURL.lastIndexOf("/");
+                            String dbName = serverURL.substring(lastIndex + 1);
+
+                            // append the DatabaseName to the SQL-FROM-Statement
+                            fsb.append(dbName + ".");
+                        }
+                    } catch (IndexOutOfBoundsException iOOBex) {
+                        throw new DBException(this + " IndexOutOfBoundsException: " +
+                                iOOBex.getMessage());
+                    }
+
+                    String targetTable = oneObj.getJDBCMetaData().getTargetSQLTable(oneObj.getDataContext());
+                    String tableName = getTableName(oneObj);
+                    fsb.append(targetTable);
+                    // AS keyword to set table short name is not standard and not supported by all
+                    // SQL manipulation languages. This need to be set properly with each driver
+                    // Keyword to be set in expresso config file for table alias name syntax
+
+                    if (!targetTable.equals(tableName)) {
+                        if (ConfigManager.getJdbcRequired(getDataContext()).
+                                getTableAliasKeyword() != null) {
+                            fsb.append(" " +
+                                    ConfigManager.getJdbcRequired(getDataContext()).
+                                    getTableAliasKeyword() +
+                                    " " + tableName);
+                        } else {
+                            //fsb.append(" AS " + tableName);
+                            // AS keyword to set table short name is not standard and not supported by all
+                            // SQL manipulation languages. This need to be set properly with each driver
+                            // Standard SQL implementation is space. AS keyword should be added in expresso config
+                            fsb.append(" " + tableName);
+                        }
+                    }
 
-        for (Enumeration eachObj = myDBObjects.elements();
-             eachObj.hasMoreElements(); ) {
-          DBObject oneObj = (DBObject) eachObj.nextElement();
+                    needComma = true;
+                }
 
-          if (needComma) {
-            fsb.append(",");
-          }
-
-          String targetTable = oneObj.getJDBCMetaData().getTargetSQLTable(
-              oneObj.getDataContext());
-          String tableName = getTableName(oneObj);
-          fsb.append(targetTable);
-          if (!targetTable.equals(tableName)) {
-            if (ConfigManager.getJdbcRequired(getDataContext()).
-                getTableAliasKeyword() != null) {
-              fsb.append(
-                  " " +
-                  ConfigManager.getJdbcRequired(getDataContext()).
-                  getTableAliasKeyword() +
-                  " " + tableName);
+                fromClause = fsb.toString();
+                success = true;
             }
+            // ----------------------------------------------------
             else {
-              fsb.append(" AS " + tableName);
-            }
-          }
+                // build clause
+                boolean needComma = false;
+
+                for (Enumeration eachObj = myDBObjects.elements();
+                     eachObj.hasMoreElements();) {
+                    DBObject oneObj = (DBObject) eachObj.nextElement();
+
+                    if (needComma) {
+                        fsb.append(",");
+                    }
+
+                    String targetTable = oneObj.getJDBCMetaData().getTargetSQLTable(oneObj.getDataContext());
+                    String tableName = getTableName(oneObj);
+                    fsb.append(targetTable);
+                    if (!targetTable.equals(tableName)) {
+                        if (ConfigManager.getJdbcRequired(getDataContext()).
+                                getTableAliasKeyword() != null) {
+                            fsb.append(" " +
+                                    ConfigManager.getJdbcRequired(getDataContext()).
+                                    getTableAliasKeyword() +
+                                    " " + tableName);
+                        } else {
+                            fsb.append(" AS " + tableName);
+                        }
+                    }
 
-          needComma = true;
+                    needComma = true;
+                }
+
+                fromClause = fsb.toString();
+                success = true;
+            }
+        } catch (Throwable t) {
+            throw new DBException(t);
+        } finally {
+            fsb.release();
         }
 
-        fromClause = fsb.toString();
-        success = true;
-      }
-    }
-    catch (Throwable t) {
-      throw new DBException(t);
-    }
-    finally {
-      fsb.release();
+        return (success);
     }
 
-    return (success);
-  }
-
-  // 2003-11-05 /ebn-ma: ...added
-  // ----------------------------------------------------
+    // 2003-11-05 /ebn-ma: ...added
+    // ----------------------------------------------------
 
 
-  /**
-   * Just like find, but only retrieves the count, not the records themselves.
-   *
-   * @return integer Count of the records matching the criteria
-   * @throws DBException If the search could not be completed
-   */
-  public synchronized int count(String expr) throws DBException {
+    /**
+     * Just like find, but only retrieves the count, not the records themselves.
+     *
+     * @return integer Count of the records matching the criteria
+     * @throws DBException If the search could not be completed
+     */
+    public synchronized int count(String expr) throws DBException {
 //        boolean needComma = false;
 //        DBObject oneObj = null;
 //        DBField oneField = null;
@@ -577,45 +565,43 @@
 //        }
 
 
-    DBConnectionPool myPool = null;
-    DBConnection myConnection = null;
+        DBConnectionPool myPool = null;
+        DBConnection myConnection = null;
+
+        try {
+            if (localConnection != null) {
+                myConnection = localConnection;
+            } else {
+                myPool = DBConnectionPool.getInstance(getDataContext());
+                myConnection = myPool.getConnection("com.jcorporate.expresso.core.dbobj.DBObject");
+            }
+
+            if (recordSet == null) {
+                String myName = (thisClass + "count()");
+                throw new DBException(myName +
+                        ":Database object not correctly initialized");
+            }
+
+            recordSet.clear();
+
+            FastStringBuffer myStatement = new FastStringBuffer(256);
+            myStatement.append("SELECT ");
+
+            if (selectDistinct) {
+                myStatement.append(" DISTINCT ");
+            }
+            if (StringUtil.isBlankOrNull(expr)) {
+                expr = "*";
+            }
+            myStatement.append("COUNT(" + expr + ") ");
 
-    try {
-      if (localConnection != null) {
-        myConnection = localConnection;
-      }
-      else {
-        myPool = DBConnectionPool.getInstance(getDataContext());
-        myConnection = myPool.getConnection(
-            "com.jcorporate.expresso.core.dbobj.DBObject");
-      }
-
-      if (recordSet == null) {
-        String myName = (thisClass + "count()");
-        throw new DBException(myName +
-                              ":Database object not correctly initialized");
-      }
-
-      recordSet.clear();
-
-      FastStringBuffer myStatement = new FastStringBuffer(256);
-      myStatement.append("SELECT ");
-
-      if (selectDistinct) {
-        myStatement.append(" DISTINCT ");
-      }
-      if (StringUtil.isBlankOrNull(expr)) {
-        expr = "*";
-      }
-      myStatement.append("COUNT(" + expr + ") ");
-
-      // ----------------------------------------------------
-      // 2003-11-05 /ebn-ma: changed...
-      // We build the from clause every time we genereate a
-      // sql-statement. Because:
-      // If we use joins, this is the only chance to integrate
-      // filter-conditions that follow the actual values in
-      // the properties of the related DBObjects.
+            // ----------------------------------------------------
+            // 2003-11-05 /ebn-ma: changed...
+            // We build the from clause every time we genereate a
+            // sql-statement. Because:
+            // If we use joins, this is the only chance to integrate
+            // filter-conditions that follow the actual values in
+            // the properties of the related DBObjects.
 
 //                myStatement.append(" FROM ");
 //                if (fromClause == null) {
@@ -643,1919 +629,1869 @@
 //                     */
 //                }
 
-      myStatement.append(" FROM ");
-      if (buildFromClause() &&
-          fromClause != null &&
-          fromClause.trim().length() > 0) {
-        myStatement.append(fromClause);
-      }
-      else {
-        throw new DBException(thisClass + "count()" +
-                              " :Building of FROM clause failed.");
-      }
-      // 2003-11-05 /ebn-ma: ...changed
-      // ----------------------------------------------------
-
-      String whereClause;
-      if (customWhereClause != null) {
-        if (appendCustomWhereClause) {
-          whereClause = buildWhereClause(true) + " AND " + customWhereClause;
-        }
-        else {
-          whereClause = " WHERE " + customWhereClause;
-        }
-      }
-      else {
-        whereClause = buildWhereClause(true);
-      }
-
-      myStatement.append(whereClause);
-
-      appendCustomWhereClause = false;
-
-      if (log.isDebugEnabled()) {
-        log.debug("Executing " + myStatement.toString());
-      }
-
-      myConnection.execute(myStatement.toString());
-
-      if (myConnection.next()) {
-        return myConnection.getInt(1);
-      }
-    }
-    finally {
-      if (localConnection == null) {
-        myPool.release(myConnection);
-      }
-    }
-
-    return 0;
-  }
-
-  /* count() */
-
-  /**
-   * Just like find, but only retrieves the count, not the records themselves.
-   *
-   * @return integer Count of the records matching the criteria
-   * @throws DBException If the search could not be completed
-   */
-  public synchronized int count() throws DBException {
-    return count("");
-  }
-
-  /* count() */
-
-  /**
-   * Creates the limitation syntax optimisation stub
-   * to embed inside the SQL command that performs
-   * search and retrieve.
-   * <p/>
-   * <p>This method takes the limitation syntax string
-   * and performs a string replacement on the following
-   * tokens
-   * <p/>
-   * <ul>
-   * <p/>
-   * <li><b>%offset%</b><li><br>
-   * the number of rows in the <code>ResultSet</code> to skip
-   * before reading the data.
-   * <p/>
-   * <li><b>%maxrecord%</b><li><br>
-   * the maximum number of rows to read from  the <code>ResultSet</code>.
-   * Also known as the <i>rowlength</i>.
-   * <p/>
-   * <li><b>%endrecord%</b><li><br>
-   * the last record of in the <code>ResultSet</code> that the
-   * search and retrieved should retrieve. The end record number
-   * is equal to <code>( %offset% + %maxrecord% - 1 )</code>
-   * <p/>
-   * </ul>
-   * <p/>
-   * </p>
-   * <p/>
-   * <p/>
-   * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
-   *
-   * @param theConnection the db connection to make this stub from
-   * @return the limitation syntax stub string
-   * @see #searchAndRetrieveList()
-   * @see #setOffsetRecord( int )
-   * @see #setMaxRecords( int )
-   */
-  protected String makeLimitationStub(DBConnection theConnection) {
-    String limit = theConnection.getLimitationSyntax();
-    int offset = this.getOffsetRecord();
-    int maxrec = this.getMaxRecords();
-    int endrec = offset + maxrec - 1;
-    limit = StringUtil.replace(limit, "%offset%", Integer.toString(offset));
-    limit = StringUtil.replace(limit, "%maxrecords%",
-                               Integer.toString(maxrec));
-
-    // limit = StringUtil.replace( limit, "%length%",    Integer.toString( maxrec ) );
-    limit = StringUtil.replace(limit, "%endrecord%",
-                               Integer.toString(endrec));
-
-    return limit;
-  }
-
-  /* makeLimitationStub(DBConnection) */
-
-  private static final String thisClass = MultiDBObject.class.getName() + ".";
-
-  /**
-   * Hash that contains the DB objects that relate to make up this query,
-   * indexed by the "short" name provided by the user.
-   */
-  private Hashtable myDBObjects = new Hashtable();
-
-  /**
-   * Vector to hold the "relations" between the different db objects that
-   * make up this query
-   */
-  private Vector relations = new Vector();
-  private String dbName = null;
-
-  /**
-   * This flag tells the buildWhereClause method(s) to either be case
-   * sensitive (normal queries) or to be case insensitive. The case
-   * insensitive query is useful when you want to do a search and retreive
-   * and want case insensitive matching.
-   */
-  private boolean caseSensitiveQuery = true;
-
-  /**
-   * broken into a vector
-   */
-  private Vector sortKeys = new Vector();
-
-  /**
-   * The vector of MultiDB objects retrieved by the last searchAndRetrieve method
-   */
-  private List recordSet = new ArrayList();
-
-  /**
-   * If we are using a custom where clause for this query, it's stored here. If
-   * null, then build the where clause
-   */
-  private String customWhereClause = null;
-
-  /**
-   * Hold the relations in "original" form - for the getThisMultiDBObj method
-   */
-  private Vector originalRelations = new Vector();
-
-  /**
-   * Local connection that we use if it's initialized, but
-   * if it's null we generate our own connection
-   */
-  protected DBConnection localConnection = null;
-
-  /* The max number of records we retrieve in a searchAndRetrieve.
-   * 0 means no limit
-   */
-  protected int maxRecords = 0;
-  private static Logger log = Logger.getLogger(MultiDBObject.class);
-
-  /**
-   * MultiDBObject constructor. calls setupFields.
-   */
-  public MultiDBObject() throws DBException {
-    setupFields();
-  }
-
-  /* MultiDBObject() */
-
-  /**
-   * MultiDBObject constructor which sets dbname from request
-   */
-  public MultiDBObject(ControllerRequest request) throws DBException {
-    this();
-    setDataContext(request.getDataContext());
-  }
-
-  /* MultiDBObject() */
-
-  /**
-   * Add a DB Object to the objects being used for this multidbobj query. The
-   * object is specified with a full classname, then a "short" name used to refer
-   * to it in this object. For example, the class name might be
-   * com.jcorporate.expresso.services.dbobj.SchemaList, the short name might be
-   * "schema".
-   *
-   * @param dbobjClassName java.lang.String
-   * @param shortName      java.lang.String
-   */
-  public void addDBObj(String dbobjClassName, String shortName) throws
-      DBException {
-    String myName = thisClass + "addDBObj(String, String)";
-
-    if (StringUtil.notNull(dbobjClassName).equals("")) {
-      throw new DBException(myName + ":Must specify a class name");
-    }
-    if (StringUtil.notNull(shortName).equals("")) {
-      throw new DBException(myName + ":Must specify a short name");
-    }
-
-    DBObject oneDBObj = null;
-
-    try {
-      Class c = ClassLocator.loadClass(dbobjClassName);
-      oneDBObj = (DBObject) c.newInstance();
-    }
-    catch (ClassNotFoundException cn) {
-      throw new DBException(myName + ":DBObject '" + dbobjClassName +
-                            "' not found", cn);
-    }
-    catch (InstantiationException ie) {
-      throw new DBException(myName + ":DBObject '" + dbobjClassName +
-                            "' cannot be instantiated", ie);
-    }
-    catch (IllegalAccessException iae) {
-      throw new DBException(myName +
-                            ":Illegal access loading DBObject '" +
-                            dbobjClassName + "'", iae);
-    }
-
-    addDBObj(oneDBObj, shortName);
-  }
-
-  public void addDBObj(DBObject oneDBObj, String shortName) throws DBException {
-    StringUtil.assertNotBlank(shortName, "Short name cannot be blank here");
-    oneDBObj.setAttribute(SHORT_NAME, shortName);
-    myDBObjects.put(shortName, oneDBObj);
-
-    if (getDataContext() == null) {
-      setDataContext(oneDBObj.getDataContext());
-    }
-
-    /* Check to see if all of the db objects are in the same database */
-    oneDBObj.setDataContext(getDataContext());
-
-    if (!oneDBObj.getDataContext().equals(getDataContext())) {
-      throw new DBException("DB Object '" +
-                            oneDBObj.getClass().getName() +
-                            "' was mapped to db/context '" +
-                            oneDBObj.getDataContext() +
-                            "', but this MultiDBObject is in context '" +
-                            getDataContext() + "'");
-    }
-  }
-
-  /* addDBOj(String, String) */
-
-  /**
-   * Build and return a string consisting of an SQL 'where' clause
-   * using the current field values as criteria for the search. See
-   * setCustomWhereClause for information on specifying a more complex where clause.
-   *
-   * @param useAllFields True if all fields are to be used,
-   *                     false for only key fields
-   * @return The where clause to use in a query.
-   */
-  public String buildWhereClause(boolean useAllFields) throws DBException {
-    FastStringBuffer fsb = FastStringBuffer.getInstance();
-    try {
-      return buildWhereClauseBuffer(useAllFields, fsb).toString();
-    }
-    finally {
-      fsb.release();
-    }
-  }
-
-  /* buildWhereClause(boolean) */
-
-  // ----------------------------------------------------
-  // 2003-11-05 /ebn-ma: added...
-  // Method is overwritten for to support a new parameter dboAlias.
-  /**
-   * Build and return a string consisting of an SQL 'where' clause
-   * using the current field values as criteria for the search. See
-   * setCustomWhereClause for information on specifying a more complex where clause.
-   *
-   * @param useAllFields True if all fields are to be used, false for only key fields
-   * @return java.lang.String.
-   */
-  protected String buildWhereClauseBuffer(boolean useAllFields,
-                                          FastStringBuffer myStatement) throws
-      DBException {
-    return buildWhereClauseBuffer(useAllFields, myStatement, " ").toString();
-  }
-
-  // 2003-11-05 /ebn-ma: ...added
-  // ----------------------------------------------------
-
-  // ----------------------------------------------------
-  // 2003-11-05 /ebn-ma: changed...
-  // Method supports a new parameter dboAlias. With the help of this parameter
-  // we build selective clauses for a single DBObject.
-  // These clauses are used in method buildJoin.
-  /**
-   * Build and return a string consisting of an SQL 'where' clause
-   * using the current field values as criteria for the search. See
-   * setCustomWhereClause for information on specifying a more complex where clause.
-   *
-   * @param useAllFields True if all fields are to be used, false for only key fields
-   * @param myStatement  Buffer for to build the new where-clause.
-   * @param dboAlias     Build the where-clause for this DBObject. If empty,
-   *                     work with all DBObjects that are not marked to be ignored.
-   * @return java.lang.String.
-   */
-  protected String buildWhereClauseBuffer(boolean useAllFields,
-                                          FastStringBuffer myStatement,
-                                          String dboAlias) throws DBException {
-
-    /* list of field names (with table names prefixed) */
-    Vector fields = new Vector();
-    DBObject oneObj = null;
-    String fieldName = null;
-    Hashtable byTableName = new Hashtable();
-
-    if (useAllFields) {
-      for (Enumeration eachObj = myDBObjects.elements();
-           eachObj.hasMoreElements(); ) {
-        oneObj = (DBObject) eachObj.nextElement();
-        byTableName.put(getTableName(oneObj), oneObj);
+            myStatement.append(" FROM ");
+            if (buildFromClause() &&
+                    fromClause != null &&
+                    fromClause.trim().length() > 0) {
+                myStatement.append(fromClause);
+            } else {
+                throw new DBException(thisClass + "count()" +
+                        " :Building of FROM clause failed.");
+            }
+            // 2003-11-05 /ebn-ma: ...changed
+            // ----------------------------------------------------
 
-        // ----------------------------------------------------
-        // 2003-11-05 /ebn-ma: added...
-        // Two cases:
-        // 1. If dboAlias tells us to compute the on-clause of a join,
-        //    then we will ignore every DBObject that is not the one
-        //    specified in dboAlias.
-        // 2. If dboAlias is empty, we are computing the where-clause of
-        //    our sql-statement.
-        //    In this case we ignore the current DBObject, if it is a
-        //    member of ignoreInWhereClause.
-        String thisDbo = (String) oneObj.getAttribute(SHORT_NAME);
-
-        if (dboAlias.trim().length() > 0) {
-          if (!thisDbo.equals(dboAlias)) {
-            continue;
-          }
-        }
-        else if (ignoreInWhereClause.indexOf("," + thisDbo + ",") >= 0) {
-          continue;
-        }
-        // 2003-11-05 /ebn-ma: ...added
-        // ----------------------------------------------------
+            String whereClause;
+            if (customWhereClause != null) {
+                if (appendCustomWhereClause) {
+                    whereClause = buildWhereClause(true) + " AND " + customWhereClause;
+                } else {
+                    whereClause = " WHERE " + customWhereClause;
+                }
+            } else {
+                whereClause = buildWhereClause(true);
+            }
 
-        for (Iterator i = oneObj.getMetaData().getFieldListArray().iterator();
-             i.hasNext(); ) {
-          fieldName = (String) i.next();
-
-          if (!oneObj.getMetaData().getFieldMetadata(fieldName).isVirtual()) {
-            fields.addElement(getTableName(oneObj) + "." +
-                              fieldName);
-          }
+            myStatement.append(whereClause);
+
+            appendCustomWhereClause = false;
+
+            if (log.isDebugEnabled()) {
+                log.debug("Executing " + myStatement.toString());
+            }
+
+            myConnection.execute(myStatement.toString());
+
+            if (myConnection.next()) {
+                return myConnection.getInt(1);
+            }
+        } finally {
+            if (localConnection == null) {
+                myPool.release(myConnection);
+            }
         }
-        /* for each field */
-      }
+
+        return 0;
     }
-    else { /* for each db object */
-      for (Enumeration eachObj = myDBObjects.elements();
-           eachObj.hasMoreElements(); ) {
-        oneObj = (DBObject) eachObj.nextElement();
 
-        // ----------------------------------------------------
-        // 2003-11-05 /ebn-ma: added...
-        // Two cases:
-        // 1. If dboAlias tells us to compute the on-clause of a join,
-        //    then we will ignore every DBObject that is not the one
-        //    specified in dboAlias.
-        // 2. If dboAlias is empty, we are computing the where-clause of
-        //    our sql-statement.
-        //    In this case we ignore the current DBObject, if it is a
-        //    member of ignoreInWhereClause.
-        String thisDbo = (String) oneObj.getAttribute(SHORT_NAME);
-
-        if (dboAlias.trim().length() > 0) {
-          if (!thisDbo.equals(dboAlias)) {
-            continue;
-          }
-        }
-        else if (ignoreInWhereClause.indexOf("," + thisDbo + ",") >= 0) {
-          continue;
-        }
-        // 2003-11-05 /ebn-ma: ...added
-        // ----------------------------------------------------
+    /* count() */
 
-        for (Iterator i = oneObj.getKeyFieldListIterator();
-             i.hasNext(); ) {
-          fieldName = (String) i.next();
-
-          if (!oneObj.getMetaData().getFieldMetadata(fieldName).isVirtual()) {
-            fields.addElement(getTableName(oneObj) + "." +
-                              fieldName);
-          }
-        }
-        /* for each field */
-      }
-      /* for each db object */
+    /**
+     * Just like find, but only retrieves the count, not the records themselves.
+     *
+     * @return integer Count of the records matching the criteria
+     * @throws DBException If the search could not be completed
+     */
+    public synchronized int count() throws DBException {
+        return count("");
     }
 
-    /* Now go thru each field - if it is non-empty, add it's criteria */
+    /* count() */
 
-    /* to the where clause. If it is empty, just skip to the next one */
-    boolean addWhere = true;
-    boolean addAnd = false;
-    String oneFieldName = null;
-    String oneTableName = null;
-    String oneFullName = null;
-    String oneFieldValue = null;
-    boolean skipText = false;
-
-    try {
-      ConfigJdbc myConfig = ConfigManager.getJdbcRequired(getDataContext());
-      skipText = myConfig.skipText();
-    }
-    catch (ConfigurationException ce) {
-      throw new DBException(ce);
-    }
-
-    boolean skipField = false;
-
-    for (Enumeration fieldsToUse = fields.elements();
-         fieldsToUse.hasMoreElements(); ) {
-      oneFullName = (String) fieldsToUse.nextElement();
-
-      StringTokenizer stk = new StringTokenizer(oneFullName, ".");
-      int stkCount = stk.countTokens();
-      if (stkCount == 2) {
-        oneTableName = stk.nextToken();
-        oneFieldName = stk.nextToken();
-      }
-      else if (stkCount == 3) {
-        String oneSchemaName = stk.nextToken();
-        oneTableName = stk.nextToken();
-        oneFieldName = stk.nextToken();
-        oneTableName = oneSchemaName + "." + oneTableName;
-      }
-
-      oneObj = (DBObject) byTableName.get(oneTableName);
-      skipField = false;
-      // ----------------------------------------------------
-      // 2003-11-18 /ebn-ma: changed...
-      // getField() makes a translation using the charset-filter.
-      // FieldRangeParser cannot succeed, if his control-characters
-      // are changed, e.g. '<' to '&lt'
-      // getFieldData() doesn't do this translation and so it works.
-      // Anyway: The corresponding code-block in JDBCUtil seems to be
-
-
-      // a better solution.
-      //  oneFieldValue =StringUtil.notNull(oneObj.getField(oneFieldName));
-
-
-      oneFieldValue = StringUtil.notNull(oneObj.getFieldData(oneFieldName));
-      // 2003-11-18 /ebn-ma: ...changed
-      // ----------------------------------------------------
-
-      String rangeString = oneObj.denotesRange(oneFieldValue);
-
-      if (!oneFieldValue.equals("")) {
-        oneFieldValue = oneObj.quoteIfNeeded(oneFieldName, rangeString);
-      }
-      if (oneFieldValue.equals("")) {
-        skipField = true;
-      }
-      if (oneFieldValue == null) {
-        skipField = true;
-      }
-      if (oneFieldValue.equals("\'\'")) {
-        skipField = true;
-      }
-      if ( (oneObj.getMetaData().getType(oneFieldName).equals("text")) &&
-          (skipText)) {
-        skipField = true;
-      }
-      if (!skipField) {
-        // check to see if the field value is valid (protects agains sql injection)
-        String unalteredFieldValue = oneObj.getDataField(oneFieldName).asString();
-        if (rangeString != null) {
-          FieldRangeParser rangeParser = new FieldRangeParser();
-          boolean valid = rangeParser.isValidRange(oneObj.getFieldMetaData(
-              oneFieldName),
-              unalteredFieldValue);
-          if (!valid) {
-            throw new DBException("Invalid field range value: " +
-                                  unalteredFieldValue);
-          }
-        }
-        else if (oneObj.containsWildCards(oneFieldValue)) {
-          Object origValue = oneObj.getDataField(oneFieldName).getValue();
-
-          String[] wildcards = null;
-          wildcards = (String[]) oneObj.getConnectionPool().getWildCardsList().
-              toArray(new String[0]);
-          Filter filter = new Filter(wildcards, wildcards);
-          String valueWithoutWildCards = filter.stripFilter(unalteredFieldValue);
-          // if the value without wildcards is empty, then we know the field is valid
-          if (!valueWithoutWildCards.equals("")) {
-            oneObj.getDataField(oneFieldName).setValue(valueWithoutWildCards);
-            oneObj.getDataField(oneFieldName).checkValue();
-            oneObj.getDataField(oneFieldName).setValue(origValue);
-          }
-        }
-        else {
-          oneObj.getDataField(oneFieldName).checkValue();
-        }
-
-        if (addWhere) {
-          myStatement.append(" WHERE ");
-          addWhere = false;
-        }
-        if (addAnd) {
-          myStatement.append(" AND ");
-        }
-        if (oneObj.containsWildCards(oneFieldValue)) {
-          if (caseSensitiveQuery) {
-            myStatement.append(getTableName(oneObj) + "." +
-                               oneFieldName);
-            myStatement.append(" LIKE ");
-            myStatement.append(oneFieldValue);
-          }
-          else {
-            myStatement.append("UPPER(");
-            myStatement.append(getTableName(oneObj) + "." +
-                               oneFieldName);
-            myStatement.append(") LIKE ");
-            myStatement.append(oneFieldValue.toUpperCase());
-          }
-        }
-        else if (rangeString != null) {
-          myStatement.append(getTableName(oneObj) + "." +
-                             oneFieldName);
-          myStatement.append(" " + rangeString + " ");
-          myStatement.append(oneFieldValue);
-        }
-        else {
-          if (caseSensitiveQuery) {
-            myStatement.append(getTableName(oneObj) + "." +
-                               oneFieldName);
-            myStatement.append(" = ");
-            myStatement.append(oneFieldValue);
-          }
-          else {
-            myStatement.append("UPPER(");
-            myStatement.append(getTableName(oneObj) + "." +
-                               oneFieldName);
-            myStatement.append(") = ");
-            myStatement.append(oneFieldValue.toUpperCase());
-          }
-        }
-
-        addAnd = true;
-      }
-
-      /* if field is not skipped for some reason */
-    }
-
-    /* for each field */
-    boolean needAnd = false;
-
-    /**
-     * FIXED: ma 17.10.2003
-     * FIXED: Don't do that at here. We do not need
-     * FIXED: WHERE, if we do not
-     * FIXED: have any where-condition.
-     * FIXED: So check that in the following for-loop.
-     *        if (addWhere) {
-     *            myStatement.append(" WHERE ");
-     *        } else {
-     *            needAnd = true;
-     *        }
+    /**
+     * Creates the limitation syntax optimisation stub
+     * to embed inside the SQL command that performs
+     * search and retrieve.
+     * <p/>
+     * <p>This method takes the limitation syntax string
+     * and performs a string replacement on the following
+     * tokens
+     * <p/>
+     * <ul>
+     * <p/>
+     * <li><b>%offset%</b><li><br>
+     * the number of rows in the <code>ResultSet</code> to skip
+     * before reading the data.
+     * <p/>
+     * <li><b>%maxrecord%</b><li><br>
+     * the maximum number of rows to read from  the <code>ResultSet</code>.
+     * Also known as the <i>rowlength</i>.
+     * <p/>
+     * <li><b>%endrecord%</b><li><br>
+     * the last record of in the <code>ResultSet</code> that the
+     * search and retrieved should retrieve. The end record number
+     * is equal to <code>( %offset% + %maxrecord% - 1 )</code>
+     * <p/>
+     * </ul>
+     * <p/>
+     * </p>
+     * <p/>
+     * <p/>
+     * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
+     *
+     * @param theConnection the db connection to make this stub from
+     * @return the limitation syntax stub string
+     * @see #searchAndRetrieveList()
+     * @see #setOffsetRecord( int )
+     * @see #setMaxRecords( int )
      */
+    protected String makeLimitationStub(DBConnection theConnection) {
+        String limit = theConnection.getLimitationSyntax();
+        int offset = this.getOffsetRecord();
+        int maxrec = this.getMaxRecords();
+        int endrec = offset + maxrec - 1;
+        limit = StringUtil.replace(limit, "%offset%", Integer.toString(offset));
+        limit = StringUtil.replace(limit, "%maxrecords%",
+                Integer.toString(maxrec));
 
-    // ----------------------------------------------------
-    // 2003-11-05 /ebn-ma: changed...
-    // If originalJoins is not empty, we are working with joins and
-    // define our relations in the from clause.
-    // In this case ignore all accidentially defined relations...
-    if (originalJoins.isEmpty()) {
-      String oneRelation = null;
+        // limit = StringUtil.replace( limit, "%length%",    Integer.toString( maxrec ) );
+        limit = StringUtil.replace(limit, "%endrecord%",
+                Integer.toString(endrec));
 
-      for (Enumeration e = relations.elements(); e.hasMoreElements(); ) {
-        /**
-         * FIXED: ma 17.10.2003  Here is a better
-         * FIXED: place for addWhere...
-         */
-        if (addWhere) {
-          myStatement.append(" WHERE ");
-          addWhere = false;
+        return limit;
+    }
+
+    /* makeLimitationStub(DBConnection) */
+
+    private static final String thisClass = MultiDBObject.class.getName() + ".";
+
+    /**
+     * Hash that contains the DB objects that relate to make up this query,
+     * indexed by the "short" name provided by the user.
+     */
+    private Hashtable myDBObjects = new Hashtable();
+
+    /**
+     * Vector to hold the "relations" between the different db objects that
+     * make up this query
+     */
+    private Vector relations = new Vector();
+    private String dbName = null;
+
+    /**
+     * This flag tells the buildWhereClause method(s) to either be case
+     * sensitive (normal queries) or to be case insensitive. The case
+     * insensitive query is useful when you want to do a search and retreive
+     * and want case insensitive matching.
+     */
+    private boolean caseSensitiveQuery = true;
+
+    /**
+     * broken into a vector
+     */
+    private Vector sortKeys = new Vector();
+
+    /**
+     * The vector of MultiDB objects retrieved by the last searchAndRetrieve method
+     */
+    private List recordSet = new ArrayList();
+
+    /**
+     * If we are using a custom where clause for this query, it's stored here. If
+     * null, then build the where clause
+     */
+    private String customWhereClause = null;
+
+    /**
+     * Hold the relations in "original" form - for the getThisMultiDBObj method
+     */
+    private Vector originalRelations = new Vector();
+
+    /**
+     * Local connection that we use if it's initialized, but
+     * if it's null we generate our own connection
+     */
+    protected DBConnection localConnection = null;
+
+    /* The max number of records we retrieve in a searchAndRetrieve.
+     * 0 means no limit
+     */
+    protected int maxRecords = 0;
+    private static Logger log = Logger.getLogger(MultiDBObject.class);
+
+    /**
+     * MultiDBObject constructor. calls setupFields.
+     */
+    public MultiDBObject() throws DBException {
+        setupFields();
+    }
+
+    /* MultiDBObject() */
+
+    /**
+     * MultiDBObject constructor which sets dbname from request
+     */
+    public MultiDBObject(ControllerRequest request) throws DBException {
+        this();
+        setDataContext(request.getDataContext());
+    }
+
+    /* MultiDBObject() */
+
+    /**
+     * Add a DB Object to the objects being used for this multidbobj query. The
+     * object is specified with a full classname, then a "short" name used to refer
+     * to it in this object. For example, the class name might be
+     * com.jcorporate.expresso.services.dbobj.SchemaList, the short name might be
+     * "schema".
+     *
+     * @param dbobjClassName java.lang.String
+     * @param shortName      java.lang.String
+     */
+    public void addDBObj(String dbobjClassName, String shortName) throws
+            DBException {
+        String myName = thisClass + "addDBObj(String, String)";
+
+        if (StringUtil.notNull(dbobjClassName).equals("")) {
+            throw new DBException(myName + ":Must specify a class name");
         }
-        else {
-          needAnd = true;
+        if (StringUtil.notNull(shortName).equals("")) {
+            throw new DBException(myName + ":Must specify a short name");
         }
 
-        oneRelation = (String) e.nextElement();
+        DBObject oneDBObj = null;
 
-        if (needAnd) {
-          myStatement.append(" AND ");
+        try {
+            Class c = ClassLocator.loadClass(dbobjClassName);
+            oneDBObj = (DBObject) c.newInstance();
+        } catch (ClassNotFoundException cn) {
+            throw new DBException(myName + ":DBObject '" + dbobjClassName +
+                    "' not found", cn);
+        } catch (InstantiationException ie) {
+            throw new DBException(myName + ":DBObject '" + dbobjClassName +
+                    "' cannot be instantiated", ie);
+        } catch (IllegalAccessException iae) {
+            throw new DBException(myName +
+                    ":Illegal access loading DBObject '" +
+                    dbobjClassName + "'", iae);
         }
 
-        myStatement.append(oneRelation);
-        needAnd = true;
-      }
+        addDBObj(oneDBObj, shortName);
     }
-    // 2003-11-05 /ebn-ma: ...changed
-    // ----------------------------------------------------
 
-    if (log.isDebugEnabled()) {
-      log.debug(myStatement.toString());
-    }
+    public void addDBObj(DBObject oneDBObj, String shortName) throws DBException {
+        StringUtil.assertNotBlank(shortName, "Short name cannot be blank here");
+        oneDBObj.setAttribute(SHORT_NAME, shortName);
+        myDBObjects.put(shortName, oneDBObj);
 
-    return myStatement.toString();
-  }
+        if (getDataContext() == null) {
+            setDataContext(oneDBObj.getDataContext());
+        }
 
-  /* buildWhereClause(boolean) */
+        /* Check to see if all of the db objects are in the same database */
+        oneDBObj.setDataContext(getDataContext());
 
-  /**
-   * Insert the method's description here.
-   * <p/>
-   * Creation date: (10/3/00 10:48:12 AM)
-   *
-   * @throws com.jcorporate.expresso.core.db.DBException
-   *          The exception description.
-   */
-  public void clear() throws com.jcorporate.expresso.core.db.DBException {
-    DBObject oneObj = null;
-
-    for (Enumeration eachObj = myDBObjects.elements();
-         eachObj.hasMoreElements(); ) {
-      oneObj = (DBObject) eachObj.nextElement();
-      oneObj.clear();
+        if (!oneDBObj.getDataContext().equals(getDataContext())) {
+            throw new DBException("DB Object '" +
+                    oneDBObj.getClass().getName() +
+                    "' was mapped to db/context '" +
+                    oneDBObj.getDataContext() +
+                    "', but this MultiDBObject is in context '" +
+                    getDataContext() + "'");
+        }
     }
 
+    /* addDBOj(String, String) */
+
     /**
-     * FIXED: ma 17.10.2003  Kill the fromClause here!
-     */
-    fromClause = null;
-
-  }
-
-  /* clear() */
-
-  /**
-   * Return the name of the context/database connection that this DB object is
-   * using. If none is set, then we are using the "default" database/context.
-   *
-   * @return the name of the datacontext
-   * @deprecated 12/2004; use getDataContext instead
-   */
-  public synchronized String getDBName() {
-    return getDataContext();
-  }
-
-  /* getDBName() */
-
-  /**
-   * Return the name of the context/database connection that this DB object is
-   * using. If none is set, then we are using the "default" database/context.
-   *
-   * @return the name of the datacontext
-   */
-  public synchronized String getDataContext() {
-    // set from RequestRegistry if this is first access
-    if (dbName == null || dbName.length() == 0) {
-      try {
-        this.setDataContext(RequestRegistry.getDataContext());
-      }
-      catch (Exception ex) {
-        log.warn("Problem getting data context from Registry: " +
-                 ex.getClass().getName() + ", msg: " + ex.getMessage());
+     * Build and return a string consisting of an SQL 'where' clause
+     * using the current field values as criteria for the search. See
+     * setCustomWhereClause for information on specifying a more complex where clause.
+     *
+     * @param useAllFields True if all fields are to be used,
+     *                     false for only key fields
+     * @return The where clause to use in a query.
+     */
+    public String buildWhereClause(boolean useAllFields) throws DBException {
+        FastStringBuffer fsb = FastStringBuffer.getInstance();
         try {
-          setDataContext(DBConnection.DEFAULT_DB_CONTEXT_NAME);
+            return buildWhereClauseBuffer(useAllFields, fsb).toString();
+        } finally {
+            fsb.release();
         }
-        catch (DBException de) {
-          log.error("Unable to set database to 'default'", de);
-        }
-      }
-
     }
 
-    return dbName;
-  }
-
-  /**
-   * This returns the encapsulated instance of the 'model' DBObject
-   * that was provided in 'addDBObj()';
-   * <p/>
-   * After a searchAndRetrieveList(), each MultiDBObject in the list has,
-   * encapsulated, an object of each type which was previously added as a model.
-   * Fields of this encapsulated object have been filled from whatever was retrieved.
-   * The original query object has just the original model instance.
-   *
-   * @param shortName the shortname of the model object
-   * @return the instance encapsulated by this MultiDBObject
-   *         author Abhi
-   * @throws DBException upon error
-   * @see #addDBObj(String, String)
-   *      Creation date: (02/01/2002 9:33 AM)
-   */
-  public DBObject getDBObject(String shortName) throws DBException {
-
-    DBObject oneObj = (DBObject) myDBObjects.get(shortName);
-    if (oneObj == null) {
-      String myName = thisClass + "getDBObject(String)";
-      throw new DBException(myName + ":No such object as '"
-                            + shortName + "'");
-    }
-    return oneObj;
-  }
-
-  /* getDBObject(String) */
-
-  /**
-   * Get the actual DBField value specified by fieldname
-   * <p/>
-   * Creation date: (9/18/00 11:37:10 AM)
-   *
-   * @param shortName the shortname of the field
-   * @param fieldName name of the field to retrieve
-   * @return The value of the field/
-   */
-  public String getField(String shortName, String fieldName) throws DBException {
-    DBObject oneObj = (DBObject) myDBObjects.get(shortName);
-
-    if (oneObj == null) {
-      String myName = thisClass + "getField(String, String)";
-      throw new DBException(myName + ":No such object as '" + shortName +
-                            "'");
-    }
-
-    return oneObj.getField(fieldName);
-  }
-
-  /* getFields(String, STring) */
-
-  private DBObject getByShortName(String shortName) throws DBException {
-    StringUtil.assertNotBlank(shortName, "Short name may not be blank");
-
-    DBObject oneObj = (DBObject) myDBObjects.get(shortName);
-
-    if (oneObj == null) {
-      throw new DBException("No such object as '" + shortName + "'");
-    }
-
-    return oneObj;
-  }
-
-  public String getFieldDecimalFormatted(String shortName, String fieldName,
-                                         String formatPattern) throws
-      DBException {
-    return getByShortName(shortName).getFieldDecimalFormatted(fieldName,
-        formatPattern);
-  }
-
-  /* getFieldDecimalFormatted(String, String) */
-
-  public float getFieldFloat(String shortName, String fieldName) throws
-      DBException {
-    return getByShortName(shortName).getFieldFloat(fieldName);
-  }
-
-  /* getFieldFloat(String, String) */
-
-  /**
-   * Returns a double object
-   * author Peter Pilgrim <peterp at xenonsoft.demon.co.uk>
-   * date Wed Jul 24 19:36:37 BST 2002
-   */
-  public double getFieldDouble(String shortName, String fieldName) throws
-      DBException {
-    return getByShortName(shortName).getFieldDouble(fieldName);
-  }
-
-  /* getFieldDouble(String, String) */
-
-  /**
-   * Returns a BigDecimal object
-   * author Peter Pilgrim <peterp at xenonsoft.demon.co.uk>
-   * date Wed Jul 24 19:36:37 BST 2002
-   */
-  public BigDecimal getFieldBigDecimal(String shortName, String fieldName) throws
-      DBException {
-    return getByShortName(shortName).getFieldBigDecimal(fieldName);
-  }
-
-  /* getFieldBigDecimal(String, String) */
-
-  public Date getFieldDate(String shortName, String fieldName) throws
-      DBException {
-    return getByShortName(shortName).getFieldDate(fieldName);
-  }
-
-  /* getFieldDate(String, String) */
-
-  public int getFieldInt(String shortName, String fieldName) throws DBException {
-    return getByShortName(shortName).getFieldInt(fieldName);
-  }
-
-  public long getFieldLong(String shortName, String fieldName) throws
-      DBException {
-    return getByShortName(shortName).getFieldLong(fieldName);
-  }
-
-  /**
-   * Construct a new MultiDBObject
-   *
-   * @return new MultiDBObject
-   * @throws DBException upon error
-   */
-  protected MultiDBObject getThisMultiDBObj() throws DBException {
-    MultiDBObject newObj = new MultiDBObject();
-    newObj.setDataContext(getDataContext());
-
-    String oneKey = null;
-    DBObject oneDBObj = null;
-
-    for (Enumeration ek = myDBObjects.keys(); ek.hasMoreElements(); ) {
-      oneKey = (String) ek.nextElement();
-      oneDBObj = (DBObject) myDBObjects.get(oneKey);
-      newObj.addDBObj(oneDBObj.newInstance(), oneKey);
-    }
-
-    String oneRelation = null;
-
-    for (Enumeration er = originalRelations.elements();
-         er.hasMoreElements(); ) {
-      oneRelation = (String) er.nextElement();
-
-      StringTokenizer stk = new StringTokenizer(oneRelation, "|");
-      newObj.setForeignKey(stk.nextToken(), stk.nextToken(),
-                           stk.nextToken(), stk.nextToken());
-    }
+    /* buildWhereClause(boolean) */
 
     // ----------------------------------------------------
     // 2003-11-05 /ebn-ma: added...
-    // In the new Object we will also need this join-definitions.
-    String oneJoin = null;
-
-    for (Enumeration n = originalJoins.elements();
-         n.hasMoreElements(); ) {
-      oneJoin = (String) n.nextElement();
-
-      StringTokenizer stk = new StringTokenizer(oneJoin, "|");
-      String leftDbo = stk.nextToken();
-      String leftField = stk.nextToken();
-      String rightDbo = stk.nextToken();
-      String rightField = stk.nextToken();
-      String joinTyp = stk.nextToken().toLowerCase();
-
-      if ("left".equals(joinTyp)) {
-        newObj.setLeftJoin(leftDbo, leftField, rightDbo, rightField);
-      }
-      else if ("inner".equals(joinTyp)) {
-        newObj.setInnerJoin(leftDbo, leftField, rightDbo, rightField);
-      }
-      else if ("right".equals(joinTyp)) {
-        newObj.setRightJoin(leftDbo, leftField, rightDbo, rightField);
-      }
+    // Method is overwritten for to support a new parameter dboAlias.
+    /**
+     * Build and return a string consisting of an SQL 'where' clause
+     * using the current field values as criteria for the search. See
+     * setCustomWhereClause for information on specifying a more complex where clause.
+     *
+     * @param useAllFields True if all fields are to be used, false for only key fields
+     * @return java.lang.String.
+     */
+    protected String buildWhereClauseBuffer(boolean useAllFields,
+                                            FastStringBuffer myStatement) throws
+            DBException {
+        return buildWhereClauseBuffer(useAllFields, myStatement, " ").toString();
     }
+
     // 2003-11-05 /ebn-ma: ...added
     // ----------------------------------------------------
 
-    return newObj;
-  }
+    // ----------------------------------------------------
+    // 2003-11-05 /ebn-ma: changed...
+    // Method supports a new parameter dboAlias. With the help of this parameter
+    // we build selective clauses for a single DBObject.
+    // These clauses are used in method buildJoin.
+    /**
+     * Build and return a string consisting of an SQL 'where' clause
+     * using the current field values as criteria for the search. See
+     * setCustomWhereClause for information on specifying a more complex where clause.
+     *
+     * @param useAllFields True if all fields are to be used, false for only key fields
+     * @param myStatement  Buffer for to build the new where-clause.
+     * @param dboAlias     Build the where-clause for this DBObject. If empty,
+     *                     work with all DBObjects that are not marked to be ignored.
+     * @return java.lang.String.
+     */
+    protected String buildWhereClauseBuffer(boolean useAllFields,
+                                            FastStringBuffer myStatement,
+                                            String dboAlias) throws DBException {
 
-  /* getThisMultiDBObj() */
+        /* list of field names (with table names prefixed) */
+        Vector fields = new Vector();
+        DBObject oneObj = null;
+        String fieldName = null;
+        Hashtable byTableName = new Hashtable();
 
-  /**
-   * Search and retrieve in a particular order
-   *
-   * @param sortKeyString A pipe-delimited list of key fields to sort
-   *                      the returned set by
-   * @return Vector A vector of new database objects retrieved by the search
-   * @throws DBException If the search could not be completed
-   * @deprecated Use searchAndRetrieveList(String) instead
-   */
-  public synchronized Vector searchAndRetrieve(String sortKeyString) throws
-      DBException {
-
-    return new Vector(searchAndRetrieveList(sortKeyString));
-  }
-
-  /* searchAndRetrieve(String) */
-
-  /**
-   * Search and retrieve in a particular order
-   *
-   * @param sortKeyString A pipe-delimited list of key fields to sort
-   *                      the returned set by
-   * @return A list of new database objects retrieved by the search
-   * @throws DBException If the search could not be completed
-   */
-  public synchronized List searchAndRetrieveList(String sortKeyString) throws
-      DBException {
-
-    sortKeys.clear();
-    if (sortKeyString != null) {
-      StringTokenizer stk = new StringTokenizer(sortKeyString, "|");
-      while (stk.hasMoreTokens()) {
-        sortKeys.addElement(stk.nextToken());
-      }
-    }
-
-    return searchAndRetrieveList();
-  }
-
-  /**
-   * Search and retrieve in a particular order
-   *
-   * @return A list of new database objects retrieved by the search
-   * @throws DBException If the search could not be completed
-   */
-  public synchronized List searchAndRetrieveList() throws DBException {
-
-    DBObject oneObj = null;
-
-    HashMap rtrvListByTable = new HashMap();
-
-    DBConnectionPool myPool = null;
-    DBConnection myConnection = null;
-
-    try {
-      if (localConnection != null) {
-        myConnection = localConnection;
-      }
-      else {
-        myPool = DBConnectionPool.getInstance(getDataContext());
-        myConnection = myPool.getConnection(
-            "com.jcorporate.expresso.core.dbobj.DBObject");
-      }
-
-      if (recordSet == null) {
-        String myName = (thisClass + "searchAndRetrieve()");
-        throw new DBException(myName +
-                              ":Database object not correctly initialized");
-      }
-
-      recordSet.clear();
-
-      myConnection.execute(getSQLSelectStatement(myPool, myConnection));
-
-      int returnRecordCount = 0;
-      int loopCount = 0;
-      while (myConnection.next()) {
-        loopCount++;
-
-        //If there's limitation syntax on, then the first record will be the
-        //maximum record.
-        if (loopCount <= offsetRecord && offsetRecord > 0 &&
-            myConnection.getLimitationPosition() ==
-            DBConnection.LIMITATION_DISABLED) {
-          continue;
-        }
-
-        returnRecordCount++;
-
-        // maxRecords = 0 by default, so I guess this means 0 doesn't count as max number... should default to -1 ??  LAH 12/03
-        if ( (returnRecordCount > maxRecords) && (maxRecords > 0)) {
-          break;
+        if (useAllFields) {
+            for (Enumeration eachObj = myDBObjects.elements();
+                 eachObj.hasMoreElements();) {
+                oneObj = (DBObject) eachObj.nextElement();
+                byTableName.put(getTableName(oneObj), oneObj);
+
+                // ----------------------------------------------------
+                // 2003-11-05 /ebn-ma: added...
+                // Two cases:
+                // 1. If dboAlias tells us to compute the on-clause of a join,
+                //    then we will ignore every DBObject that is not the one
+                //    specified in dboAlias.
+                // 2. If dboAlias is empty, we are computing the where-clause of
+                //    our sql-statement.
+                //    In this case we ignore the current DBObject, if it is a
+                //    member of ignoreInWhereClause.
+                String thisDbo = (String) oneObj.getAttribute(SHORT_NAME);
+
+                if (dboAlias.trim().length() > 0) {
+                    if (!thisDbo.equals(dboAlias)) {
+                        continue;
+                    }
+                } else if (ignoreInWhereClause.indexOf("," + thisDbo + ",") >= 0) {
+                    continue;
+                }
+                // 2003-11-05 /ebn-ma: ...added
+                // ----------------------------------------------------
+
+                for (Iterator i = oneObj.getMetaData().getFieldListArray().iterator();
+                     i.hasNext();) {
+                    fieldName = (String) i.next();
+
+                    if (!oneObj.getMetaData().getFieldMetadata(fieldName).isVirtual()) {
+                        fields.addElement(getTableName(oneObj) + "." +
+                                fieldName);
+                    }
+                }
+                /* for each field */
+            }
+        } else { /* for each db object */
+            for (Enumeration eachObj = myDBObjects.elements();
+                 eachObj.hasMoreElements();) {
+                oneObj = (DBObject) eachObj.nextElement();
+
+                // ----------------------------------------------------
+                // 2003-11-05 /ebn-ma: added...
+                // Two cases:
+                // 1. If dboAlias tells us to compute the on-clause of a join,
+                //    then we will ignore every DBObject that is not the one
+                //    specified in dboAlias.
+                // 2. If dboAlias is empty, we are computing the where-clause of
+                //    our sql-statement.
+                //    In this case we ignore the current DBObject, if it is a
+                //    member of ignoreInWhereClause.
+                String thisDbo = (String) oneObj.getAttribute(SHORT_NAME);
+
+                if (dboAlias.trim().length() > 0) {
+                    if (!thisDbo.equals(dboAlias)) {
+                        continue;
+                    }
+                } else if (ignoreInWhereClause.indexOf("," + thisDbo + ",") >= 0) {
+                    continue;
+                }
+                // 2003-11-05 /ebn-ma: ...added
+                // ----------------------------------------------------
+
+                for (Iterator i = oneObj.getKeyFieldListIterator();
+                     i.hasNext();) {
+                    fieldName = (String) i.next();
+
+                    if (!oneObj.getMetaData().getFieldMetadata(fieldName).isVirtual()) {
+                        fields.addElement(getTableName(oneObj) + "." +
+                                fieldName);
+                    }
+                }
+                /* for each field */
+            }
+            /* for each db object */
         }
 
-        if (log.isDebugEnabled()) {
-          log.debug("Returning row " + loopCount);
-        }
+        /* Now go thru each field - if it is non-empty, add it's criteria */
 
-        MultiDBObject myObj = getThisMultiDBObj();
+        /* to the where clause. If it is empty, just skip to the next one */
+        boolean addWhere = true;
+        boolean addAnd = false;
+        String oneFieldName = null;
+        String oneTableName = null;
+        String oneFullName = null;
         String oneFieldValue = null;
-        int i = 1;
-
-        for (Enumeration eachObj = myDBObjects.elements();
-             eachObj.hasMoreElements(); ) {
-          oneObj = (DBObject) eachObj.nextElement();
+        boolean skipText = false;
 
-          ArrayList retrievedFieldList = (ArrayList) rtrvListByTable.get(
-              getTableName(oneObj));
-          // The following should never happen .... but CYA
-          if (retrievedFieldList == null) {
-            retrievedFieldList = new ArrayList();
-          }
-          for (Iterator it = retrievedFieldList.listIterator();
-               it.hasNext(); ) {
-            java.lang.String fieldName = (String) it.next();
-            DataFieldMetaData metaData = oneObj.getFieldMetaData(fieldName);
-
-            if (!metaData.isVirtual() && !metaData.isBinaryObjectType()) {
-              try {
-                oneFieldValue = myConnection.getString(i);
-              }
-              catch (DBException de) {
-                String myName = (thisClass + "searchAndRetrieve()");
-                throw new DBException(myName +
-                                      ":Error retrieving field '" +
-                                      getTableName(oneObj) +
-                                      "." + fieldName + "'",
-                                      de);
-              }
-
-              i++;
-
-              if (log.isDebugEnabled()) {
-                log.debug("Setting " +
-                          getTableName(oneObj) + "." +
-                          fieldName + " to " + oneFieldValue);
-              }
-
-              myObj.setField( (String) oneObj.getAttribute(SHORT_NAME),
-                             fieldName, oneFieldValue);
-            }
-          }
-        }
-        /* each db object */
-
-        myObj.setDataContext(getDataContext());
-        recordSet.add(myObj);
-      }
-
-      /* each row retrieved from the db */
-    }
-    finally {
-      if (localConnection == null) {
-        myPool.release(myConnection);
-      }
-    }
-
-    return recordSet;
-  }
-
-  /**
-   * get the SQL select statment to be executed, if searchAnd RetrieveList happenned
-   * on this MULTIDBOBJECT
-   *
-   * @return String of select statement to be executed
-   * @throws DBException If the search could not be completed
-   */
-  public synchronized String getSQLSelectStatement() throws DBException {
-    DBConnectionPool myPool = null;
-    DBConnection myConnection = null;
-    try {
-      if (localConnection != null) {
-        myConnection = localConnection;
-      }
-      else {
-        myPool = DBConnectionPool.getInstance(getDBName());
-        myConnection = myPool.getConnection(
-            "com.jcorporate.expresso.core.dbobj.DBObject");
-      }
-      return getSQLSelectStatement(myPool, myConnection);
-    }
-    finally {
-      if (localConnection == null) {
-        myPool.release(myConnection);
-      }
-    }
-  }
-
-  /**
-   * get the SQL select statment to be executed, if searchAnd RetrieveList happenned
-   * on this MULTIDBOBJECT
-   * The use of this method is when building complex custom WHERE clauses
-   * with nested queries one can call it to get the nested select built dynamically
-   *
-   * @return String of select statement to be executed
-   * @throws DBException If the search could not be completed
-   * author Yves Henri AMAIZO
-   */
-  public synchronized String getSQLSelectStatement(DBConnectionPool myPool,
-      DBConnection myConnection) throws DBException {
-
-    boolean needComma = false;
-    DBObject oneObj = null;
-
-    HashMap rtrvListByTable = new HashMap();
-
-    String fieldName = null;
-    FastStringBuffer myStatement = new FastStringBuffer(256);
-    try {
-      myStatement.append("SELECT ");
-
-      if (myConnection.getLimitationPosition() ==
-          DBConnection.LIMITATION_AFTER_SELECT &&
-          (offsetRecord > 0 || maxRecords > 0)) {
-
-        // Insert limitation stub after table nomination
-        String limitStub = makeLimitationStub(myConnection);
-
-        myStatement.append(" ");
-        myStatement.append(limitStub);
-        myStatement.append(" ");
-      }
-
-      if (selectDistinct) {
-        myStatement.append(" ");
-        myStatement.append("DISTINCT");
-        myStatement.append(" ");
-      }
-
-      for (Enumeration eachObj = myDBObjects.elements();
-           eachObj.hasMoreElements(); ) {
-        oneObj = (DBObject) eachObj.nextElement();
-
-        ArrayList retrievedFieldList = (ArrayList) rtrvListByTable.get(
-            getTableName(oneObj));
-        if (retrievedFieldList == null) {
-          retrievedFieldList = new ArrayList();
-          rtrvListByTable.put(getTableName(oneObj), retrievedFieldList);
-        }
-
-        if (oneObj.anyFieldsDistinct) {
-          String oneFieldName = null;
-          for (Iterator i = oneObj.getDistinctFieldArrayList().iterator();
-               i.hasNext();
-              ) {
-            oneFieldName = (String) i.next();
-            if (needComma) {
-              myStatement.append(", ");
-            }
-            myStatement.append(" ");
-            myStatement.append(myPool.getDistinctRowsetKeyword());
-            myStatement.append(" ");
-            myStatement.append(selectFieldString(oneObj, oneFieldName));
-            retrievedFieldList.add(oneFieldName);
-            needComma = true;
-          }
-        }
-        else if (oneObj.anyFieldsToRetrieveMulti) {
-          String oneFieldName = null;
-          for (Iterator i = oneObj.getFieldsToRetrieveIterator();
-               i.hasNext();
-              ) {
-            oneFieldName = (String) i.next();
-            if (needComma) {
-              myStatement.append(", ");
-            }
-            myStatement.append(selectFieldString(oneObj, oneFieldName));
-            retrievedFieldList.add(oneFieldName);
-            needComma = true;
-          }
-          /* for each field */
-        }
-        else {
-          for (Iterator i = oneObj.getMetaData().getFieldListArray().iterator();
-               i.hasNext();
-              ) {
-            fieldName = (String) i.next();
-            DataFieldMetaData metaData =
-                oneObj.getFieldMetaData(fieldName);
-
-            if (!metaData.isVirtual()
-                && !metaData.isBinaryObjectType()) {
-              if (needComma) {
-                myStatement.append(", ");
-              }
-
-              myStatement.append(selectFieldString(oneObj, fieldName));
-              retrievedFieldList.add(fieldName);
-              needComma = true;
-            }
-
-            /* if field is not virtual & not binary*/
-          }
-
-          /* for each field */
-        }
-
-      }
-
-      myStatement.append(" FROM ");
-      if (buildFromClause() &&
-          fromClause != null &&
-          fromClause.trim().length() > 0) {
-        myStatement.append(fromClause);
-      }
-      else {
-        throw new DBException(thisClass + "count()" +
-                              " :Building of FROM clause failed.");
-      }
-      // 2003-11-05 /ebn-ma: ...changed
-      // ----------------------------------------------------
-
-      if (myConnection.getLimitationPosition() ==
-          DBConnection.LIMITATION_AFTER_TABLE &&
-          (offsetRecord > 0 || maxRecords > 0)) {
-
-        // Insert limitation stub after table nomination
-        String limitStub = makeLimitationStub(myConnection);
-        myStatement.append(" ");
-        myStatement.append(limitStub);
-        myStatement.append(" ");
-      }
-
-      String whereClause;
-      if (customWhereClause != null) {
-        if (appendCustomWhereClause) {
-          whereClause = buildWhereClause(true) + " AND " + customWhereClause;
-        }
-        else {
-          whereClause = " WHERE " + customWhereClause;
-        }
-      }
-      else {
-        whereClause = buildWhereClause(true);
-      }
-
-      myStatement.append(whereClause);
-
-      appendCustomWhereClause = false;
-
-      if (myConnection.getLimitationPosition() ==
-          DBConnection.LIMITATION_AFTER_WHERE &&
-          (offsetRecord > 0 || maxRecords > 0)) {
-
-        // Insert limitation stub after table nomination
-        String limitStub = makeLimitationStub(myConnection);
-
-        if (whereClause.length() > 0) {
-          myStatement.append(" AND");
-        }
-
-        myStatement.append(" ");
-        myStatement.append(limitStub);
-        myStatement.append(" ");
-      }
-
-      /* Add the ORDER BY clause if any sortKeys are specified */
-      if (sortKeys.size() > 0) {
-        myStatement.append(" ORDER BY ");
-
-        boolean needComma2 = false;
-
-        for (Enumeration e = sortKeys.elements(); e.hasMoreElements(); ) {
-          if (needComma2) {
-            myStatement.append(", ");
-          }
-
-          myStatement.append( (String) e.nextElement());
-          needComma2 = true;
-        }
-      }
-
-      if (myConnection.getLimitationPosition() ==
-          DBConnection.LIMITATION_AFTER_ORDER_BY &&
-          (offsetRecord > 0 || maxRecords > 0)) {
-        myStatement.append(" ");
-        myStatement.append(makeLimitationStub(myConnection));
-      }
-
-      if (log.isDebugEnabled()) {
-        log.debug("Executing " + myStatement.toString());
-      }
-
-      return myStatement.toString();
-    }
-    finally {
-      myStatement.release();
-    }
-  }
-
-  /**
-   * This tells the buildWhereClause to either respect case (true) or
-   * ignore case (false). You can call this method before doing a search and
-   * retreive if you want to match without worrying about case. For example
-   * if you where to call this method with isCaseSensitiveQuery = FALSE
-   * then this comparison would match in the search:
-   * <p/>
-   * vendor_name actual value = "My Name"
-   * <p/>
-   * query value = "my name"
-   * <p/>
-   * This would match in a search and retrieve.
-   * <p/>
-   * author Adam Rossi, PlatinumSolutions
-   *
-   * @param isCaseSensitiveQuery boolean
-   */
-  public void setCaseSensitiveQuery(boolean isCaseSensitiveQuery) {
-    caseSensitiveQuery = isCaseSensitiveQuery;
-  }
-
-  /**
-   * Specify a custom "where" clause for the SQL used to retrieve records for
-   * this object. The where clause 'reset' after each call to searchAndRetrieve()
-   * or other retrieval methods, so it must be set just before the call to
-   * retrieve the records is made. If no custom where clause is specified by this
-   * method, the where clause is built from the field values in the object.
-   * <p/>
-   * DO NOT INCLUDE THE 'where' KEYWORD. Just what comes after the 'where'
-   *
-   * @param newCustomWhere java.lang.String
-   */
-  public synchronized void setCustomWhereClause(String newCustomWhere) {
-    setCustomWhereClause(newCustomWhere, false);
-  }
-
-  /* setCustomWhereClause(String) */
-
-  /**
-   * Specify a custom "where" clause for the SQL used to retrieve records for
-   * this object. The where clause 'reset' after each call to searchAndRetrieve()
-   * or other retrieval methods, so it must be set just before the call to
-   * retrieve the records is made. If no custom where clause is specified by this
-   * method, the where clause is built from the field values in the object.
-   *
-   * @param newCustomWhere java.lang.String
-   * @param append         true if the custom where clause is to be appended to the 'built'
-   *                       where clause; THIS IS DIFFERENT THAN DBObject where 'append' means to append another condition onto the custom where clause
-   */
-  public synchronized void setCustomWhereClause(String newCustomWhere,
-                                                boolean append) {
-    customWhereClause = newCustomWhere;
-    appendCustomWhereClause = append;
-  }
-
-  /* setCustomWhereClause(String, boolean) */
-
-  // ----------------------------------------------------
-  // 2003-11-05 /ebn-ma: added...
-  /**
-   * Gets the customWhereClause
-   *
-   * @return customFromClause
-   */
-  public String getCustomWhereClause() {
-    return customWhereClause;
-  }
-
-  /**
-   * Specify if the customWhereClause should be appended to a generated
-   * whereClause.
-   *
-   * @param newValue true = append to whereClause, else overwrite it.
-   */
-  public synchronized void setAppendCustomWhereClause(boolean newValue) {
-    appendCustomWhereClause = newValue;
-  }
-
-  /**
-   * Gets the settings of appendCustomWhereClause.
-   *
-   * @return appendCustomWhereClause
-   */
-  public boolean getAppendCustomWhereClause() {
-    return appendCustomWhereClause;
-  }
-
-  // 2003-11-05 /ebn-ma: ...added
-  // ----------------------------------------------------
-
-  /**
-   * Set the database name/context for this multi db object. If setDataContext is not called,
-   * the "default" db name and context is used. See
-   * com.jcorporate.expresso.core.misc.ConfigManager for information about multiple
-   * contexts. Note that setting a db/context name only affects the object when it
-   * allocates it's own db connections - if a specific connection is used (via the
-   * setConnection(DBConnection) method) then that connection must be already
-   * associated with the correct db/context.
-   *
-   * @param newOther The name of the context or database to use
-   * @deprecated 12/2004 v5.6; use setDataContext() instead
-   */
-  public synchronized void setDBName(String newOther) throws DBException {
-    dbName = newOther;
-  }
-
-  /* setDBName(String) */
-
-  /**
-   * Set the database name/context for this multi db object. If setDataContext is not called,
-   * the "default" db name and context is used. See
-   * com.jcorporate.expresso.core.misc.ConfigManager for information about multiple
-   * contexts. Note that setting a db/context name only affects the object when it
-   * allocates it's own db connections - if a specific connection is used (via the
-   * setConnection(DBConnection) method) then that connection must be already
-   * associated with the correct db/context.
-   *
-   * @param context The name of the context or database to use
-   */
-  public synchronized void setDataContext(String context) throws DBException {
-    dbName = context;
-  }
-
-  /* setDataContext(String) */
-
-  /**
-   * Specify that the DISTINCT keyword for unique rows must be specified right
-   * after the SELECT keyword
-   *
-   * @param flag true if DISTINCT supported right after SELECT, false (default) otherwise.
-   */
-  public void setSelectDistinct(boolean flag) {
-    selectDistinct = flag;
-  }
-
-  /**
-   * Specify a select list of fields to retrieve from a particular DBObject component
-   *
-   * @param shortName  The alias for the DBObject
-   * @param fieldNames Pipe("|")-separated list of fieldnames
-   */
-  public void setFieldsToRetrieve(String shortName, String fieldNames) throws
-      DBException {
-    DBObject oneObj = (DBObject) myDBObjects.get(shortName);
-
-    if (oneObj == null) {
-      String myName = thisClass + "setFieldsToRetrieve(String, String)";
-      throw new DBException(myName + ":No such object as '" + shortName +
-                            "'");
-    }
-
-    oneObj.setFieldsToRetrieve(fieldNames);
-  }
-
-  /**
-   * Specify to retrieve NO fields from a particular DBObject component
-   * <p/>
-   * author Zaz Harris, SRI International
-   *
-   * @param shortName The alias for the DBObject
-   */
-  public void setFieldsToRetrieveToNone(String shortName) throws DBException {
-    DBObject oneObj = (DBObject) myDBObjects.get(shortName);
-
-    if (oneObj == null) {
-      String myName = thisClass + "setFieldsToRetrieveToNone(String)";
-      throw new DBException(myName + ":No such object as '" + shortName +
-                            "'");
-    }
-
-    oneObj.retrieveFields = null;
-    oneObj.anyFieldsToRetrieveMulti = true;
-  }
-
-  /**
-   * Specify a field to be retieved uniquely froma component DBObject
-   *
-   * @param shortName The alias for the DBObject
-   * @param fieldName The field to mark as unique
-   * @param flag      true=distinct, flase=all matching rows
-   */
-  public void setFieldDistinct(String shortName, String fieldName, boolean flag) throws
-      DBException {
-    DBObject oneObj = (DBObject) myDBObjects.get(shortName);
-
-    if (oneObj == null) {
-      String myName = thisClass + "setFieldDistinct(String, String, boolean)";
-      throw new DBException(myName + ":No such object as '" + shortName +
-                            "'");
-    }
-
-    oneObj.setFieldDistinct(fieldName, flag);
-  }
-
-  /**
-   * Insert the method's description here.
-   * <p/>
-   * Creation date: (9/18/00 11:37:10 AM)
-   *
-   * @param shortName  the short name to set
-   * @param fieldName  the fieldname to set
-   * @param fieldValue the value to set the field at.
-   */
-  public void setField(String shortName, String fieldName, String fieldValue) throws
-      DBException {
-    DBObject oneObj = (DBObject) myDBObjects.get(shortName);
-
-    if (oneObj == null) {
-      String myName = thisClass + "setField(String, String, String)";
-      throw new DBException(myName + ":No such object as '" + shortName +
-                            "'");
-    }
-
-    oneObj.setField(fieldName, fieldValue);
-  }
-
-  /* setField(String, String, String) */
-
-  /**
-   * Insert the method's description here.
-   * <p/>
-   * Creation date: (9/18/00 11:36:28 AM)
-   *
-   * @param shortName  java.lang.String
-   * @param foreignKey java.lang.String
-   * @param shortName2 java.lang.String
-   * @param primaryKey java.lang.String
-   */
-  public void setForeignKey(String shortName, String foreignKey,
-                            String shortName2, String primaryKey) throws
-      DBException {
-    String myName = thisClass +
-        "setForeignKey(String, String, String, String)";
-    DBObject foreignDBObj = (DBObject) myDBObjects.get(shortName);
-
-    if (foreignDBObj == null) {
-      throw new DBException(myName + ":DB Object with short name '" +
-                            shortName + "' is not part of this query");
-    }
-
-    DBObject primaryDBObj = (DBObject) myDBObjects.get(shortName2);
-
-    if (primaryDBObj == null) {
-      throw new DBException(myName + ":DB Object with short name '" +
-                            shortName2 + "' is not part of this query");
-    }
-
-    relations.addElement(getTableName(foreignDBObj) + "." + foreignKey +
-                         " = " + getTableName(primaryDBObj) + "." +
-                         primaryKey);
-    originalRelations.addElement(shortName + "|" + foreignKey + "|" +
-                                 shortName2 + "|" + primaryKey);
-  }
-
-  /* setForeignKey(String, String, String, String) */
-
-  /**
-   * Builds a 'FROM' clause using the 'INNER JOIN' syntax.
-   *
-   * @param leftShortName  short name for the left hand table.
-   * @param leftColumn     name of the column from the left hand table for the JOIN condition
-   * @param rightShortName short name for the right hand table.
-   * @param rightColumn    name of the column from the right hand table for the JOIN condition
-   */
-  public void setInnerJoin(String leftShortName, String leftColumn,
-                           String rightShortName, String rightColumn) throws
-      DBException {
+        try {
+            ConfigJdbc myConfig = ConfigManager.getJdbcRequired(getDataContext());
+            skipText = myConfig.skipText();
+        } catch (ConfigurationException ce) {
+            throw new DBException(ce);
+        }
+
+        boolean skipField = false;
+
+        for (Enumeration fieldsToUse = fields.elements();
+             fieldsToUse.hasMoreElements();) {
+            oneFullName = (String) fieldsToUse.nextElement();
+
+            StringTokenizer stk = new StringTokenizer(oneFullName, ".");
+            int stkCount = stk.countTokens();
+            if (stkCount == 2) {
+                oneTableName = stk.nextToken();
+                oneFieldName = stk.nextToken();
+            } else if (stkCount == 3) {
+                String oneSchemaName = stk.nextToken();
+                oneTableName = stk.nextToken();
+                oneFieldName = stk.nextToken();
+                oneTableName = oneSchemaName + "." + oneTableName;
+            }
 
-    // ----------------------------------------------------
-    // 2003-11-05 /ebn-ma: changed...
-    // We will build the from-clause at execution time.
-    // buildJoin(leftShortName, leftColumn, rightShortName, rightColumn, INNER_JOIN);
+            oneObj = (DBObject) byTableName.get(oneTableName);
+            skipField = false;
+            // ----------------------------------------------------
+            // 2003-11-18 /ebn-ma: changed...
+            // getField() makes a translation using the charset-filter.
+            // FieldRangeParser cannot succeed, if his control-characters
+            // are changed, e.g. '<' to '&lt'
+            // getFieldData() doesn't do this translation and so it works.
+            // Anyway: The corresponding code-block in JDBCUtil seems to be
 
-    originalJoins.addElement(leftShortName + "|" + leftColumn + "|" +
-                             rightShortName + "|" + rightColumn + "|inner");
-    // 2003-11-05 /ebn-ma: ...changed
-    // ----------------------------------------------------
-  }
 
-  /* setInnerJoin(String, String, String, String) */
+            // a better solution.
+            //  oneFieldValue =StringUtil.notNull(oneObj.getField(oneFieldName));
 
-  /**
-   * Builds a 'FROM' clause using the 'LEFT JOIN' syntax.
-   *
-   * @param leftShortName  short name for the left hand table.
-   * @param leftColumn     name of the column from the left hand table for the JOIN condition
-   * @param rightShortName short name for the right hand table.
-   * @param rightColumn    name of the column from the right hand table for the JOIN condition
-   */
-  public void setLeftJoin(String leftShortName, String leftColumn,
-                          String rightShortName, String rightColumn) throws
-      DBException {
 
-    // ----------------------------------------------------
-    // 2003-11-05 /ebn-ma: changed...
-    // We will build the from-clause at execution time.
-    // buildJoin(leftShortName, leftColumn, rightShortName, rightColumn, LEFT_JOIN);
+            oneFieldValue = StringUtil.notNull(oneObj.getFieldData(oneFieldName));
+            // 2003-11-18 /ebn-ma: ...changed
+            // ----------------------------------------------------
 
-    originalJoins.addElement(leftShortName + "|" + leftColumn + "|" +
-                             rightShortName + "|" + rightColumn + "|left");
-    // 2003-11-05 /ebn-ma: ...changed
-    // ----------------------------------------------------
-  }
+            String rangeString = oneObj.denotesRange(oneFieldValue);
 
-  /* setLeftJoin(String, String, String, String) */
+            if (!oneFieldValue.equals("")) {
+                oneFieldValue = oneObj.quoteIfNeeded(oneFieldName, rangeString);
+            }
+            if (oneFieldValue.equals("")) {
+                skipField = true;
+            }
+            if (oneFieldValue == null) {
+                skipField = true;
+            }
+            if (oneFieldValue.equals("\'\'")) {
+                skipField = true;
+            }
+            if ((oneObj.getMetaData().getType(oneFieldName).equals("text")) &&
+                    (skipText)) {
+                skipField = true;
+            }
+            if (!skipField) {
+                // check to see if the field value is valid (protects agains sql injection)
+                String unalteredFieldValue = oneObj.getDataField(oneFieldName).asString();
+                if (rangeString != null) {
+                    FieldRangeParser rangeParser = new FieldRangeParser();
+                    boolean valid = rangeParser.isValidRange(oneObj.getFieldMetaData(oneFieldName),
+                            unalteredFieldValue);
+                    if (!valid) {
+                        throw new DBException("Invalid field range value: " +
+                                unalteredFieldValue);
+                    }
+                } else if (oneObj.containsWildCards(oneFieldValue)) {
+                    Object origValue = oneObj.getDataField(oneFieldName).getValue();
+
+                    String[] wildcards = null;
+                    wildcards = (String[]) oneObj.getConnectionPool().getWildCardsList().
+                            toArray(new String[0]);
+                    Filter filter = new Filter(wildcards, wildcards);
+                    String valueWithoutWildCards = filter.stripFilter(unalteredFieldValue);
+                    // if the value without wildcards is empty, then we know the field is valid
+                    if (!valueWithoutWildCards.equals("")) {
+                        oneObj.getDataField(oneFieldName).setValue(valueWithoutWildCards);
+                        oneObj.getDataField(oneFieldName).checkValue();
+                        oneObj.getDataField(oneFieldName).setValue(origValue);
+                    }
+                } else {
+                    oneObj.getDataField(oneFieldName).checkValue();
+                }
+
+                if (addWhere) {
+                    myStatement.append(" WHERE ");
+                    addWhere = false;
+                }
+                if (addAnd) {
+                    myStatement.append(" AND ");
+                }
+                if (oneObj.containsWildCards(oneFieldValue)) {
+                    if (caseSensitiveQuery) {
+                        myStatement.append(getTableName(oneObj) + "." +
+                                oneFieldName);
+                        myStatement.append(" LIKE ");
+                        myStatement.append(oneFieldValue);
+                    } else {
+                        myStatement.append("UPPER(");
+                        myStatement.append(getTableName(oneObj) + "." +
+                                oneFieldName);
+                        myStatement.append(") LIKE ");
+                        myStatement.append(oneFieldValue.toUpperCase());
+                    }
+                } else if (rangeString != null) {
+                    myStatement.append(getTableName(oneObj) + "." +
+                            oneFieldName);
+                    myStatement.append(" " + rangeString + " ");
+                    myStatement.append(oneFieldValue);
+                } else {
+                    if (caseSensitiveQuery) {
+                        myStatement.append(getTableName(oneObj) + "." +
+                                oneFieldName);
+                        myStatement.append(" = ");
+                        myStatement.append(oneFieldValue);
+                    } else {
+                        myStatement.append("UPPER(");
+                        myStatement.append(getTableName(oneObj) + "." +
+                                oneFieldName);
+                        myStatement.append(") = ");
+                        myStatement.append(oneFieldValue.toUpperCase());
+                    }
+                }
 
-  /**
-   * Builds a 'FROM' clause using the 'LEFT JOIN' syntax.
-   *
-   * @param leftShortName  short name for the left hand table.
-   * @param leftColumn     name of the column from the left hand table for the JOIN condition
-   * @param rightShortName short name for the right hand table.
-   * @param rightColumn    name of the column from the right hand table for the JOIN condition
-   */
-  public void setRightJoin(String leftShortName, String leftColumn,
-                           String rightShortName, String rightColumn) throws
-      DBException {
+                addAnd = true;
+            }
 
-    // ----------------------------------------------------
-    // 2003-11-05 /ebn-ma: changed...
-    // We will build the from-clause at execution time.
-    // buildJoin(leftShortName, leftColumn, rightShortName, rightColumn, RIGHT_JOIN);
+            /* if field is not skipped for some reason */
+        }
 
-    originalJoins.addElement(leftShortName + "|" + leftColumn + "|" +
-                             rightShortName + "|" + rightColumn + "|right");
-    // 2003-11-05 /ebn-ma: ...changed
-    // ----------------------------------------------------
-  }
+        /* for each field */
+        boolean needAnd = false;
 
-  /* setRightJoin(String, String, String, String) */
+        /**
+         * FIXED: ma 17.10.2003
+         * FIXED: Don't do that at here. We do not need
+         * FIXED: WHERE, if we do not
+         * FIXED: have any where-condition.
+         * FIXED: So check that in the following for-loop.
+         *        if (addWhere) {
+         *            myStatement.append(" WHERE ");
+         *        } else {
+         *            needAnd = true;
+         *        }
+         */
 
-  /**
-   * Builds a 'FROM' clause using ANSI 'JOIN' syntax.
-   *
-   * @param leftShortName  short name for the left hand table.
-   * @param leftColumn     name of the column from the left hand table for the JOIN condition
-   * @param rightShortName short name for the right hand table.
-   * @param rightColumn    name of the column from the right hand table for the JOIN condition
-   * @param joinType       the type of join to be performed, i.e. LEFT, RIGHT or INNER.
-   */
-  private void buildJoin(String leftShortName, String leftColumn,
-                         String rightShortName, String rightColumn,
-                         int joinType) throws DBException {
+        // ----------------------------------------------------
+        // 2003-11-05 /ebn-ma: changed...
+        // If originalJoins is not empty, we are working with joins and
+        // define our relations in the from clause.
+        // In this case ignore all accidentially defined relations...
+        if (originalJoins.isEmpty()) {
+            String oneRelation = null;
+
+            for (Enumeration e = relations.elements(); e.hasMoreElements();) {
+                /**
+                 * FIXED: ma 17.10.2003  Here is a better
+                 * FIXED: place for addWhere...
+                 */
+                if (addWhere) {
+                    myStatement.append(" WHERE ");
+                    addWhere = false;
+                } else {
+                    needAnd = true;
+                }
+
+                oneRelation = (String) e.nextElement();
+
+                if (needAnd) {
+                    myStatement.append(" AND ");
+                }
 
-    // ----------------------------------------------------
-    // 2003-11-05 /ebn-ma: changed...
-    // In the case of outer-joins we have to obtain in the on-clause of
-    // the join these filter-conditions, that are defined for the related
-    // tables.
-    // Instead: If we would add the outer-join-filters to the where-clause,
-    // we would never see the results we intended when we made the decision
-    // to use an outer join.
-    // Therefore: Mark these DBObjects to be ignored when the where-clause
-    // will be computed.
-
-    String onClause = "";
-    FastStringBuffer s = new FastStringBuffer(2048);
-    FastStringBuffer fsb = new FastStringBuffer(256);
-
-    try {
-      DBObject leftDBObj = getByShortName(leftShortName);
-      DBObject rightDBObj = getByShortName(rightShortName);
-
-      if (fromClause == null) {
-        s.append(getTableName(leftDBObj));
-      }
-      else {
-        s.append(fromClause);
-      }
-
-      switch (joinType) {
-        case INNER_JOIN:
-          s.append(" INNER JOIN ");
-
-          // Inner-join filters can still be placed in the where-clause.
-          onClause = "";
-          break;
-        case RIGHT_JOIN:
-          s.append(" RIGHT JOIN ");
-          onClause = buildWhereClauseBuffer(true, fsb, leftShortName);
-          ignoreInWhereClause =
-              ignoreInWhereClause + "," + leftShortName + ",";
-          break;
-        case LEFT_JOIN:
-          s.append(" LEFT JOIN ");
-          onClause = buildWhereClauseBuffer(true, fsb, rightShortName);
-          ignoreInWhereClause =
-              ignoreInWhereClause + "," + rightShortName + ",";
-          break;
-      }
-
-      if (onClause.length() > 5) {
-        onClause = " AND " + onClause.substring(6);
-      }
-
-      s.append(getTableName(rightDBObj) +
-               " ON (" + getTableName(leftDBObj) + "." +
-               leftColumn + " = " + getTableName(rightDBObj) +
-               "." + rightColumn + onClause + ")");
-
-      fromClause = s.toString();
-    }
-    finally {
-      s.release();
-      fsb.release();
-    }
-    // 2003-11-05 /ebn-ma: ...changed
-    // ----------------------------------------------------
-  }
+                myStatement.append(oneRelation);
+                needAnd = true;
+            }
+        }
+        // 2003-11-05 /ebn-ma: ...changed
+        // ----------------------------------------------------
 
-  /* buildJoin(String, String, String, String, int) */
+        if (log.isDebugEnabled()) {
+            log.debug(myStatement.toString());
+        }
 
-  /**
-   * Specify a maximum number of records to be retrieved in any subsequent
-   * searchAndRetrieve() call. Records will be retrieved (in the specified
-   * sort order) until the specified maximum is reached, then the remainder
-   * of the result set is discarded. Specifying zero indicates that all records are to be retrieved.
-   *
-   * @param newMax The maximum number of records to retrieve.
-   * @throws DBException If the max number is less than 0
-   */
-  public synchronized void setMaxRecords(int newMax) throws DBException {
-
-    if (maxRecords < 0) {
-      String myName = (thisClass + "setMaxRecords(int)");
-      throw new DBException(myName +
-                            ":Max records can't be less than 0");
-    }
-
-    maxRecords = newMax;
-  }
-
-  /* setMaxRecords(int) */
-
-  /**
-   * Method to set up the fields for this database object.  If you wish to set
-   * up a MultiDBQuery ahead of time you can use this method in the inherited
-   * class to specify the objects and relationships necessary. This become
-   * something of the equivilant of a "view" in database terms.
-   *
-   * @throws DBException If there is an error setting up the fields
-   *                     as requested.
-   */
-  protected void setupFields() throws DBException {
-  }
-
-  /* setupFields() */
-
-  /**
-   * Build an appropriate String for use in the select part of an SQL statement
-   *
-   * @param oneObj    Database object containing the field
-   * @param fieldName The name of the field to be handled
-   * @return The portion of the select clause with the appropriate function
-   *         wrapped around it
-   */
-  public String selectFieldString(DBObject oneObj, String fieldName) throws
-      DBException {
-    return StringUtil.replaceStringOnce(oneObj.selectFieldString(fieldName),
-                                        fieldName,
-                                        getTableName(oneObj) + "." + fieldName);
-
-  }
-
-  /* selectFieldString(String) */
-
-  /**
-   * Execute custom SQL query
-   *
-   * @param sqlQuery   The SQL query
-   * @param fieldCount The number of fields returned by the query
-   * @return A list of recordlists
-   * @throws DBException If the query could not be completed
-   */
-  public synchronized List makeDirectQueryList(String sqlQuery, int fieldCount) throws
-      DBException {
-
-    DBConnectionPool myPool = null;
-    DBConnection myConnection = null;
-
-    try {
-      if (localConnection != null) {
-        myConnection = localConnection;
-      }
-      else {
-        myPool = DBConnectionPool.getInstance(getDataContext());
-        myConnection = myPool.getConnection(
-            "com.jcorporate.expresso.core.dbobj.DBObject");
-      }
-
-      if (recordSet == null) {
-        String myName = (thisClass + "makeDirectQueryList()");
-        throw new DBException(myName +
-                              ":Database object not correctly initialized");
-      }
-
-      recordSet.clear();
-
-      if (log.isDebugEnabled()) {
-        log.debug("Executing " + sqlQuery);
-      }
-
-      if (fieldCount > 0) {
-        myConnection.execute(sqlQuery);
-      }
-      else {
-        myConnection.executeUpdate(sqlQuery);
-      }
-
-      int recordCount = 0;
-      while (fieldCount > 0 && myConnection.next()) {
-        recordCount++;
+        return myStatement.toString();
+    }
 
-        if ( (recordCount > maxRecords) && (maxRecords > 0)) {
-          break;
-        }
+    /* buildWhereClause(boolean) */
 
-//Retreive Row
-        List tFieldValues = new ArrayList();
-        for (int i = 1; i <= fieldCount; i++) {
-          String oneFieldValue = null;
-
-          try {
-            oneFieldValue = myConnection.getString(i);
-          }
-          catch (DBException de) {
-            throw new DBException(thisClass + "makeDirectQueryList()" +
-                                  ":Error retrieving field " + i, de);
-          }
+    /**
+     * Insert the method's description here.
+     * <p/>
+     * Creation date: (10/3/00 10:48:12 AM)
+     *
+     * @throws com.jcorporate.expresso.core.db.DBException
+     *          The exception description.
+     */
+    public void clear() throws com.jcorporate.expresso.core.db.DBException {
+        DBObject oneObj = null;
 
-          tFieldValues.add(oneFieldValue);
+        for (Enumeration eachObj = myDBObjects.elements();
+             eachObj.hasMoreElements();) {
+            oneObj = (DBObject) eachObj.nextElement();
+            oneObj.clear();
         }
 
-//Add row to rowset
-        recordSet.add(tFieldValues);
-      }
+        /**
+         * FIXED: ma 17.10.2003  Kill the fromClause here!
+         */
+        fromClause = null;
 
-      /* each row retrieved from the db */
     }
-    finally {
-      if (localConnection == null) {
-        myPool.release(myConnection);
-      }
-    }
-
-    return recordSet;
-  }
-
-  /**
-   * (assumes retrieval from DB has occurred)
-   * assemble a given object, one retrieved by the join, getting
-   * as many fields as found into the returned object. ignores all virtual fields.
-   *
-   * @param shortName name of join object
-   * @return DBObject of type given by shortname
-   * @deprecated v. 5.5+; 9/04; use getDBObject() instead
-   */
-  public DBObject assembleObject(String shortName) throws DBException {
-    DBObject result = null;
-    DBObject model = getDBObject(shortName);
-    result = model.newInstance();
-    for (Iterator iterator = model.getMetaData().getAllFieldsMap().values().
-         iterator(); iterator.hasNext(); ) {
-      DBField oneField = (DBField) iterator.next();
-      try {
-        if (oneField.isVirtual) {
-          continue; // ignore virtual fields
-        }
-
-        String fieldName = oneField.getName();
-        if (isFieldNull(shortName, fieldName)) {
-          result.setField(fieldName, (String)null);
-        }
-        else {
-          result.setField(fieldName, getField(shortName, fieldName));
-        }
-      }
-      catch (DBException e) {
-        // we don't care if getField throws; it will do so if there is nothing retrieved for this field
-      }
-    }
-    result.setStatus(DBObject.STATUS_CURRENT);
-    return result;
-  }
-
-  /**
-   * Specify whether the shortName specified when a DBObject is added is also
-   * used as an alias for the table in the query.  (This is false by default.)
-   *
-   * comment from user Mark:
-   * "the setShortNameAsAlias(true) solution only works if you use setForeignKey
-   * , it doesn't work with setInnerJoin. That's fine with me, but since it might be a bug, i report it here.
-   *
-   * @param flag True if the shortName should be used as an alias
-   */
-  public void setShortNameAsAlias(boolean flag) {
-    shortNameAsAlias = flag;
-  }
-
-  /**
-   * Get whether the shortName specified when a DBObject is added is also
-   * used as an alias for the table in the query.
-   *
-   * @return True if short name is used as an alias for the table
-   */
-  public boolean getShortNameAsAlias() {
-    return shortNameAsAlias;
-  }
-
-  /**
-   * Get the name a table should be referenced by.  Should be the name of the
-   * table in the database unless using shortNames as alias.
-   *
-   * @param oneObj The DBObject to get the name of
-   * @return The name of the table
-   */
-  private String getTableName(DBObject oneObj) throws DataException {
-    String name = (String) oneObj.getAttribute(SHORT_NAME);
-    if (name != null && shortNameAsAlias) {
-      return name;
-    }
-    return oneObj.getJDBCMetaData().getTargetSQLTable(oneObj.getDataContext());
-  }
-
-  /**
-   * @return
-   */
-  public DBConnection getConnection() {
-    return localConnection;
-  }
-
-  /**
-   * @param connection
-   */
-  public void setConnection(DBConnection connection) {
-    localConnection = connection;
-  }
-
-  /**
-   * @return true if the given field is null
-   */
-  public boolean isFieldNull(String shortName, String fieldName) throws
-      DBException {
-    return getByShortName(shortName).isFieldNull(fieldName);
-  }
+
+    /* clear() */
+
+    /**
+     * Return the name of the context/database connection that this DB object is
+     * using. If none is set, then we are using the "default" database/context.
+     *
+     * @return the name of the datacontext
+     * @deprecated 12/2004; use getDataContext instead
+     */
+    public synchronized String getDBName() {
+        return getDataContext();
+    }
+
+    /* getDBName() */
+
+    /**
+     * Return the name of the context/database connection that this DB object is
+     * using. If none is set, then we are using the "default" database/context.
+     *
+     * @return the name of the datacontext
+     */
+    public synchronized String getDataContext() {
+        // set from RequestRegistry if this is first access
+        if (dbName == null || dbName.length() == 0) {
+            try {
+                this.setDataContext(RequestRegistry.getDataContext());
+            } catch (Exception ex) {
+                log.warn("Problem getting data context from Registry: " +
+                        ex.getClass().getName() + ", msg: " + ex.getMessage());
+                try {
+                    setDataContext(DBConnection.DEFAULT_DB_CONTEXT_NAME);
+                } catch (DBException de) {
+                    log.error("Unable to set database to 'default'", de);
+                }
+            }
+
+        }
+
+        return dbName;
+    }
+
+    /**
+     * This returns the encapsulated instance of the 'model' DBObject
+     * that was provided in 'addDBObj()';
+     * <p/>
+     * After a searchAndRetrieveList(), each MultiDBObject in the list has,
+     * encapsulated, an object of