[Opensource] new multidbobject functionality

Brian Rook brian.rook at xor.com
Tue Jul 9 13:54:39 PDT 2002


MulitDBObject is lacking some of the functionality that DBObject has.
Specifically the methods and attributes necessary for pagination.  I've
added a couple of lines to the class and am providing them here for updates
to this class in future releases:

(These are additions that I made to the 4.02 codebase)

Oh yeah, most of this code comes directly from Peter Pilgrim's work on
DBObject.  (Thanks, dood)

added:
protected int offsetRecord attribute

public setOffsetRecord method
public getOffsetRecord method

and updated the searchAndRetrieve method with the SQL limitation code that
Peter(?) added to DBObject

my coworker Mike Mahon also added the getDBObject (public) method which
returns the DBObject returned from getByShortName (private method) so that
you can get at individual DBObjects and use them for updates/deletes.

brian


package com.jcorporate.expresso.core.dbobj;

/**
 * MultiDBObject.java
 *
 * Copyright 2000, 2001 Jcorporate Ltd.
 */
import java.util.Vector;
import java.util.Date;
import com.jcorporate.expresso.core.db.DBException;
import com.jcorporate.expresso.core.misc.StringUtil;
import java.util.Hashtable;
import java.util.Enumeration;
import com.jcorporate.expresso.core.misc.ConfigManager;
import com.jcorporate.expresso.core.misc.ConfigJdbc;
import com.jcorporate.expresso.core.misc.ConfigurationException;
import java.util.StringTokenizer;
import com.jcorporate.expresso.core.db.DBConnectionPool;
import com.jcorporate.expresso.core.db.DBConnection;
import com.jcorporate.expresso.services.test.Testable;
import com.jcorporate.expresso.core.logging.LogManager;
import org.apache.log4j.Category;

/**
 * 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
 * master/detail or a more complex relationship, but it can be expressed as
a
 * "join" operation between the tables.
 *
 * After establishing the relationships between the objects the
MultiDBObject
 * can have search criteria set for it & searchAndRetrieve operations done
just
 * like a 'regular' DBObject, but these operations affect the entire related
 * group of objects. At the moment, MultiDBObjects are read-only, though
that
 * may change in the future.
 *
 * Creation date: (9/18/00 11:32:03 AM)
 * @author: Administrator
 */
public class MultiDBObject implements Testable {

    private static String thisClass =
        "com.jcorporate.expresso.core.dbobj.MultiDBObject.";

	/* 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;

	/* field names to sort the results by */
	private String sortKey = null;

	/* broken into a vector */
	private Vector sortKeys = new Vector();

	/** The vector of MultiDB objects retrieved by the last searchAndRetrieve
method */
	private Vector recordSet = new Vector();

	/** 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;

	/** 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
     */
    protected int offsetRecord = 0;

    private static Category log = null;


    /**
     * MultiDBObject constructor comment.
     *
     * @throws DBException
     */
    public MultiDBObject() throws DBException {
        super();
        setupLog();
        setupFields();
    } /* 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 ClassName java.lang.String
     * @param shortName java.lang.String
     * @throws DBException
     */
    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 = Class.forName(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("shortName", shortName);
        myDBObjects.put(shortName, oneDBObj);

	if (getDBName() == null) {
		setDBName(oneDBObj.getDBName());
	}

	/* Check to see if all of the db objects are in the same database */
	oneDBObj.setDBName(getDBName());
	if (!oneDBObj.getDBName().equals(getDBName())) {
		throw new DBException("DB Object '" + oneDBObj.getClass().getName()
		+ "' was mapped to db/context '" + oneDBObj.getDBName()
		+ "', but this MultiDBObject is in context '" + getDBName() + "'");
	}
    } /* 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
     * @throws DBException
     * @return
     */
    protected String buildWhereClause(boolean useAllFields)
        throws DBException {

        String myName = new String(thisClass + "buildWhereClause(boolean)");

        /* list of field names (with table names prefixed) */
        Vector fields = new Vector();
        DBObject oneObj = null;
        String fieldName = null;
        Hashtable byTableName = new Hashtable();

        StringBuffer myStatement = new StringBuffer("");
        if (useAllFields) {
            for (Enumeration eachObj = myDBObjects.elements();
                eachObj.hasMoreElements(); ) {

                oneObj = (DBObject) eachObj.nextElement();
                byTableName.put(oneObj.getTableName(), oneObj);
                for (Enumeration ee = oneObj.getFieldList();
                    ee.hasMoreElements(); ) {

                    fieldName = (String) ee.nextElement();
                    if (!oneObj.isVirtual(fieldName)) {
                        fields.addElement(oneObj.getTableName() + "."
                            + fieldName);
                    }
                } /* for each field */
            } /* for each db object */
        } else {
            for (Enumeration eachObj = myDBObjects.elements();
                eachObj.hasMoreElements(); ) {

                oneObj = (DBObject) eachObj.nextElement();
                for (Enumeration ee = oneObj.getKeyFieldList();
                    ee.hasMoreElements(); ) {

                    fieldName = (String) ee.nextElement();
                    if (!oneObj.isVirtual(fieldName)) {
                        fields.addElement(oneObj.getTableName() + "."
                            + fieldName);
                    }
                } /* for each field */
            } /* for each db object */
        }

        /* Now go thru each field - if it is non-empty, add it's criteria */
        /* 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(getDBName());

            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, ".");
            oneTableName = stk.nextToken();
            oneFieldName = stk.nextToken();
            oneObj = (DBObject) byTableName.get(oneTableName);

            skipField = false;

            oneFieldValue =
StringUtil.notNull(oneObj.getField(oneFieldName));

	    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.getType(oneFieldName).equals("text"))
                && (skipText)) {

                skipField = true;
            }

            if (!skipField) {
                if (addWhere) {
                    myStatement.append(" WHERE ");
                    addWhere = false;
                }
                if (addAnd) {
                    myStatement.append(" AND ");
                }
                if (oneObj.containsWildCards(oneFieldValue)) {
                    myStatement.append(oneObj.getTableName() + "."
                        + oneFieldName);
                    myStatement.append(" LIKE ");
                    myStatement.append(oneFieldValue);
                } else if (rangeString != null) {
                    myStatement.append(oneObj.getTableName() + "."
                        + oneFieldName);
                    myStatement.append(" " + rangeString + " ");
                    myStatement.append(oneFieldValue);
                } else {
                    myStatement.append(oneObj.getTableName() + "."
                        + oneFieldName);
                    myStatement.append(" = ");
                    myStatement.append(oneFieldValue);
                }

                addAnd = true;
            }
            /* if field is not skipped for some reason */
        }
        /* for each field */

        boolean needAnd = false;
        if (addWhere) {
            myStatement.append(" WHERE ");
        } else {
            needAnd = true;
        }

        String oneRelation = null;
        for (Enumeration e = relations.elements(); e.hasMoreElements(); ) {
            oneRelation = (String) e.nextElement();
            if (needAnd) {
                myStatement.append(" AND ");
            }
            myStatement.append(oneRelation);
            needAnd = true;
        }

        if (log.isDebugEnabled()) {
            log.debug(myStatement.toString());
        }

        return myStatement.toString();
    } /* buildWhereClause(boolean) */


    /**
     * Insert the method's description here.
     *
     * 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();
        }
    } /* cleqar() */


	/**
	 * 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
	 */
	public synchronized String getDBName() {
		return dbName;
	} /* getDBName() */


    /**
     * Insert the method's description here.
     *
     * Creation date: (9/18/00 11:37:10 AM)
     * @param   shortName
     * @param   fieldName
     * @throws  DBException
     * @return
     */
    public String getField(String shortName, String fieldName)
        throws DBException {

        String myName = thisClass + "getField(String, String)";

        DBObject oneObj = (DBObject) myDBObjects.get(shortName);
        if (oneObj == null) {
            throw new DBException(myName + ":No such object as '"
                + shortName + "'");
        }
        return oneObj.getField(fieldName);
    } /* getFields(String, STring) */

    /**
     * This method returns a DBObject that can be edited.
     *
     * Creation date: (9/18/00 11:37:10 AM)
     * @param   shortName
     * @throws  DBException
     * @return  DBObject
     */
    public DBObject getDBObject(String shortName) throws DBException {
        return getByShortName(shortName);
    }

    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 Date getFieldDate(String shortName, String fieldName)
        throws DBException {

        return getByShortName(shortName).getFieldDate(fieldName);
    } /* getFieldDate(String, String) */


    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) */


    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);
    }


	/**
	 *
     *
     * @throws  DBException
     * @return
	 */
	protected MultiDBObject getThisMultiDBObj() throws DBException {
		MultiDBObject newObj = new MultiDBObject();
		newObj.setDBName(getDBName());
		String oneKey = null;
		String oneDBObjName = null;
		DBObject oneDBObj = null;
		for (Enumeration ek = myDBObjects.keys(); ek.hasMoreElements(); ) {
			oneKey = (String) ek.nextElement();
			oneDBObj = (DBObject) myDBObjects.get(oneKey);
			newObj.addDBObj(oneDBObj.getClass().getName(), 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());
		}
		return newObj;
	} /* getThisMultiDBObj() */

	 /**
     * Get the count of all the records in this multidbobject
     * NOTE: Criteria in 'text' type colums is ignored (SQL Server
limitation)
     *
     * @return	int the number of records that match this search
     * @throws	DBException If the search could not be completed
     */
    public synchronized int count() throws DBException {
    	String myName = new String(thisClass + "count()");
        boolean needComma = false;
		DBObject oneObj = null;
        if (recordSet == null) {
            throw new DBException(myName
                + ":Database object not correctly initialized");
        }
        recordSet.removeAllElements();
        String fieldName = null;
        StringBuffer myStatement = new StringBuffer();
        myStatement.append("SELECT COUNT(*) FROM ");

		needComma = false;
        for (Enumeration eachObj = myDBObjects.elements();
            eachObj.hasMoreElements();) {

            oneObj = (DBObject) eachObj.nextElement();
            if (needComma) {
                myStatement.append(",");
            }
            myStatement.append(oneObj.getTableName());
            needComma = true;
        }
        if (customWhereClause != null) {
            myStatement.append(customWhereClause);
            customWhereClause = null;
        } else {
            myStatement.append(buildWhereClause(true));
        }

		if (log.isDebugEnabled()) {
            log.error("Executing " + myStatement.toString());
        }

        DBConnectionPool myPool = null;
        DBConnection myConnection = null;
        try {
            if (localConnection != null) {
                myConnection = localConnection;
            } else {
                myPool = DBConnectionPool.getInstance(getDBName());
                myConnection = myPool.getConnection(myName);
            }
            myConnection.execute(myStatement.toString());

            String oneFieldValue = new String("");
            StringBuffer oneKeyString = new StringBuffer("");

            if (myConnection.next()) {
                return myConnection.getInt(1);
            }
        } catch (DBException de) {
            throw de;
        } finally {
            if (localConnection == null) {
                myPool.release(myConnection);
            }
        }
		return 0;
	} /* count() */


	/**
     * Creates the limitation syntax optimisation stub
     * to embed inside the SQL command that performs
     * search and retrieve.
     *
     * <p>This method takes the limitation syntax string
     * and performs a string replacement on the following
     * tokens
     *
     * <ul>
     *
     * <li><b>%offset%</b><li><br>
     * the number of rows in the <code>ResultSet</code> to skip
     * before reading the data.
     *
     * <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>.
     *
     * <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>
     *
     * </ul>
     *
     * </p>
     *
     *
     * @author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
     *
     * @see #getLimitationSyntax()
     * @see #searchAndRetrieve()
     * @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) */

    /**
     * Find a set of records of all of the objects that match the current
     * search critieria in the fields
     * and retrieve the list of all records that match this criteria
     * NOTE: Criteria in 'text' type colums is ignored (SQL Server
limitation)
     *
     * @return	Vector A vector of new database objects containing the
results
     *			of the search
     * @throws	DBException If the search could not be completed
     */
    public synchronized Vector searchAndRetrieve() throws DBException {
        String myName = new String(thisClass + "searchAndRetrieve()");
        boolean needComma = false;
        DBObject oneObj = null;
        if (recordSet == null) {
            throw new DBException(myName
                + ":Database object not correctly initialized");
        }
        recordSet.removeAllElements();
        String fieldName = null;
        StringBuffer myStatement = new StringBuffer("SELECT ");
        for (Enumeration eachObj = myDBObjects.elements();
            eachObj.hasMoreElements();) {

            oneObj = (DBObject) eachObj.nextElement();
            for (Enumeration e = oneObj.getFieldList();
e.hasMoreElements();) {
                fieldName = (String) e.nextElement();
                if (!oneObj.isVirtual(fieldName)) {
                    if (needComma) {
                        myStatement.append(", ");
                    }
                    myStatement.append(oneObj.getTableName() + "."
                        + oneObj.selectFieldString(fieldName));
                    needComma = true;
                }
                /* if field is not virtual */
            }
            /* for each field */
        }
        myStatement.append(" FROM ");
        needComma = false;
        for (Enumeration eachObj = myDBObjects.elements();
            eachObj.hasMoreElements();) {

            oneObj = (DBObject) eachObj.nextElement();
            if (needComma) {
                myStatement.append(",");
            }
            myStatement.append(oneObj.getTableName());
            needComma = true;
        }
        DBConnectionPool myPool = null;
        DBConnection myConnection = null;
        try {
            if (localConnection != null) {
                myConnection = localConnection;
            } else {
                myPool = DBConnectionPool.getInstance(getDBName());
                myConnection = myPool.getConnection(myName);
            }

			//adding limitation information
			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(" ");
			}


			if (customWhereClause != null) {
	            myStatement.append(customWhereClause);
	            customWhereClause = null;
			} else {
    	        myStatement.append(buildWhereClause(true));
	        }

	        /* 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());
    	    }

	        myConnection.execute(myStatement.toString());

            int recordCount = 0;
            int retrieveCount = 0;
            while (myConnection.next()) {
                MultiDBObject myObj = getThisMultiDBObj();
                recordCount++;
                retrieveCount++;

				 //If there's limitation syntax on, then the first record will be the
                //maximum record.
                if (retrieveCount < offsetRecord  && offsetRecord > 0
                && myConnection.getLimitationPosition() ==
                DBConnection.LIMITATION_DISABLED) {
                    continue;
                } else if (retrieveCount == offsetRecord  &&
                offsetRecord > 0 && myConnection.getLimitationPosition() ==
                DBConnection.LIMITATION_DISABLED) {
                    recordCount = 0; //Reset count for counting for max
records.
                    continue; //?
                }

				if ((recordCount > maxRecords) && (maxRecords > 0)) {
                    break;
                }
                int i = 1;
                if (log.isDebugEnabled()) {
                    log.debug("Retrieved row " + recordCount);
                }

                String oneFieldValue = null;
                for (Enumeration eachObj = myDBObjects.elements();
                    eachObj.hasMoreElements();) {

                    oneObj = (DBObject) eachObj.nextElement();
                    for (Enumeration e = oneObj.getFieldList();
                        e.hasMoreElements();) {

                        fieldName = (String) e.nextElement();
                        if (!oneObj.isVirtual(fieldName)) {
                            try {
                                oneFieldValue = myConnection.getString(i);
                            } catch (DBException de) {
                                throw new DBException(myName
                                    + ":Error retrieving field '"
                                    + oneObj.getTableName() + "."
                                    + fieldName + "'", de);
                            }
                            i++;
                            if (log.isDebugEnabled()) {
                                log.debug("Setting " + oneObj.getTableName()
                                    + "." + fieldName + " to " +
oneFieldValue);
                            }

                            myObj.setField((String) oneObj.getAttribute(
                                "shortName"), fieldName, oneFieldValue);
                        }
                    }
                } /* each db object */
                myObj.setDBName(getDBName());
                recordSet.addElement(myObj);
            }
            /* each row retrieved from the db */
        } catch (DBException de) {
            throw de;
        } finally {
            if (localConnection == null) {
                myPool.release(myConnection);
            }
        }
        return recordSet;
    } /* searchAndRetrieve() */


	/**
	 * 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
	 */
	public synchronized Vector searchAndRetrieve(String sortKeyString)
        throws DBException {

		String myName = new String(thisClass + "searchAndRetrieve()");

		if (sortKeys == null) {
			throw new DBException(myName + ":Sort keys null");
		}

		sortKeys.removeAllElements();

		StringTokenizer stk = new StringTokenizer(sortKeyString, "|");
		while (stk.hasMoreTokens()) {
			sortKeys.addElement(stk.nextToken());
		}

		return searchAndRetrieve();
	} /* searchAndRetrieve(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
     */
    public synchronized void setCustomWhereClause(String newCustomWhere) {
        customWhereClause = newCustomWhere;
    } /* setCustomWhereClause(String) */


	/**
	 * Set the database name/context for this multi db object. If setDBName 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
     * @throws DBException
	 */
	public synchronized void setDBName(String newOther) throws DBException {
		dbName = newOther;
	} /* setDBName(String) */


    /**
     * Insert the method's description here.
     *
     * Creation date: (9/18/00 11:37:10 AM)
     * @param   shortName
     * @param   fieldName
     * @param   fieldValue
     * @throws  DBException
     */
    public void setField(String shortName, String fieldName, String
fieldValue)
        throws DBException {

        String myName = thisClass + "setField(String, String, String)";

        DBObject oneObj = (DBObject) myDBObjects.get(shortName);
        if (oneObj == null) {
            throw new DBException(myName + ":No such object as '" +
shortName
                + "'");
        }
        oneObj.setField(fieldName, fieldValue);
    } /* setField(String, String, String) */


    /**
     * Insert the method's description here.
     *
     * 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
     * @throws DBException
     */
    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(foreignDBObj.getTableName() + "." + foreignKey
            + " = " + primaryDBObj.getTableName() + "." + primaryKey);

        originalRelations.addElement(shortName + "|" + foreignKey + "|"
            + shortName2 + "|" + primaryKey);
    } /* setForeignKey(String, String, String, String) */


	/**
	 * 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 {
		String myName = new String(thisClass + "setMaxRecords(int)");

		if (maxRecords < 0) {
			throw new DBException(myName + ":Max records can't be less than 0");
		}
		maxRecords = newMax;
	} /* setMaxRecords(int) */

	/**
     * 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
     */
    protected 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 newMax The maximum number of records to retrieve.
     * @throws DBException If the max number is less than 0
     */
    public synchronized void setOffsetRecord(int newOffset) throws
DBException {
        if (newOffset < 0) {
            throw new DBException("Offset recors 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>.
     *
     * @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() */

	/**
	 * 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() */


    /*
     *
     *
     * @param   ergv[]
     * @param   verbose
     * @param   out
     * @throws  Exception
     * @return
     */
	public boolean unitTest(String argv[], boolean verbose,
        java.io.PrintStream out) throws Exception {

		String testUser = "Admin";

		MultiDBObject mq = new MultiDBObject();
		mq.setDBName("default");

		/* this multi db object contains 3 tables */
		mq.addDBObj("com.jcorporate.expresso.services.dbobj.UserDBObj",
            "user");
		mq.addDBObj("com.jcorporate.expresso.services.dbobj.GroupMembers",
            "groupmembers");
		mq.addDBObj("com.jcorporate.expresso.services.dbobj.UserGroup",
            "group");

		/* specify the relationships */
		mq.setForeignKey("groupmembers", "UserName", "user", "UserName");
		mq.setForeignKey("groupmembers", "GroupName", "group", "GroupName");

		/* specify the search criteria */
		mq.setField("user", "UserName", testUser);

		MultiDBObject result = null;
		out.println("User " + testUser + " belongs to the following groups:");

		for (Enumeration e = mq.searchAndRetrieve().elements();
            e.hasMoreElements(); ) {

			result = (MultiDBObject) e.nextElement();
			out.println(result.getField("group", "Descrip"));
		}
		return true;
	} /* unitTest(String, boolean, java.io.PrintStream) */


	/**
	 *  Set up the logging channels for all DBObjects. A DBObject that wants
it's own
     * channel may override this method to set it's own channel name
	 *
	 */
	protected void setupLog()  {
        log = Category.getInstance("expresso.core.dbobj.DBObject");
	} /* setupLog() */

} /* MultiDBObject */




More information about the Opensource mailing list