• Linas Naginionis
Software Craftsmanship

Mapping rows in ORM

Mapping rows in ORM

Marshmallow ORM framework from Spring4D just received a new interface, called IRowMapper<T>. What is it and why do we need it?

IRowMapper<T>

Description of the IRowMapper<T> from the official code documentation:

Represents interface for mapping rows of a ResultSet on a per-row basis. Implementations of this interface perform the actual work of mapping each row to a result object.

And how it is defined in Spring.Persistence.Core.Interfaces unit:

/// <summary>
 /// Represents interface for mapping rows of a ResultSet on a per-row basis.
 /// Implementations of this interface perform the actual work of mapping each row to a result object.
 /// </summary>
 IRowMapper<T: class, constructor> = interface(IInvokable)
 ['{557EE245-3542-40EC-86B5-16B1A3EA902A}']
 /// <summary>
 /// Implementations must implement this method to map each row of data in the ResultSet.
 /// Params. resultSet: the ResultSet to map (pre-initialized for the current row)
 /// </summary>
 function MapRow(const resultSet: IDBResultSet): T;

Basically when you are using Marshmallow (MM) ORM framework, it performs all the mapping for you. You don’t need to worry how it’s done, you just need to annotate your entities with ORM specific attributes. But in some cases you might want to have a full control on the mapping process. There might be a few reasons for this:

  • Since MM maps entities using RTTI, it introduces some performance penalty which may not be desirable. Mapping you entities manually can speed-up your data reads significantly.
  • You don’t need to update or insert new data (of course you can also use IRowMapper only for select queries and ORM mapping for update or insert queries). If so, you could decide to go even further and do not annotate your entities with MM attributes. In this case you will need to use hand-written queries and your row mappers when retrieving data.

 Usage example

Suppose we have a simple TProduct class:

 [Entity]
 [Table('Products')]
 TProduct = class
 private
   [Column('PRODID', [cpRequired, cpPrimaryKey, cpNotNull])] [AutoGenerated]
   FId: Integer;
 private
   FName: string;
   FPrice: Double;
 public
   property ID: Integer read FId write FId;
   [Column('PRODNAME')]
   property Name: string read FName write FName;
   [Column('PRODPRICE')]
   property Price: Double read FPrice write FPrice;
 end;

Now we can define our TProductRowMapper which implements IRowMapper<TProduct> interface:

TProductRowMapper = class(TInterfacedObject, IRowMapper<TProduct>)
protected
   function MapRow(const resultSet: IDBResultSet): TProduct;
end;
{ TProductRowMapper }
function TProductRowMapper.MapRow(const resultSet: IDBResultSet): TProduct;
begin
  Result := TProduct.Create;
  Result.ID := resultSet.GetFieldValue('PRODID');
  Result.Name := resultSet.GetFieldValue('PRODNAME');
  Result.Price := resultSet.GetFieldValue('PRODPRICE');
end;

And finally, we need to register it within our TSession and get the data from the database:

fSession.RegisterRowMapper<TProduct>(TProductRowMapper.Create);
products := fSession.FindAll<TProduct>; // TProductRowMapper will be used to map TProduct entities

You can find Marshmallow in Spring4D bitbucket repository, feature/marshmallow branch. Note, that framework is under big changes regarding integration with Spring4D, so some things might not work yet.

If you have some questions of thoughts, please write a comment.

Leave a Reply

Your email address will not be published. Required fields are marked *

*