Sneak preview about DORM, The Delphi ORM

Design Patterns, Programming, RTTI 18 Comments »

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:

  • External file mapping. (JSON format)
  • Persistence ignorance (every TObject can be persisted)
  • Support for One-One and One-Many relations (still no many-many)
  • Support for LazyLoading (you can enable§/disable lazyloading by file or by code by per-basis needs)
  • Support for IdentityMap
  • Support for custom “finder” (you can still use complex SQL if you want)
  • Complete support for CRUD
  • Transactions
  • Built in logging system to log *EVERY* sql or action performed by the framework
  • Opened to multiple data access strategies (interfaced based, not inheritance based) for use with different database (now I’ve developed the firebird one using DBX)
  • Caching for RTTI (the TSession object have a single TRttiContext holding ALL metadata)

Code is still under heavely development.

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

  1. TPerson = class(TObject)
  2. property Phones: TdormCollection…. //implements IList
  3. end;
  4.  
  5. TPhone = classs(TObject)
  6. end;
  7.  
  8. //and now the unit tests
  9.  
  10. procedure TTestDORMHasMany.Setup;
  11. begin
  12.   Session := TSession.Create;
  13.   Session.Configure(TStreamReader.Create('dorm.conf'));
  14. end;
  15.  
  16. procedure TTestDORMHasMany.TearDown;
  17. begin
  18.   Session.Free;
  19. end;
  20.  
  21. procedure TTestDORMHasMany.TestHasManyLazyLoad;
  22. var
  23.   p: TPerson;
  24.   t: TPhone;
  25.   guid: string;
  26. begin
  27.   p := TPerson.NewPersona;  //static method. Return a fully populated TPerson object
  28.   try
  29.     t := TPhone.Create;
  30.     p.Phones.Add(t);
  31.     Session.Save(p);
  32.     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)
  33.   finally
  34.     Session.Commit;
  35.   end;
  36.   Session.StartTransaction;
  37.  
  38.   // Test with lazy load ON
  39.   Session.SetLazyLoadFor(TypeInfo(TPerson), 'Phones', true);
  40.   p := Session.Load(TypeInfo(TPerson), guid) as TPerson;
  41.   try
  42.     CheckEquals(0, p.Phones.Count);
  43.   finally
  44.     Session.Commit;
  45.   end;
  46.  
  47.   Session.StartTransaction;
  48.   // Test with lazy load OFF
  49.   Session.SetLazyLoadFor(TypeInfo(TPerson), 'Phones', false);
  50.   p := Session.Load(TypeInfo(TPerson), guid) as TPerson; // Without commit, AV becouse IdentityMap doesn't work properly
  51.   try
  52.     CheckEquals(1, p.Phones.Count); // Child objects are loaded
  53.   finally
  54.     Session.Commit;
  55.   end;
  56. end;
  57.  
  58. procedure TTestDORMHasMany.TestLoadHasMany;
  59. var
  60.   list: IList;
  61.   t, t1: TPhone;
  62.   p: TPerson;
  63.   guid: string;
  64. begin
  65.   p := TPerson.NewPersona;  //static method. Return a fully populated TPerson object
  66.   try
  67.     t := TPhone.Create;
  68.     t.Numero := '555-7765123';
  69.     t.Kind := 'Casa';
  70.     p.Phones.Add(t);
  71.  
  72.     t1 := TPhone.Create;
  73.     t1.Number := '555-7765123';
  74.     t1.Kind := 'Casa';
  75.     p.Phones.Add(t1);
  76.     Session.Save(p); // save Person and Phones
  77.     guid := p.guid;
  78.   finally
  79.     Session.Commit;
  80.   end;
  81.  
  82.   Session.StartTransaction;
  83.   p := Session.Load(TypeInfo(TPerson), guid) as TPerson;
  84.   try
  85.     CheckEquals(2, p.Phones.Count);
  86.   finally
  87.     Session.Commit;
  88.   end;
  89. end;

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

  1. {
  2.   "persistence": {
  3.     "database_adapter": "dorm.adapter.Firebird.TFirebirdPersistStrategy",
  4.     "database_connection_string":"127.0.0.1:C:\\MyProjects\\DORM\\experiments\\dorm.fdb",
  5.     "username": "sysdba",
  6.     "password":"masterkey"
  7.     },
  8.   "config": {
  9.     "package": "dorm.bo.Person",
  10.     "logger_class_name": "dorm.loggers.FileLog.TdormFileLog"
  11.   },      
  12.   "mapping":
  13.     {      
  14.       "TPerson":
  15.       {
  16.         "table": "people",
  17.         "id": {"name":"guid", "field":"guid", "field_type":"string", "size": 100, "default_value": ""},
  18.         "fields":[
  19.           {"name":"firstname", "field":"first_name", "field_type":"string", "size": 100},
  20.           {"name":"lastname", "field":"last_name", "field_type":"string", "size": 100},
  21.           {"name":"age", "field":"age", "field_type":"integer"},
  22.           {"name":"borndate", "field":"born_date", "field_type":"date"}
  23.           ],
  24.         "has_many":[{
  25.           "name": "Phones",
  26.           "class_name":"TPhone",
  27.           "child_field_name":"guid_person",
  28.           "lazy_load": false
  29.         }],
  30.         "has_one": {
  31.           "name": "car",
  32.           "class_name":"TCar",
  33.           "child_field_name":"guid_person"
  34.         }
  35.       },
  36.       "TPhone":
  37.       {
  38.         "table": "phones",
  39.         "id": {"name":"guid", "field":"guid", "field_type":"string", "size": 100, "default_value": ""},
  40.         "fields":[
  41.           {"name":"number", "field":"number", "field_type":"string", "size": 100},
  42.           {"name":"kind", "field":"kind", "field_type":"string", "size": 100},
  43.           {"name":"guid_person", "field":"guid_person", "field_type":"string", "size": 100}
  44.           ]
  45.       }
  46.     }
  47. }

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?

Using AMQP from Delphi with ZeroMQ

Delphi for Win32, Design Patterns, Programming, Uncategorized 8 Comments »

The Advanced Message Queuing Protocol (AMQP) is an open standard application layer protocol for Message Oriented Middleware (MoM).

The defining features of AMQP are message orientation, queuing, routing (including point-to-point and publish-and-subscribe), reliability and security.

The good news about AMQP is that AMQP mandates the behaviour of the messaging provider and client to the extent that implementations from different vendors are truly interoperable, in the same way as SMTP, HTTP, FTP, etc. have created interoperable systems.

In a so “Open” market, live an interesting project called ZeroMQ.

In a my recent Delphi project, I must choice a thin and fast messaging system, ZeroMQ has been the choice.

However, ZeroMQ has not the Delphi client for talking with the broker, so I decided to write my own.

ZeroMQ is very fast but doesn’t support some enteprise features like users management and message persistence, but is very simple to use and to intergate in a legacy system.

For example, with my wrapper, a simple “sender” is like following:

  1. zmq := TZeroMQ.Create;
  2. try
  3. zmq.Open('localhost');
  4. ex := zmq.CreateLocalExchange('MyExchange', zmqStyleDataDistribution);
  5. zmq.Bind('MyExchange', 'GlobalQueue');
  6. zmq.Send(ex, 'Hello World From Delphi');
  7. finally
  8. zmq.Free;
  9. end;

And a simple receiver is simple as follow:

  1. zmq := TZeroMQ.Create;
  2. try
  3. zmq.Open('localhost');
  4. ex := zmq.CreateLocalQueue('LocalQueue');
  5. zmq.Bind('GlobalExchange', 'LocalQueue');
  6. zmq.Receive(msg, msgtype, msgsize, zmqBlocking);
  7. WriteLn(msg);  //we are in a console application
  8. finally
  9. zmq.Free;
  10. end;

In the distribution there are a complete set of examples including a simple “Chat” application.

ZeroMQ is primarily intended to power stock trading business, this is the reason becouse is very fast.

To use ZeroMQ you need the ZeroMQ server downloadable from http://www.zeromq.org/ where you can find additional info about Exchange and Queue configuration and binding.

Wrapper (beta) can be downloaded from the ZeroMQ section.

Comments and fix for the wrapper are very apreciated.

Have fun and happy messaging :-)

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in