Reading the Los Techies article “Don’t write your own ORM” by Jimmy Bogard I was reminded of the best ORM I ever worked with, an ORM that is now, most likely, lost to the mists of time.

Back in 1998 I started working as a Delphi developer for a company that had it’s own in-house tool for handling the mapping between object and database, a tool that was already cross platform, having been ported from the original system to Delphi.

Pay attention to that date - it’s well before object oriented development was popular, and long before most modern ORMs were more than a gleam in their developer’s eye. In fact, the very term ORM was unknown to us - we referred to it as the data access layer, but any modern developer would immediately classify it as an ORM.

One of the key features of this ORM is something that would be considered heretical by most modern developers - it wasn’t based on POCO/POJO semantics. Not only was there a distinct base class used for all persisted objects, but the properties exposed by the objects weren’t simple values but full objects in their own right.

Let’s consider what a simple Person class might have looked like - a C# declaration (if there had ever been a version ported to .NET) might have looked like this:

public class Person : DataAccessObject
{
    public Property<string> FullName { get; set; }
    public Property<string> FamilyName{ get; set; }
    public Property<string> KnownAs { get; set; }
}

While this looks slightly complex, it wasn’t a burden as we had a visual designer for creating our classes and all the required code declarations were generated on demand.

Since each property was a full object, we had access to a lot more information about each one than you have in a POCO/POJO environment. We could, for example, easily tell if a property had been modified since loading, or what the original value was. A C# declaration for Property<T> might look like this:

public  class Property<T> 
{
    // The current value of this property
    public T Value { get; set; }

    // The original value of this property as loaded from the database
    public T OriginalValue { get; set; }

    // Has this property been modified?
    public bool Modified { get; }

    // Cancel any changes made to this property since loading from the database
    public void Revert();
}

As you’ll probably appreciate, these capabilities were very useful both server side and client side - and yes, we had the same object model available on both the server and client.

Associations between objects were also implemented with dedicated objects, allowing any developer to easily access related objects, or even detect whether an association had been loaded or note.

If we had a customer object in an ordering system, the declaration might look like this:

public class Customer : DataAccessObject
{
    // Who is the customer
    public ToOne<Person> Person { get ; }
	
    // What orders has this customer placed?
    public ToMany<Order> HasPlacedOrders{ get; }
}

Associations to collections of objects were especially powerful, with constructs allowing the number of associated objects to be accessed without loading the collection prematurely.

public class ToMany<T>
{
    // Return the count of items associated. 
    // Will load the association if they haven't already been loaded
    public int Count { get; set; }
	
    // Return the count of items associated.
    // If the association has not been loaded, will query the database for the count
    public int Size { get; set; }
	
    // Check if this association has already been loaded
    public bool Loaded { get; set; }
	
    // Indexed access to the objects associated
    // Will load the association if they haven't already been loaded
    public T this[int index]; 
	
    // Has this association been modified? 
    public bool Modified { get; }

    // Cancel any changes made to this association since loading from the database
    public void Revert();
}

One of the classic issues with using an ORM is the Select N+1 problem, where performance of a loop is poor because of a database hit every iteration. Avoiding this problem was relatively easy, as we could use a block load to load a specific association for a collection of objects.

If we wanted to load the HasPlacedOrders association for a list of Customers, we’d have written code like this:

var vipCustomers = 
var orders = ToMany.BlockLoad(vipCustomers, "HasPlacedOrders");

I spent several years working with this ORM, ending up as one of the maintainers of it’s Delphi codebase and watching as it was ported to a third platform, Java. Working with POCO/POJO based ORMs (including Nhibernate) in the years since has been a productive experience, but it’s never been quite as powerful or easy to use as the original I learnt way back when.

Comments

blog comments powered by Disqus
Next Post
DEV311 Snippet Demos  07 Sep 2012
Prior Post
Computer Abuse and Safety of Data  04 Aug 2012
Related Posts
Using Constructors  27 Feb 2023
An Inconvenient API  18 Feb 2023
Method Archetypes  11 Sep 2022
A bash puzzle, solved  02 Jul 2022
A bash puzzle  25 Jun 2022
Improve your troubleshooting by aggregating errors  11 Jun 2022
Improve your troubleshooting by wrapping errors  28 May 2022
Keep your promises  14 May 2022
When are you done?  18 Apr 2022
Fixing GitHub Authentication  28 Nov 2021
Archives
August 2012
2012