Thursday, December 22, 2016The ability to lazy-load an association is placed at number 8 in the list of My Top 10 Aurelius Features.
Suppose you have a TContact class like the following:
And you use the following code to retrieve a list of contacts and get the name of the country of the first contact (to reduce size, the code doesn't check for potential errors or release memory):
type TContact = class private FId: integer; FName: string; FCountry: TCountry; public property Id: integer read FId write FId; property Name: string read FName write FName; property Country: TCountry read FCountry write FCountry; end;
By default, when the first line is executed, Aurelius builds and executes an SQL statement retrieving column values from Contact table and Country table, all at once, and all entities (contacts and countries) are created and instantiated:
// Get all contacts MyContacts := Manager.Find<TContact>.List; // Get name of country of first contact: FirstContactCountryName := MyContacts.Country.Name;
When the second line is executed (when we retrieve the Country name), all data is already in memory. This is useful in many situations, but there might be cases where doing several LEFT JOIN for many associations and retrieving data for all records is not desired. If I'm not going to get the country name for all contacts I retrieve, for example, why would I want to retrieve all country names in advance?
SELECT A.ID AS A_ID, A.NAME AS A_NAME, A.COUNTRY_ID AS A_COUNTRY_ID, B.ID AS B_ID, B.NAME AS B_NAME FROM CONTACT A LEFT JOIN COUNTRY B ON (B.ID = A.COUNTRY_ID)
The lazy-loading feature gives you that flexibility. You just reimplement your class using a special Proxy type:
If we execute the code that retrieve contacts again, this is the SQL executed by Aurelius:
type TContact = class private FId: integer; FName: string; FCountry: Proxy<TCountry>; function GetCountry: TCountry; procedure SetCountry(const Value: TCountry); public property Id: integer read FId write FId; property Name: string read FName write FName; property Country: TCountry read GetCountry write SetCountry; end; function TContact.GetCountry: TCountry; begin Result := FCountry.Value; end; procedure TContact.SetCountry(const Value: TCountry); begin FCountry.Value := Value; end;
No data from country was retrieved. Only when the second line is executed, Aurelius executes an extra SQL statement:
SELECT A.ID AS A_ID, A.NAME AS A_NAME, A.COUNTRY_ID AS A_COUNTRY_ID FROM CONTACT A
and then the country object is populated. Now you have the two options and you can fine tune your application for the best situation. It's also worth note that even when you have mapped your class to lazy-load a specific association, in some queries you can force Aurelius to still load all objects at once. The following code will use LEFT JOIN and retrieve all countries in the same SQL statement:
SELECT A.ID AS A_ID, A.NAME AS A_NAME FROM COUNTRY A
Don't forget to subscribe to our YouTube channel and receive notifications about upcoming videos!
// Get all contacts and countries at once, // regardless of lazy-load mode in mapping MyContacts := Manager.Find<TContact> .CreateAlias('Country', 'c', TFetchMode.Eager) .List; // Get name of country of first contact: FirstContactCountryName := MyContacts.Country.Name;
Wagner R. Landgraf
This blog post has received 2 comments.
Previous | Next | Index