Sneak preview about DORM, The Delphi ORM

My yesterday post about this busy time, have raised some interest about DORM, the Delphi ORM.

So, also if I still haven’t released any files, wish to expose some internals about DORM.

DORM is an implementation of the DataMapper design pattern written having Hibernate in mind.

It’s completely unit tested and have the following features:

Code is still under heavely development.

Those are 2 test-method to show the use of DORM:

TPerson = class(TObject)
...
property Phones: TdormCollection.... //implements IList
end;
 
TPhone = classs(TObject)
...
end;
 
//and now the unit tests
 
procedure TTestDORMHasMany.Setup;
begin
  Session := TSession.Create;
  Session.Configure(TStreamReader.Create('dorm.conf'));
end;
 
procedure TTestDORMHasMany.TearDown;
begin
  Session.Free;
end;
 
procedure TTestDORMHasMany.TestHasManyLazyLoad;
var
  p: TPerson;
  t: TPhone;
  guid: string;
begin
  p := TPerson.NewPersona;  //static method. Return a fully populated TPerson object
  try
    t := TPhone.Create;
    p.Phones.Add(t);
    Session.Save(p);
    guid := p.guid;  //GUIDs, or other PK types, are generated automagically by DORM. Obviously there is a specific class loaded to do this specified in the dorm.conf file)
  finally
    Session.Commit;
  end;
  Session.StartTransaction;
 
  // Test with lazy load ON
  Session.SetLazyLoadFor(TypeInfo(TPerson), 'Phones', true);
  p := Session.Load(TypeInfo(TPerson), guid) as TPerson;
  try
    CheckEquals(0, p.Phones.Count);
  finally
    Session.Commit;
  end;
 
  Session.StartTransaction;
  // Test with lazy load OFF
  Session.SetLazyLoadFor(TypeInfo(TPerson), 'Phones', false);
  p := Session.Load(TypeInfo(TPerson), guid) as TPerson; // Without commit, AV becouse IdentityMap doesn't work properly
  try
    CheckEquals(1, p.Phones.Count); // Child objects are loaded
  finally
    Session.Commit;
  end;
end;
 
procedure TTestDORMHasMany.TestLoadHasMany;
var
  list: IList;
  t, t1: TPhone;
  p: TPerson;
  guid: string;
begin
  p := TPerson.NewPersona;  //static method. Return a fully populated TPerson object
  try
    t := TPhone.Create;
    t.Numero := '555-7765123';
    t.Kind := 'Casa';
    p.Phones.Add(t);
 
    t1 := TPhone.Create;
    t1.Number := '555-7765123';
    t1.Kind := 'Casa';
    p.Phones.Add(t1);
    Session.Save(p); // save Person and Phones
    guid := p.guid;
  finally
    Session.Commit;
  end;
 
  Session.StartTransaction;
  p := Session.Load(TypeInfo(TPerson), guid) as TPerson;
  try
    CheckEquals(2, p.Phones.Count);
  finally
    Session.Commit;
  end;
end;
 

Mapping, contained in a file called “dorm.conf”, is similar to the following:

{
  "persistence": {
    "database_adapter": "dorm.adapter.Firebird.TFirebirdPersistStrategy",
    "database_connection_string":"127.0.0.1:C:\MyProjects\DORM\experiments\dorm.fdb",
    "username": "sysdba",
    "password":"masterkey"
    },
  "config": {
    "package": "dorm.bo.Person",
    "logger_class_name": "dorm.loggers.FileLog.TdormFileLog"
  },      
  "mapping":
    {      
      "TPerson":
      {
        "table": "people",
        "id": {"name":"guid", "field":"guid", "field_type":"string", "size": 100, "default_value": ""},
        "fields":[
          {"name":"firstname", "field":"first_name", "field_type":"string", "size": 100},
          {"name":"lastname", "field":"last_name", "field_type":"string", "size": 100},
          {"name":"age", "field":"age", "field_type":"integer"},
          {"name":"borndate", "field":"born_date", "field_type":"date"}
          ],
        "has_many":[{
          "name": "Phones",
          "class_name":"TPhone",
          "child_field_name":"guid_person",
          "lazy_load": false
        }],
        "has_one": {
          "name": "car",
          "class_name":"TCar",
          "child_field_name":"guid_person"
        }
      },
      "TPhone":
      {
        "table": "phones",
        "id": {"name":"guid", "field":"guid", "field_type":"string", "size": 100, "default_value": ""},
        "fields":[
          {"name":"number", "field":"number", "field_type":"string", "size": 100},
          {"name":"kind", "field":"kind", "field_type":"string", "size": 100},
          {"name":"guid_person", "field":"guid_person", "field_type":"string", "size": 100}
          ]
      }
    }
}

The PODO (Plain Old Delphi Objects) can be binded to the VCL controls with a set of MediatingView (Model-GUI-Mediator Pattern) with an Observer mechanism to mantain things in synch.

Any comments? Someone interested?

Comments

comments powered by Disqus