Show/Hide Toolbars

TMS Aurelius Documentation

Navigation: Mapping

Associations and Lazy-Loading

Scroll Prev Top Next More

Aurelius supports associations between objects, which are mapped to foreign keys in the database. Suppose you have the following TInvoice class:

 

TInvoice = class
private

  FCustomer: TCustomer;

  FInvoiceItems: TList<TInvoiceItem>;

 

the class TInvoice has an association to the class TCustomer. By using Association mapping attribute, you can define this association and Aurelius deals with it automatically - customer data will be saved in its own table, and in Invoice table only thing saved will be a value in a foreign key field, referencing the primary key in Customer table.

 

Also, TInvoice has a list of invoice items, which is also a type of association. You can define such lists using ManyValuedAssociation mapping attribute. In this case, the TInvoiceItem objects in the list will have a foreign key referencing the primary key in InvoiceTable.

 

Eager Loading

 

When an object is retrieved from the database, its properties are retrieved and set. This is also true for associations. By default, eager-loading is performed, which means associated objects and lists are loaded and filled when object is loaded. In the TInvoice example above, when a TInvoice instance is loaded, Aurelius also creates a TCustomer instance, fill its data and set it to the FCustomer field. Aurelius uses a single SQL statement to retrieve data for all associations. FInvoiceItems list is also loaded. In this case, an extra SELECT statement is performed to load the list.

 

Lazy Loading

 

You can optionally define associations to be lazy-loaded. This means that Aurelius will not retrieve association data from database until it's really needed (when the property is accessed). You define lazy-loading associations this way:

 

1. Declare the class field as a Proxy<TMyClass> type, instead of TMyClass (Proxy<T> type is declared in unit Aurelius.Types.Proxy)

2. Declare the Association (or ManyValuedAssociation) attribute above the field, and define fetch mode as lazy in attribute parameters

3. Declare a property of type TMyClass with getter and setter that read/write from/to the proxy value field.

 

Example:

 

TMediaFile = class
private
  [Association([TAssociationProp.Lazy], [])]
  [JoinColumn('ID_ALBUM', [])]
  FAlbum: Proxy<TAlbum>;
 
  function GetAlbum: TAlbum;
  procedure SetAlbum(const Value: TAlbum);
public
  property Album: TAlbum read GetAlbum write SetAlbum;
  
implementation
 
function TMediaFile.GetAlbum: TAlbum;
begin
  Result := FAlbum.Value;
end;
 
procedure TMediaFile.SetAlbum(const Value: TAlbum);
begin
  FAlbum.Value := Value;
end;

 

In the example above, Album will not be loaded when TMediaFile object is loaded. But if in Delphi code you do this:

 

TheAlbum := MyMediaFileObject.Album;

 

then Aurelius will perform an extra SELECT statement on the fly, instantiate a new TAlbum object and fill its data.

 

Lazy loading lists

 

Lists can be set as lazy as well, which means the list will only be filled when the list object is accessed. It works in a very similar way to lazy-loading in normal associations. The only difference is that since you might need an instance to the TList object to manipulate the collection, you must initialize it and then destroy it. Note that you should not access Value property directly when creating/destroying the list object. Use methods SetInitialValue and DestroyValue. The code below illustrates how to do that.

 

TInvoice = class
private
  [ManyValuedAssociation([TAssociationProp.Lazy], CascadeTypeAll)]
  [ForeignJoinColumn('INVOICE_ID', [TColumnProp.Required])]
  FItems: Proxy<TList<TInvoiceItem>>;
private
  function GetItems: TList<TInvoiceItem>;
public
  constructor Create; virtual;
  destructor Destroy; override;
  property Items: TList<TInvoiceItem> read GetItems;
end;
 
implementation
 
constructor TInvoice.Create;
begin
  FItems.SetInitialValue(TList<TInvoiceItem>.Create);
end;
 
destructor TInvoice.Destroy;
begin
  FItems.DestroyValue;
  inherited;
end;
 
function TInvoice.GetItems: TList<TInvoiceItem>;
begin
  result := FItems.Value;
end;

 

Proxy<T> Available property

 

Available property allows you to check if proxy object is available, without forcing it be loaded. If Available is true, it means that the proxy object is already available in memory, even if it's empty. If it's false, it means the object is not available in memory and a request must be performed to load the content. In other words, Available property indicates if accessing the object will fire a new server request to retrieve the object.

 

Proxy<T> Key property

 

You can read Key property directly from the Proxy<T> value to get the database values for the foreign key used to load this proxy. This way you have access to the underlying database value without needing to force the proxy to load. Note that Key might not be always available - it will be filled by the object manager when the data is loaded from the database. If you set the proxy value manually, Key value might differ from the actualy id of the object in Proxy<T>.