Print

Print


Author: [log in to unmask]
Date: Wed Apr 22 16:39:56 2015
New Revision: 2791

Log:
Add DatabaseObject interface and cleanup basic operations for objects and collections.

Added:
    java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/DatabaseObject.java
    java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/DatabaseObjectException.java
    java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/
    java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectCollectionTest.java
    java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectConverterTest.java
    java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectTest.java
Removed:
    java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/api/BaseConditionsObjectCollectionTest.java
    java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/api/BaseConditionsObjectTest.java
    java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/api/DummyConditionsObjectConverterTest.java
Modified:
    java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/AbstractConditionsObjectConverter.java
    java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/BaseConditionsObject.java
    java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/BaseConditionsObjectCollection.java
    java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObject.java
    java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObjectCollection.java
    java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObjectUtilities.java
    java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/cli/AddCommand.java
    java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/cli/TagCommand.java
    java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/database/ConditionsRecordConverter.java
    java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/database/ConditionsSeriesConverter.java
    java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/dummy/DummyConditionsObject.java
    java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/svt/SvtConditionsLoader.java

Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/AbstractConditionsObjectConverter.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/AbstractConditionsObjectConverter.java	(original)
+++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/AbstractConditionsObjectConverter.java	Wed Apr 22 16:39:56 2015
@@ -124,7 +124,7 @@
         // Select the objects into the collection by the collection ID.
         try {
             collection.select(conditionsRecord.getCollectionId());
-        } catch (ConditionsObjectException | SQLException e) {
+        } catch (DatabaseObjectException | SQLException e) {
             throw new RuntimeException("Error creating conditions collection from table " + name
                     + " with collection ID " + conditionsRecord.getCollectionId(), e);
         }

Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/BaseConditionsObject.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/BaseConditionsObject.java	(original)
+++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/BaseConditionsObject.java	Wed Apr 22 16:39:56 2015
@@ -29,7 +29,7 @@
 
     static final int UNSET_COLLECTION_ID = -1;
 
-    static final int UNSET_ID = -1;
+    static final int UNSET_ROW_ID = -1;
 
     protected static String defaultToString(final BaseConditionsObject object) {
         final StringBuffer sb = new StringBuffer();
@@ -55,7 +55,7 @@
     /**
      * The row ID of the object in its table. This will be -1 for new objects that are not in the database.
      */
-    private int id = UNSET_ID;
+    private int id = UNSET_ROW_ID;
 
     /**
      * Flag to indicate object is locally changed and database update has not been executed.
@@ -114,36 +114,32 @@
      * @throw SQLException if there is an SQL error when executing the SELECT operation
      */
     public BaseConditionsObject(final Connection connection, final TableMetaData tableMetaData, final int rowId)
-            throws ConditionsObjectException, SQLException {
+            throws DatabaseObjectException, SQLException {
         this.connection = connection;
         this.tableMetaData = tableMetaData;
         this.fieldValues = new FieldValuesMap(tableMetaData);
-        final boolean selected = select(rowId);
-        if (!selected) {
-            throw new ConditionsObjectException("Failed to select data into object using row ID " + rowId);
-        }
-    }
-
-    @Override
-    public final boolean delete() throws ConditionsObjectException, SQLException {
+        select(rowId);
+        // if (!selected) {
+        // throw new ConditionsObjectException("Failed to select data into object using row ID " + rowId);
+        // }
+    }
+
+    @Override
+    public final void delete() throws DatabaseObjectException, SQLException {
         if (isNew()) {
-            throw new ConditionsObjectException("Object is not in database and so cannot be deleted.");
+            throw new DatabaseObjectException("Object is not in database and so cannot be deleted.", this);
         }
         final String sql = "DELETE FROM " + this.tableMetaData.getTableName() + " WHERE id=" + this.getRowId();
-        // if (this.verbose) {
-        // System.out.println(sql);
-        // }
         Statement statement = null;
-        int rowsUpdated;
         try {
             statement = this.connection.createStatement();
-            rowsUpdated = statement.executeUpdate(sql);
+            statement.executeUpdate(sql);
+            this.setRowId(UNSET_ROW_ID);
         } finally {
             if (statement != null) {
                 statement.close();
             }
         }
-        return rowsUpdated != 0;
     }
 
     @Override
@@ -177,9 +173,12 @@
     }
 
     @Override
-    public final boolean insert() throws ConditionsObjectException, SQLException {
-        if (!isNew()) {
-            throw new ConditionsObjectException("Cannot insert an existing record.");
+    public final void insert() throws DatabaseObjectException, SQLException {
+        if (!this.isNew()) {
+            throw new DatabaseObjectException("Cannot insert existing record with row ID: " + this.getRowId(), this);
+        }
+        if (!this.hasValidCollection()) {
+            throw new DatabaseObjectException("Cannot insert object without a valid collection ID.", this);
         }
         final StringBuffer sb = new StringBuffer();
         sb.append("INSERT INTO " + this.tableMetaData.getTableName() + " (");
@@ -198,15 +197,11 @@
         sb.setLength(sb.length() - 2);
         sb.append(")");
         final String sql = sb.toString();
-        // if (this.verbose) {
-        // System.out.println(sql);
-        // }
         Statement statement = null;
         ResultSet resultSet = null;
-        int rowsUpdated = 0;
         try {
             statement = this.connection.createStatement();
-            rowsUpdated = statement.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
+            statement.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
             resultSet = statement.getGeneratedKeys();
             while (resultSet.next()) {
                 final int key = resultSet.getInt(1);
@@ -221,7 +216,7 @@
                 statement.close();
             }
         }
-        return rowsUpdated != 0;
+        this.isDirty = false;
     }
 
     @Override
@@ -231,11 +226,11 @@
 
     @Override
     public final boolean isNew() {
-        return getRowId() == UNSET_ID;
-    }
-
-    @Override
-    public final boolean select(final int id) throws ConditionsObjectException, SQLException {
+        return getRowId() == UNSET_ROW_ID;
+    }
+
+    @Override
+    public final boolean select(final int id) throws DatabaseObjectException, SQLException {
         this.id = id;
         if (id < 1) {
             throw new IllegalArgumentException("bad row ID value: " + id);
@@ -249,20 +244,18 @@
         sb.append(" FROM " + this.tableMetaData.getTableName());
         sb.append(" WHERE id = " + this.getRowId());
         final String sql = sb.toString();
-        // if (this.verbose) {
-        // System.out.println(sql);
-        // }
         Statement statement = null;
         ResultSet resultSet = null;
         boolean selected = false;
         try {
             statement = this.connection.createStatement();
             resultSet = statement.executeQuery(sql);
-            while (resultSet.next()) {
+            selected = resultSet.next();
+            if (selected) {
                 for (int columnIndex = 1; columnIndex <= this.tableMetaData.getFieldNames().length; columnIndex++) {
-                    this.setFieldValue(this.tableMetaData.getFieldNames()[columnIndex - 1], resultSet.getObject(columnIndex));
+                    this.setFieldValue(this.tableMetaData.getFieldNames()[columnIndex - 1],
+                            resultSet.getObject(columnIndex));
                 }
-                selected = true;
             }
         } finally {
             if (resultSet != null) {
@@ -297,7 +290,7 @@
     }
 
     @Override
-    public void setId(final int id) {
+    public void setRowId(final int id) {
         this.id = id;
     }
 
@@ -318,10 +311,11 @@
     }
 
     @Override
-    public final boolean update() throws ConditionsObjectException, SQLException {
+    public final boolean update() throws DatabaseObjectException, SQLException {
+        int rowsUpdated = 0;
         if (isDirty()) {
             if (isNew()) {
-                throw new ConditionsObjectException("Cannot update a new object.");
+                throw new DatabaseObjectException("Cannot update a new object.", this);
             }
             final StringBuffer sb = new StringBuffer();
             sb.append("UPDATE " + this.tableMetaData.getTableName() + " SET ");
@@ -338,30 +332,29 @@
             sb.setLength(sb.length() - 2);
             sb.append(" WHERE id=" + this.getRowId());
             final String sql = sb.toString();
-            // if (this.verbose) {
-            // System.out.println(sql);
-            // }
             Statement statement = null;
-            int rowsUpdated = 0;
             try {
                 statement = this.connection.createStatement();
                 rowsUpdated = statement.executeUpdate(sql);
-                if (rowsUpdated > 0) {
-                    this.isDirty = false;
-                }
             } finally {
                 if (statement != null) {
                     statement.close();
                 }
             }
-            return rowsUpdated != 0;
-        } else {
-            return false;
-        }
+        }
+        if (rowsUpdated > 0) {
+            this.isDirty = false;
+        }
+        return rowsUpdated != 0;
     }
 
     @Override
     public <T> T getFieldValue(final String name) {
         return (T) this.fieldValues.getValue(name);
     }
+
+    @Override
+    public boolean hasValidCollection() {
+        return getCollectionId() != UNSET_COLLECTION_ID;
+    }
 }

Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/BaseConditionsObjectCollection.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/BaseConditionsObjectCollection.java	(original)
+++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/BaseConditionsObjectCollection.java	Wed Apr 22 16:39:56 2015
@@ -23,12 +23,13 @@
     private Connection connection;
     private final Set<ObjectType> objects = new LinkedHashSet<ObjectType>();
     private TableMetaData tableMetaData;
+    private boolean isDirty;
 
     protected BaseConditionsObjectCollection() {
     }
 
     public BaseConditionsObjectCollection(final Connection connection, final TableMetaData tableMetaData,
-            final int collectionId) throws SQLException, ConditionsObjectException {
+            final int collectionId) throws SQLException, DatabaseObjectException {
         this.connection = connection;
         this.tableMetaData = tableMetaData;
         this.collectionId = collectionId;
@@ -39,15 +40,16 @@
 
     @Override
     public boolean add(final ObjectType object) {
-        // System.out.println("adding object " + object + " to collection " + getCollectionId());
+        if (object == null) {
+            throw new IllegalArgumentException("The object argument is null.");
+        }
         if (getCollectionId() != BaseConditionsObject.UNSET_COLLECTION_ID) {
-            if (object.getCollectionId() != BaseConditionsObject.UNSET_ID) {
+            if (object.getCollectionId() != BaseConditionsObject.UNSET_ROW_ID) {
                 if (object.getCollectionId() != this.collectionId) {
                     throw new IllegalArgumentException("Cannot add object with different collection ID: "
                             + object.getCollectionId());
                 }
             } else {
-                // System.out.println("object.setCollectionId - " + this.collectionId);
                 try {
                     object.setCollectionId(this.collectionId);
                 } catch (final ConditionsObjectException e) {
@@ -55,7 +57,12 @@
                 }
             }
         }
-        return this.objects.add(object);
+        final boolean added = this.objects.add(object);
+        if (!added) {
+            throw new RuntimeException("Failed to add object.");
+        }
+        this.isDirty = true;
+        return added;
     }
 
     /**
@@ -63,14 +70,13 @@
      * @throws SQLException
      */
     @Override
-    public void deleteAll() throws ConditionsObjectException, SQLException {
+    public final void delete() throws DatabaseObjectException, SQLException {
         Statement statement = null;
         try {
             final String sql = "DELETE FROM `" + this.tableMetaData.getTableName() + "` WHERE collection_id = '"
                     + getCollectionId() + "'";
             statement = this.connection.createStatement();
             statement.executeUpdate(sql);
-            // System.out.println("result from delete: " + result);
             this.connection.commit();
         } catch (final SQLException e) {
             e.printStackTrace();
@@ -82,7 +88,7 @@
     }
 
     @Override
-    public ObjectType get(final int index) {
+    public final ObjectType get(final int index) {
         if (index + 1 > this.size() || index < 0) {
             throw new IndexOutOfBoundsException("index out of bounds: " + index);
         }
@@ -97,12 +103,12 @@
     }
 
     @Override
-    public int getCollectionId() {
+    public final int getCollectionId() {
         return this.collectionId;
     }
 
     @Override
-    public TableMetaData getTableMetaData() {
+    public final TableMetaData getTableMetaData() {
         return this.tableMetaData;
     }
 
@@ -112,12 +118,22 @@
      * @throws SQLException
      */
     @Override
-    public void insertAll(final int collectionId) throws ConditionsObjectException, SQLException {
-        if (this.collectionId != -1) {
-            throw new RuntimeException("collection already exists");
-        }
-        this.collectionId = collectionId;
-
+    public final void insert() throws DatabaseObjectException, SQLException {
+
+        // Turn off auto-commit to perform a transaction.
+        this.connection.setAutoCommit(false);
+
+        if (this.collectionId == BaseConditionsObject.UNSET_COLLECTION_ID) {
+            // Automatically get the next global collection ID from the conditions database.
+            this.collectionId = this.getNextCollectionId();
+        } else {
+            // If the collection already exists in the database with this ID then it cannot be inserted.
+            if (checkExists()) {
+                throw new DatabaseObjectException("The collection " + this.collectionId
+                        + " cannot be inserted because it already exists in the " + this.tableMetaData.getTableName()
+                        + " table.", this);
+            }
+        }
         PreparedStatement insertObjects = null;
         final StringBuffer sb = new StringBuffer();
         sb.append("INSERT INTO " + this.getTableMetaData().getTableName() + " (");
@@ -132,25 +148,27 @@
         sb.setLength(sb.length() - 2);
         sb.append(")");
         final String updateStatement = sb.toString();
-        // System.out.println(updateStatement);
-        try {
-            this.connection.setAutoCommit(false);
+        try {
             insertObjects = this.connection.prepareStatement(updateStatement, Statement.RETURN_GENERATED_KEYS);
             for (final ObjectType object : this) {
-                object.setCollectionId(this.collectionId);
+                try {
+                    object.setCollectionId(this.collectionId);
+                } catch (final ConditionsObjectException e) {
+                    throw new DatabaseObjectException("Error setting collection ID on object.", object);
+                }
                 for (int fieldIndex = 0; fieldIndex < this.getTableMetaData().getFieldNames().length; fieldIndex++) {
                     final String fieldName = this.getTableMetaData().getFieldNames()[fieldIndex];
-                    final Object value = object.getFieldValue(getTableMetaData().getFieldType(fieldName), fieldName);
-                    System.out.println(fieldName + "=" + value);
                     insertObjects.setObject(fieldIndex + 1,
                             object.getFieldValue(getTableMetaData().getFieldType(fieldName), fieldName));
                 }
                 insertObjects.executeUpdate();
-                this.connection.commit();
                 final ResultSet resultSet = insertObjects.getGeneratedKeys();
                 resultSet.next();
-                object.setId(resultSet.getInt(1));
-                // System.out.println("set id to " + resultSet.getInt(1) + " from generated keys");
+                object.setRowId(resultSet.getInt(1));
+
+                // This will commit the insert statements for the collections info table and the records.
+                this.connection.commit();
+
                 resultSet.close();
             }
         } catch (final SQLException e1) {
@@ -159,7 +177,6 @@
                 try {
                     System.err.println("Transaction is being rolled back ...");
                     this.connection.rollback();
-                    System.err.println("Done rolling back transaction!");
                 } catch (final SQLException e2) {
                     e2.printStackTrace();
                 }
@@ -168,24 +185,53 @@
             if (insertObjects != null) {
                 insertObjects.close();
             }
-        }
-    }
-
-    @Override
-    public boolean remove(final int index) {
-        final ObjectType object = get(index);
-        if (object != null) {
-            return this.objects.remove(object);
-        } else {
-            return false;
-        }
-    }
-
-    @Override
-    public void select(final int collectionId) throws SQLException, ConditionsObjectException {
-        // System.out.println("BaseConditionsObjectCollection.select - " + collectionId);
+            this.connection.setAutoCommit(true);
+        }
+    }
+
+    /**
+     * Add a row for a new collection in the <i>collections</i> table and return the new collection ID assigned to it.
+     *
+     * @param tableName the name of the table
+     * @param comment an optional comment about this new collection
+     * @return the collection's ID
+     * @throws SQLException
+     */
+    private synchronized int getNextCollectionId() throws SQLException, DatabaseObjectException {
+        final String log = "BaseConditionsObject generated new collection ID";
+        final String description = "inserted " + this.size() + " records into " + this.tableMetaData.getTableName();
+        PreparedStatement statement = null;
+        ResultSet resultSet = null;
+        int collectionId = -1;
+        try {
+            statement = this.connection.prepareStatement(
+                    "INSERT INTO collections (table_name, log, description, created) VALUES (?, ?, ?, NOW())",
+                    Statement.RETURN_GENERATED_KEYS);
+            statement.setString(1, this.tableMetaData.getTableName());
+            statement.setString(2, log);
+            statement.setString(3, description);
+            statement.execute();
+            resultSet = statement.getGeneratedKeys();
+            if (!resultSet.next()) {
+                throw new DatabaseObjectException("Failed to create new collection record.", this);
+            }
+            collectionId = resultSet.getInt(1);
+        } finally {
+            if (resultSet != null) {
+                resultSet.close();
+            }
+            if (statement != null) {
+                statement.close();
+            }
+        }
+        return collectionId;
+    }
+
+    @Override
+    public final boolean select(final int collectionId) throws SQLException, DatabaseObjectException {
         this.collectionId = collectionId;
         Statement statement = null;
+        boolean selected = false;
         try {
             statement = this.connection.createStatement();
             final StringBuffer sb = new StringBuffer();
@@ -196,7 +242,6 @@
             sb.setLength(sb.length() - 2);
             sb.append(" FROM " + this.tableMetaData.getTableName() + " WHERE collection_id=" + collectionId);
             final String sql = sb.toString();
-            // System.out.println(sql);
             final ResultSet resultSet = statement.executeQuery(sql);
             while (resultSet.next()) {
                 try {
@@ -204,12 +249,13 @@
                     newObject.setConnection(this.connection);
                     newObject.setTableMetaData(this.tableMetaData);
                     final int id = resultSet.getInt(1);
-                    newObject.setId(id);
+                    newObject.setRowId(id);
                     for (int fieldIndex = 0; fieldIndex < this.tableMetaData.getFieldNames().length; fieldIndex++) {
                         final String fieldName = this.tableMetaData.getFieldNames()[fieldIndex];
                         newObject.setFieldValue(fieldName, resultSet.getObject(fieldIndex + 2));
                     }
                     add(newObject);
+                    selected = true;
                 } catch (InstantiationException | IllegalAccessException e) {
                     throw new RuntimeException(e);
                 }
@@ -219,17 +265,38 @@
                 statement.close();
             }
         }
-    }
-
-    @Override
-    public void setCollectionId(final int collectionId) {
+        return selected;
+    }
+
+    @Override
+    public final void setCollectionId(final int collectionId) {
         this.collectionId = collectionId;
+        try {
+            // Set collection ID on all objects.
+            setConditionsObjectCollectionIds();
+        } catch (final ConditionsObjectException e) {
+            throw new RuntimeException("Error setting collection ID on object.", e);
+        }
+    }
+
+    private final void setConditionsObjectCollectionIds() throws ConditionsObjectException {
+        for (final ConditionsObject object : this) {
+            object.setCollectionId(this.collectionId);
+        }
+    }
+
+    private final void setConditionsObjectConnections() {
+        for (final ConditionsObject object : this) {
+            object.setConnection(this.connection);
+        }
     }
 
     @Override
     public void setConnection(final Connection connection) {
         this.connection = connection;
-        // TODO: This should set the connection on all objects also.
+
+        // Set connection on all objects.
+        setConditionsObjectConnections();
     }
 
     @Override
@@ -243,13 +310,17 @@
     }
 
     @Override
-    // FIXME: Should execute prepared statement in transaction.
-    public void updateAll() throws ConditionsObjectException, SQLException {
+    // FIXME: This method should execute a prepared statement in a transaction.
+    public boolean update() throws DatabaseObjectException, SQLException {
+        boolean updated = false;
         for (final ObjectType object : this.objects) {
             if (object.isDirty()) {
-                object.update();
-            }
-        }
+                if (object.update() && updated == false) {
+                    updated = true;
+                }
+            }
+        }
+        return updated;
     }
 
     /**
@@ -297,4 +368,41 @@
     public Iterator<ObjectType> iterator() {
         return this.objects.iterator();
     }
+
+    @Override
+    public boolean isDirty() {
+        return this.isDirty;
+    }
+
+    @Override
+    public boolean isNew() {
+        if (this.collectionId == BaseConditionsObject.UNSET_COLLECTION_ID) {
+            return true;
+        }
+        try {
+            return checkExists();
+        } catch (final SQLException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private boolean checkExists() throws SQLException {
+        Statement statement = null;
+        ResultSet resultSet = null;
+        boolean exists = false;
+        try {
+            statement = this.connection.createStatement();
+            resultSet = statement.executeQuery("SELECT id FROM " + this.tableMetaData.getTableName()
+                    + " where collection_id=" + this.collectionId);
+            exists = resultSet.next();
+        } finally {
+            if (statement != null) {
+                statement.close();
+            }
+            if (resultSet != null) {
+                resultSet.close();
+            }
+        }
+        return exists;
+    }
 }

Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObject.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObject.java	(original)
+++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObject.java	Wed Apr 22 16:39:56 2015
@@ -1,21 +1,13 @@
 package org.hps.conditions.api;
 
-import java.sql.Connection;
-import java.sql.SQLException;
-
-public interface ConditionsObject {
-
-    /**
-     * @return
-     * @throws ConditionsObjectException
-     * @throws SQLException
-     */
-    boolean delete() throws ConditionsObjectException, SQLException;
+public interface ConditionsObject extends DatabaseObject {
 
     /**
      * @return
      */
     Integer getCollectionId();
+
+    boolean hasValidCollection();
 
     FieldValues getFieldValues();
 
@@ -25,13 +17,6 @@
     int getRowId();
 
     /**
-     * @return
-     */
-    TableMetaData getTableMetaData();
-
-    /**
-     * q
-     *
      * @param type
      * @param name
      * @return
@@ -41,50 +26,19 @@
     <T> T getFieldValue(final String name);
 
     /**
-     * @return
-     * @throws ConditionsObjectException
-     * @throws SQLException
-     */
-    boolean insert() throws ConditionsObjectException, SQLException;
-
-    /**
-     * @return
-     */
-    boolean isDirty();
-
-    /**
-     * @return
-     */
-    boolean isNew();
-
-    boolean select(final int rowId) throws ConditionsObjectException, SQLException;
-
-    /**
      * @param collectionId
      * @throws ConditionsObjectException
      */
     void setCollectionId(int collectionId) throws ConditionsObjectException;
 
-    /**
-     * @param connection
-     */
-    void setConnection(Connection connection);
-
     void setFieldValues(FieldValues fieldValues);
 
-    void setId(int id);
-
-    void setTableMetaData(TableMetaData tableMetaData);
+    // FIXME: Maybe this should not be in the interface.
+    void setRowId(int id);
 
     /**
      * @param name
      * @param value
      */
     void setFieldValue(String name, Object value);
-
-    /**
-     * @return
-     * @throws ConditionsObjectException
-     */
-    boolean update() throws ConditionsObjectException, SQLException;
 }

Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObjectCollection.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObjectCollection.java	(original)
+++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObjectCollection.java	Wed Apr 22 16:39:56 2015
@@ -1,36 +1,20 @@
 package org.hps.conditions.api;
 
-import java.sql.Connection;
-import java.sql.SQLException;
 import java.util.Comparator;
 
-public interface ConditionsObjectCollection<ObjectType extends ConditionsObject> extends Iterable<ObjectType> {
+public interface ConditionsObjectCollection<ObjectType extends ConditionsObject> extends Iterable<ObjectType>,
+        DatabaseObject {
 
     boolean add(final ObjectType object);
-
-    void deleteAll() throws ConditionsObjectException, SQLException;
 
     ObjectType get(final int index);
 
     int getCollectionId();
 
-    TableMetaData getTableMetaData();
-
-    void insertAll(final int collectionId) throws ConditionsObjectException, SQLException;
-
-    boolean remove(final int index);
-
-    void select(final int collectionId) throws ConditionsObjectException, SQLException;
-
-    void setCollectionId(int collectionId);
-
-    void setTableMetaData(TableMetaData tableMetaData);
-
-    void setConnection(Connection connection);
+    // FIXME: Perhaps should not be part of interface.
+    void setCollectionId(int id);
 
     int size();
-
-    void updateAll() throws ConditionsObjectException, SQLException;
 
     void sort(final Comparator<ObjectType> comparator);
 

Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObjectUtilities.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObjectUtilities.java	(original)
+++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObjectUtilities.java	Wed Apr 22 16:39:56 2015
@@ -70,18 +70,16 @@
     public static Set<String> getFieldNames(final Class<? extends ConditionsObject> type) {
         final Set<String> fieldNames = new HashSet<String>();
         for (final Method method : type.getMethods()) {
-            System.out.println(method.getName());
             if (!method.getReturnType().equals(Void.TYPE)) {
                 for (final Annotation annotation : method.getAnnotations()) {
                     if (annotation.annotationType().equals(Field.class)) {
                         if (!Modifier.isPublic(method.getModifiers())) {
                             throw new RuntimeException("The method " + type.getName() + "." + method.getName()
-                                    + " has a Field annotation but is not public.");
+                                    + " has a Field annotation, but it is not public.");
                         }
                         final Field field = (Field) annotation;
                         for (final String fieldName : field.names()) {
                             if (fieldName != null && !"".equals(fieldName)) {
-                                System.out.println("  " + fieldName);
                                 fieldNames.add(fieldName);
                             }
                         }

Added: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/DatabaseObject.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/DatabaseObject.java	(added)
+++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/DatabaseObject.java	Wed Apr 22 16:39:56 2015
@@ -0,0 +1,62 @@
+package org.hps.conditions.api;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+public interface DatabaseObject {
+
+    /**
+     * Get the {@link TableMetaData} for this object.
+     *
+     * @return the {@link TableMetaData} for this object.
+     */
+    TableMetaData getTableMetaData();
+
+    void setTableMetaData(TableMetaData tableMetaData);
+
+    /**
+     * Insert the data of this object into the database.
+     * <p>
+     * This could be a single object or a collection of objects depending on the implementation.
+     *
+     * @throws ConditionsObjectException
+     * @throws SQLException
+     */
+    public void insert() throws DatabaseObjectException, SQLException;
+
+    /**
+     * Set the database <code>Connection</code> for the object.
+     *
+     * @param connection the database <code>Connection</code> for the object
+     */
+    void setConnection(Connection connection);
+
+    /**
+     * Return <code>true</code> if there are local data modifications that have not been persisted to the database.
+     *
+     * @return <code>true</code> if there un-persisted local data modifications
+     */
+    boolean isDirty();
+
+    /**
+     * Return <code>true</code> if the record
+     *
+     * @return
+     */
+    boolean isNew();
+
+    /**
+     * @return <code>true</code> if an update occurred
+     * @throws ConditionsObjectException
+     */
+    boolean update() throws DatabaseObjectException, SQLException;
+
+    /**
+     * @return
+     * @throws ConditionsObjectException
+     * @throws SQLException
+     */
+    void delete() throws DatabaseObjectException, SQLException;
+
+    boolean select(final int id) throws DatabaseObjectException, SQLException;
+}

Added: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/DatabaseObjectException.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/DatabaseObjectException.java	(added)
+++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/DatabaseObjectException.java	Wed Apr 22 16:39:56 2015
@@ -0,0 +1,16 @@
+package org.hps.conditions.api;
+
+public final class DatabaseObjectException extends Exception {
+
+    private final DatabaseObject object;
+
+    public DatabaseObjectException(final String message, final DatabaseObject object) {
+        super(message);
+        this.object = object;
+    }
+
+    public DatabaseObject getDatabaseObject() {
+        return this.object;
+    }
+
+}

Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/cli/AddCommand.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/cli/AddCommand.java	(original)
+++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/cli/AddCommand.java	Wed Apr 22 16:39:56 2015
@@ -8,8 +8,8 @@
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.Options;
-import org.hps.conditions.api.ConditionsObjectException;
 import org.hps.conditions.api.ConditionsRecord;
+import org.hps.conditions.api.DatabaseObjectException;
 import org.hps.conditions.api.FieldValuesMap;
 import org.hps.conditions.database.DatabaseConditionsManager;
 import org.lcsim.util.log.LogUtil;
@@ -161,13 +161,9 @@
             if (!DatabaseConditionsManager.getInstance().isConnected()) {
                 createdConnection = manager.openConnection();
             }
-            try {
-                conditionsRecord.insert();
-            } catch (final SQLException e) {
-                throw new RuntimeException("Error inserting new conditions record.", e);
-            }
+            conditionsRecord.insert();
             manager.closeConnection(createdConnection);
-        } catch (final ConditionsObjectException e) {
+        } catch (final SQLException | DatabaseObjectException e) {
             LOGGER.log(Level.SEVERE, "Error adding conditions record", e);
             throw new RuntimeException("An error occurred while adding a conditions record.", e);
         }

Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/cli/TagCommand.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/cli/TagCommand.java	(original)
+++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/cli/TagCommand.java	Wed Apr 22 16:39:56 2015
@@ -10,9 +10,9 @@
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.Options;
-import org.hps.conditions.api.ConditionsObjectException;
 import org.hps.conditions.api.ConditionsRecord;
 import org.hps.conditions.api.ConditionsRecord.ConditionsRecordCollection;
+import org.hps.conditions.api.DatabaseObjectException;
 import org.hps.conditions.api.TableMetaData;
 import org.hps.conditions.database.DatabaseConditionsManager;
 import org.lcsim.conditions.ConditionsManager.ConditionsNotFoundException;
@@ -23,7 +23,7 @@
  *
  * @author <a href="mailto:[log in to unmask]">Jeremy McCormick</a>
  */
-// TODO: Add multiple records strategy (last updated, last created, etc.).
+// TODO: Add command switch to specify multiple records strategy (last updated, last created, latest run end, etc.).
 public class TagCommand extends AbstractCommand {
 
     /**
@@ -158,8 +158,8 @@
         // Create the tag in the database if user verified or force option was present.
         if (makeTag) {
             try {
-                tagRecords.insertAll(-1); // FIXME: I guess insertAll should be overridden for ConditionsRecord.
-            } catch (ConditionsObjectException | SQLException e) {
+                tagRecords.insert();
+            } catch (DatabaseObjectException | SQLException e) {
                 throw new RuntimeException(e);
             }
         }

Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/database/ConditionsRecordConverter.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/database/ConditionsRecordConverter.java	(original)
+++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/database/ConditionsRecordConverter.java	Wed Apr 22 16:39:56 2015
@@ -6,9 +6,9 @@
 import org.hps.conditions.api.AbstractConditionsObjectConverter;
 import org.hps.conditions.api.BaseConditionsObjectCollection;
 import org.hps.conditions.api.ConditionsObject;
-import org.hps.conditions.api.ConditionsObjectException;
 import org.hps.conditions.api.ConditionsRecord;
 import org.hps.conditions.api.ConditionsRecord.ConditionsRecordCollection;
+import org.hps.conditions.api.DatabaseObjectException;
 import org.hps.conditions.api.TableMetaData;
 import org.lcsim.conditions.ConditionsManager;
 
@@ -68,9 +68,7 @@
                 // resultSet, tableMetaData);
                 collection.add(conditionsRecord);
             }
-        } catch (final SQLException x) {
-            throw new RuntimeException("Database error", x);
-        } catch (final ConditionsObjectException e) {
+        } catch (final DatabaseObjectException | SQLException e) {
             throw new RuntimeException("Error creating new conditions record.", e);
         }
 

Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/database/ConditionsSeriesConverter.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/database/ConditionsSeriesConverter.java	(original)
+++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/database/ConditionsSeriesConverter.java	Wed Apr 22 16:39:56 2015
@@ -4,10 +4,10 @@
 
 import org.hps.conditions.api.ConditionsObject;
 import org.hps.conditions.api.ConditionsObjectCollection;
-import org.hps.conditions.api.ConditionsObjectException;
 import org.hps.conditions.api.ConditionsRecord;
 import org.hps.conditions.api.ConditionsRecord.ConditionsRecordCollection;
 import org.hps.conditions.api.ConditionsSeries;
+import org.hps.conditions.api.DatabaseObjectException;
 import org.hps.conditions.api.TableMetaData;
 
 /**
@@ -93,7 +93,7 @@
                 collection.setTableMetaData(tableMetaData);
                 collection.setConnection(conditionsManager.getConnection());
                 collection.select(conditionsRecord.getCollectionId());
-            } catch (final ConditionsObjectException | SQLException e) {
+            } catch (final DatabaseObjectException | SQLException e) {
                 throw new RuntimeException(e);
             }
             series.add((ConditionsObjectCollection<ObjectType>) collection);

Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/dummy/DummyConditionsObject.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/dummy/DummyConditionsObject.java	(original)
+++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/dummy/DummyConditionsObject.java	Wed Apr 22 16:39:56 2015
@@ -5,7 +5,7 @@
 
 import org.hps.conditions.api.BaseConditionsObject;
 import org.hps.conditions.api.BaseConditionsObjectCollection;
-import org.hps.conditions.api.ConditionsObjectException;
+import org.hps.conditions.api.DatabaseObjectException;
 import org.hps.conditions.api.TableMetaData;
 import org.hps.conditions.database.Field;
 import org.hps.conditions.database.Table;
@@ -17,11 +17,12 @@
 public final class DummyConditionsObject extends BaseConditionsObject {
 
     public static class DummyConditionsObjectCollection extends BaseConditionsObjectCollection<DummyConditionsObject> {
+
         public DummyConditionsObjectCollection() {
         }
 
-        DummyConditionsObjectCollection(final Connection connection, final TableMetaData tableMetaData)
-                throws SQLException, ConditionsObjectException {
+        public DummyConditionsObjectCollection(final Connection connection, final TableMetaData tableMetaData)
+                throws SQLException, DatabaseObjectException {
             super(connection, tableMetaData, -1);
         }
     }

Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/svt/SvtConditionsLoader.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/svt/SvtConditionsLoader.java	(original)
+++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/svt/SvtConditionsLoader.java	Wed Apr 22 16:39:56 2015
@@ -128,11 +128,11 @@
                 // Set the collection ID.
                 final int collectionID = DatabaseConditionsManager.getInstance().getNextCollectionID(
                         SvtConditionsLoader.CALIBRATIONS_TABLE_NAME);
-                // calibrations.setCollectionId(collectionID);
+                calibrations.setCollectionId(collectionID);
                 logger.info("Using collection ID " + collectionID);
 
                 // Load the calibrations
-                calibrations.insertAll(collectionID);
+                calibrations.insert();
                 logger.info("A total of " + calibrations.size()
                         + " SvtCalibrations were loaded successfully into the database.");
 
@@ -167,11 +167,11 @@
                 // Set the collection ID
                 int collectionID = DatabaseConditionsManager.getInstance().getNextCollectionID(
                         SvtConditionsLoader.DAQ_MAP_TABLE_NAME);
-                // daqMapping.setCollectionId(collectionID);
+                daqMapping.setCollectionId(collectionID);
                 logger.info("Using collection ID " + collectionID);
 
                 // Load the DAQ map
-                daqMapping.insertAll(collectionID);
+                daqMapping.insert();
                 logger.info("DAQ map has been loaded successfully");
                 logger.fine(daqMapping.toString());
 
@@ -193,10 +193,10 @@
                 // Set the collection ID
                 collectionID = DatabaseConditionsManager.getInstance().getNextCollectionID(
                         SvtConditionsLoader.SVT_CHANNELS_TABLE_NAME);
-                // svtChannels.setCollectionId(collectionID);
+                svtChannels.setCollectionId(collectionID);
                 logger.info("Using collection ID " + collectionID);
 
-                svtChannels.insertAll(collectionID);
+                svtChannels.insert();
                 logger.info("A total of " + svtChannels.size()
                         + " SvtChannels were successfully loaded into the database.");
 

Added: java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectCollectionTest.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectCollectionTest.java	(added)
+++ java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectCollectionTest.java	Wed Apr 22 16:39:56 2015
@@ -0,0 +1,66 @@
+package org.hps.conditions.dummy;
+
+import java.sql.Connection;
+
+import junit.framework.TestCase;
+
+import org.hps.conditions.api.TableMetaData;
+import org.hps.conditions.api.TableRegistry;
+import org.hps.conditions.database.DatabaseConditionsManager;
+import org.hps.conditions.dummy.DummyConditionsObject.DummyConditionsObjectCollection;
+
+public class DummyConditionsObjectCollectionTest extends TestCase {
+
+    public void testBaseConditionsObjectCollection() throws Exception {
+
+        // Configure the conditions system.
+        final DatabaseConditionsManager manager = DatabaseConditionsManager.getInstance();
+        manager.setConnectionResource("/org/hps/conditions/config/jeremym_dev_connection.prop");
+        manager.setXmlConfig("/org/hps/conditions/config/conditions_database_no_svt.xml");
+        final Connection connection = manager.getConnection();
+
+        // Setup basic table meta data.
+        final TableMetaData tableMetaData = TableRegistry.getTableRegistry().findByTableName("dummy");
+
+        // Create a new collection.
+        final DummyConditionsObjectCollection collection = new DummyConditionsObjectCollection(connection,
+                tableMetaData);
+
+        // Add object to collection.
+        final DummyConditionsObject object1 = new DummyConditionsObject(connection, tableMetaData);
+        object1.setFieldValue("dummy", 1.0);
+        collection.add(object1);
+
+        // Add object to collection.
+        final DummyConditionsObject object2 = new DummyConditionsObject(connection, tableMetaData);
+        object2.setFieldValue("dummy", 2.0);
+        collection.add(object2);
+
+        // Insert all objects into the database.
+        collection.insert();
+
+        System.out.println(collection.size() + " objects inserted into " + collection.getCollectionId());
+
+        assertTrue("Collection isNew returned wrong value.", !collection.isNew());
+
+        // Create another collection.
+        final DummyConditionsObjectCollection anotherCollection = new DummyConditionsObjectCollection(connection,
+                tableMetaData);
+
+        // Select the previously created objects into this collection by using the collection_id value.
+        anotherCollection.select(collection.getCollectionId());
+        System.out.println("selected " + anotherCollection.size() + " objects into collection");
+
+        // Change the objects locally.
+        anotherCollection.get(0).setFieldValue("dummy", 3.0);
+        anotherCollection.get(1).setFieldValue("dummy", 4.0);
+
+        // Update all objects.
+        System.out.println("updating objects from collection " + collection.getCollectionId());
+        anotherCollection.update();
+
+        // Delete all objects.
+        System.out.println("deleting objects from collection " + collection.getCollectionId());
+        collection.delete();
+    }
+}

Added: java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectConverterTest.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectConverterTest.java	(added)
+++ java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectConverterTest.java	Wed Apr 22 16:39:56 2015
@@ -0,0 +1,48 @@
+package org.hps.conditions.dummy;
+
+import junit.framework.TestCase;
+
+import org.hps.conditions.api.TableMetaData;
+import org.hps.conditions.api.TableRegistry;
+import org.hps.conditions.database.DatabaseConditionsManager;
+import org.hps.conditions.dummy.DummyConditionsObject.DummyConditionsObjectCollection;
+
+/**
+ * @author <a href="mailto:[log in to unmask]">Jeremy McCormick</a>
+ */
+public class DummyConditionsObjectConverterTest extends TestCase {
+
+    public void testConditionsObjectConverter() throws Exception {
+
+        final DatabaseConditionsManager manager = DatabaseConditionsManager.getInstance();
+        manager.setConnectionResource("/org/hps/conditions/config/jeremym_dev_connection.prop");
+        manager.setXmlConfig("/org/hps/conditions/config/conditions_database_no_svt.xml");
+        manager.registerConditionsConverter(new DummyConditionsObjectConverter());
+        manager.setDetector("HPS-dummy-detector", 1);
+        manager.openConnection();
+
+        final TableMetaData tableMetaData = TableRegistry.getTableRegistry().findByTableName("dummy");
+
+        final DummyConditionsObjectCollection newCollection = new DummyConditionsObjectCollection();
+        newCollection.setTableMetaData(tableMetaData);
+        newCollection.setConnection(manager.getConnection());
+
+        final DummyConditionsObject object = new DummyConditionsObject(manager.getConnection(), tableMetaData);
+        object.setFieldValue("dummy", 1.2345);
+        newCollection.setCollectionId(1001);
+        newCollection.add(object);
+
+        try {
+            newCollection.insert();
+
+            final DummyConditionsObjectCollection readCollection = manager.getCachedConditions(
+                    DummyConditionsObjectCollection.class, "dummy").getCachedData();
+
+            System.out.println("got dummy collection " + readCollection.getCollectionId() + " with "
+                    + readCollection.size() + " objects");
+        } finally {
+            System.out.println("deleting collection " + newCollection.getCollectionId());
+            newCollection.delete();
+        }
+    }
+}

Added: java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectTest.java
 =============================================================================
--- java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectTest.java	(added)
+++ java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectTest.java	Wed Apr 22 16:39:56 2015
@@ -0,0 +1,148 @@
+package org.hps.conditions.dummy;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import junit.framework.TestCase;
+
+import org.hps.conditions.api.DatabaseObjectException;
+import org.hps.conditions.api.TableMetaData;
+import org.hps.conditions.api.TableRegistry;
+import org.hps.conditions.database.DatabaseConditionsManager;
+
+/**
+ * Perform create, select, update, and delete operations on the <code>BaseConditionsObject</code> class via a dummy
+ * conditions class implementation with a single double value.
+ *
+ * @author <a href="mailto:[log in to unmask]">Jeremy McCormick</a>
+ */
+public final class DummyConditionsObjectTest extends TestCase {
+
+    private static DatabaseConditionsManager manager;
+
+    @Override
+    public void setUp() {
+        // Configure the conditions system. This uses my local development database that is not globally accessible.
+        // --JM
+        manager = DatabaseConditionsManager.getInstance();
+        manager.setConnectionResource("/org/hps/conditions/config/jeremym_dev_connection.prop");
+        manager.setXmlConfig("/org/hps/conditions/config/conditions_database_no_svt.xml");
+    }
+
+    /**
+     * Dummy value.
+     */
+    private static final double DUMMY_VALUE1 = 1.0;
+
+    /**
+     * Another dummy value.
+     */
+    private static final double DUMMY_VALUE2 = 2.0;
+
+    /**
+     * Perform the test.
+     *
+     * @throws Exception if errors occurred when performing the test
+     */
+    public void testBaseConditionsObject() throws Exception {
+
+        // Open the database connection.
+        final Connection connection = manager.getConnection();
+
+        // Get table meta data.
+        final TableMetaData tableMetaData = TableRegistry.getTableRegistry().findByTableName("dummy");
+
+        assertNotNull("No meta data found for dummy table.", tableMetaData);
+
+        // Insert a new object.
+        final DummyConditionsObject newObject = new DummyConditionsObject(connection, tableMetaData);
+
+        assertEquals("The isNew method returned the wrong value.", true, newObject.isNew());
+
+        // The insert operation should fail with an error.
+        try {
+            newObject.insert();
+            throw new RuntimeException("The insert operation on an invalid object should have failed.");
+        } catch (final DatabaseObjectException e) {
+            System.err.println(e.getMessage());
+        }
+
+        // The delete operation should fail with an error.
+        try {
+            newObject.delete();
+            throw new RuntimeException("The delete operation on an invalid object should have failed.");
+        } catch (final DatabaseObjectException e) {
+            System.err.println(e.getMessage());
+        }
+
+        // The update operation should fail with an error.
+        final boolean updated = newObject.update();
+        assertTrue("The update operation on a new object should have failed.", !updated);
+
+        newObject.setFieldValue("collection_id", 42); /* Use an arbitrary collection ID value. */
+        newObject.setFieldValue("dummy", DUMMY_VALUE1);
+        newObject.insert();
+        System.out.println("Inserted object with id " + newObject.getRowId() + " into "
+                + newObject.getTableMetaData().getTableName() + " table.");
+        assertEquals("The isNew method returned the wrong value.", false, newObject.isNew());
+        assertEquals("The isDirty method returned the wrong value.", false, newObject.isDirty());
+        assertEquals("Object does not have a valid collection after insert.", true, newObject.hasValidCollection());
+
+        // Select into another object by ID.
+        final DummyConditionsObject anotherObject = new DummyConditionsObject(connection, tableMetaData);
+        final int rowId = newObject.getRowId();
+        anotherObject.select(rowId);
+        System.out.println("Selected row " + rowId + " into another object.");
+
+        // Check that the selection into another object worked.
+        assertEquals("Selected object has wrong row id.", newObject.getRowId(), anotherObject.getRowId());
+        assertTrue("Select object does not have valid collection.", anotherObject.hasValidCollection());
+        assertEquals("Selected object has wrong collection id.", newObject.getCollectionId(),
+                anotherObject.getCollectionId());
+        assertEquals("Selected object has wrong value.", newObject.getDummy(), anotherObject.getDummy());
+
+        // Update the same object.
+        newObject.setFieldValue("dummy", DUMMY_VALUE2);
+        System.out.println("Set dummy to " + DUMMY_VALUE2 + " in existing object.");
+        assertEquals("The value is wrong before update.", DUMMY_VALUE2, newObject.getDummy());
+        assertEquals("The isDirty method returned the wrong value.", true, newObject.isDirty());
+        assertEquals("The updated method returned the wrong value.", true, newObject.update());
+        assertEquals("The value is wrong after update.", DUMMY_VALUE2, newObject.getDummy());
+        assertEquals("The isDirty method returned the wrong value.", false, newObject.isDirty());
+
+        // Update which should be ignored on non-dirty record.
+        assertEquals("The update method should have returned false.", false, newObject.update());
+
+        // Select again into another object.
+        anotherObject.select(newObject.getRowId());
+
+        // Select into another object using the row ID.
+        assertEquals("Select object has wrong value after update.", newObject.getDummy(), DUMMY_VALUE2);
+
+        // Delete the object.
+        newObject.delete();
+
+        System.out.println("Deleted object.");
+
+        assertEquals("The isNew method returned the wrong value after delete.", true, newObject.isNew());
+
+        final boolean selected = anotherObject.select(rowId);
+        assertEquals("The select operation returned the wrong value after delete.", false, selected);
+    }
+
+    /**
+     * Cleanup the table used for the test.
+     */
+    @Override
+    public void tearDown() {
+        Statement statement = null;
+        try {
+            statement = manager.getConnection().createStatement();
+            statement.executeUpdate("DELETE from dummy");
+        } catch (final SQLException e) {
+            e.printStackTrace();
+        }
+        manager.closeConnection();
+    }
+}