John Deer Office (JDO)


It was probably most weird project from requirements point of view and the most fun to implement. Old version of JDO (farmer's application for tracking all farm-related activity) worked with flat files and they realized that they needed to move to relational database. However, their development team still thought that database is evil and didn't want to touch it at all. Finally, they contracted VantagePoint Network (John Deer web development branch) to develop a database access layer, so that JDO team was able to search, get results and update data without single sql query. Later they also wanted multiple transactions with save points but without single writing to database until they call commit. It meant building a full-featured transactional database in memory and I got a chance to volunteer myself first, escaping (almost) boring implementation of mapping of our classes (Farm, Field, etc.) with database tables. Also, I was responsible for building storage objects for all data objects, establishing relations and communication (messaging) between them, sorting, filtering, final dll build and transferring encrypted data in xml format via internet.

Bellow you can find more detailed description of main classes and concepts. In short it looks like this. All data objects were inherited from MObject and knew about their status and size. MRecord was a container for any of data object and can restore it to original state. MRollbackStack was built based on multimap, LIFO type collection of changed objects and was able to rollback either all or current changes in memory. MJDOffice had entry points to high level objects such as MClient (MClinet had link to MFarm, MFarm to MField, etc.). MDBStorage was a wrap of ODBC functions and worked directly with database.



MObject

Base class for objects which belong to a JDOffice database. All specific classes of objects managed by the database, containing attributes including those that reference other objects or contain lists of objects, are subclasses of MObject. The subclass provides "Get" and "Set" functions for each distinct attribute value on that type of model object, such as a farm or field. Relationships are implemented by single- or multi-valued attribute values, which return a single related object (of some related type), or a list of such objects, respectively. If the attribute value is a list of related objects, there is always an attribute on the related object that links it back to the referring object. The members of the list are automatically maintained to contain all the related objects that refer back to it. For example, assigning the farm attribute to a field automatically includes it in the associated fields list for its farm owner.

Access to existing model objects is always obtained by retrieving object pointers from some other object or list of objects. No constructors are required or provided for MObject subclasses; pointers are retrieved from other objects instead. The MJDOffice object contains functions to obtain selected lists of objects having global scope within the model, such as clients or crop types. All object pointers within the model are ultimately obtained from objects retrieved from one of these lists, except when they are first created as new objects within the database.

The first creation of a new model object to be added to the database is accomplished by "factory" functions that create the new object, link it to the appropriate place in the database, and add it as a member of any list to which it belongs. The "factory" function for any MObject subclass is not contained on the subclass itself, but on a parent object that must be present for the object to be created. For example, the factory function for an MFarm object is contained on the MClient object. Objects that exist at the global level of the model, such as clients themselves or supporting reference items such as crop types, have their factory functions on the MJDOffice class, assuming that a create capability is permitted for them at all.

Two different forms of delete capability are also provided for selected MObject subclasses. Deleting an object automatically removes it from any associated lists.

All creation, deletion, and update of model objects is under the control of a global change stack maintained within MJDOffice. Changes are not actually written to the database until requested by a save function on the MJDOffice object.

MJDOffice

Class for top-level object attached to a JDOffice database. All objects representing the state of a JDOffice database are retrieved either directly or indirectly from a MJDOffice object. Multiple MJDOffice objects may be open at once but objects from one must never be used against another.

SavePendingChanges() - Saves all pending changes at top level of change stack. Pops the top level of the stack to the same state before the most recent call to #PushPendingChanges#(), if any. If set of pending changes refers to any newly created objects in an underlying level of the change stack, a database error will occur

SaveAllChanges() - Saves all pending changes at top level of change stack.

CancelPendingChanges() - Cancels all pending changes at top level of change stack. Pops the top level of the stack to the same state before the most recent unpopped call to PushPendingChanges(), if any.

CancelAllChanges () - Cancels all pending changes.

PushPendingChanges() - Creates a new top level of the change stack. The new level will contain all further changes made until a subsequent call to SavePendingChanges() or CancelPendingChanges(). Pushes the previous state of all pending changes to just below the new top level.

MDBStorage

This class contains the ODBC connection to the database.

MRollbackStack

Keeps copy of objects before changes were made. For current level of transaction only one copy of object is kept in stack regardless how many changes were made.

MRecord

is used for keeping snapshot of object (inherited from MObject) in memory before changes were made.