Blog
All Blog Posts | Next Post | Previous PostCrash Course TMS Aurelius – Getting Started
Wednesday, February 6, 2013
Even though TMS Aurelius provides extensive documentation, I sometimes receive requests to provide more examples, sample codes and explanations about how to accomplish some daily tasks. Thus, I will start a series of posts about how to use TMS Aurelius. Everything is already covered in the documentation, but here I will try not to provide complete, technical, “official” coverage of a feature, but plainly explain what it is for instead by giving real-world examples etc. - in summary, a different, hands-on way of showing things.I will start right from the beginning. I want to show a small application using TMS Aurelius. Having an entity/a model class TCustomer, mapped as follows:
unit Customer; interface uses Aurelius.Mapping.Attributes; type [Entity, Automapping] TCustomer = class private FId: integer; FName: string; public property Id: integer read FId write FId; property Name: string read FName write FName; end; implementation end.
program GettingStarted; {$APPTYPE CONSOLE} uses Aurelius.Drivers.Interfaces, Aurelius.Drivers.SQLite, Aurelius.Engine.DatabaseManager, Aurelius.Engine.ObjectManager, Aurelius.SQL.SQLite, Customer; var Connection: IDBConnection; Manager: TObjectManager; Customer: TCustomer; begin Connection := TSQLiteNativeConnectionAdapter.Create('test.db'); Manager := TObjectManager.Create(Connection); Customer := TCustomer.Create; Customer.Name := 'First customer'; Manager.Save(Customer); Manager.Free; WriteLn('Customer saved.'); ReadLn; end.
1. A class to be persisted. This is your TCustomer class.
2. A mapping between the class and the database. This is accomplished by the [Entity] and [Automapping] attributes. In this case properties are mapped automatically to database columns, but you can set up a custom mapping if you want to. I will call object instances managed by Aurelius “entities”.
3. A connection to a database. This is the Connection variable, which implements an IDBConnection interface. In this case, we are connecting to a local SQLite database and need to make use of the TSQLiteNativeConnectionAdapter. I.e. we always need an adapter that connects the database world to the object world. In Windows you have to make sure sqlite3.dll is in a directory Windows can find. In Mac OS X and iOS, SQLite is already available.
4. An object manager to persist and manage your entities. The second line creates an object manager, which stores objects in the database specified by IDBConnection.
With all that, the code just instantiates the TCustomer object, fills its properties and saves it to the database. That is your first TMS Aurelius application!
As an additional note with regard to the code sample, if the file “test.db” does not exist, Aurelius will create it for you. However, you have to explicitly ask it to create the database structure for you, using the following code:
procedure CreateDatabase(Connection: IDBConnection); var DBManager: TDatabaseManager; begin DBManager := TDatabaseManager.Create(Connection); DBManager.BuildDatabase; DBManager.Free; end;
Wagner Landgraf
This blog post has received 8 comments.
2. Wednesday, February 6, 2013 at 12:58:49 PM
No, TCustomer instance is destroyed by Manager.
Wagner Landgraf
3. Thursday, February 7, 2013 at 5:43:43 AM
How does Aurelius handle updating objects?
Say for arguments sake that the customer table consists of just the name, but that name must be unique in the table. The ID field is no longer present (yes I realise this is bad design but I have seen it in client databases before).
So, you set your TCustomer object to have a name of ''First customer'' and save it. If you then changed that name to ''Second customer'', in the same object, and saved it, would Aurelius add a new record, or update the previous record to have a different name? How is this distinction handled in cases like this, or does it insist on having fixed ID fields that are always defined?
eg;
TCustomer = class
private
FName: string;
public
property Name: string read FName write FName;
end;
...
Customer := TCustomer.Create;
Customer.Name := ''First customer'';
Manager.Save(Customer);
Customer.Name := ''Second customer'';
Manager.Save(Customer);
Does it add a new record or update existing one, or error with ambiguity? Of course, in real code you would do what was appropriate for the task, but I am curious as to how Aurelius handles these edge cases.
The reason I ask is that, for example you need to import data from an external source, and you have to make that distinction between updating and adding. How can you control Aurelius to do this ''neatly'' if you do not know the ID field before (or there isn''t one available)? Will it handle this gracefully, or is there some nifty way of ensuring it ''just knows'' when to update or add a record?
What happens if you do this:
Customer := TCustomer.Create;
Customer.Name := ''First customer'';
Manager.Save(Customer);
Customer.Name := ''First customer'';
Manager.Save(Customer);
Will Aurelius ignore the second save due to no changes, will it add another record, does it read the metadata to determine if duplicates of the Name field would be allowed and then add a new copy? Or can you define when it does each? If possible, how easy/messy is it to define these rules?
I''m really curious because I have written an ORM framework before (even using attributes in a similar way to your example above) and when dealing with poorly set up databases it can sometimes be challenging to remove ambiguities with ''generic'' ORM code.
Looking forward to hearing your response, and FWIW if Aurelius was around before I''d rolled my own I probably would have used that instead :)
Say for arguments sake that the customer table consists of just the name, but that name must be unique in the table. The ID field is no longer present (yes I realise this is bad design but I have seen it in client databases before).
So, you set your TCustomer object to have a name of ''First customer'' and save it. If you then changed that name to ''Second customer'', in the same object, and saved it, would Aurelius add a new record, or update the previous record to have a different name? How is this distinction handled in cases like this, or does it insist on having fixed ID fields that are always defined?
eg;
TCustomer = class
private
FName: string;
public
property Name: string read FName write FName;
end;
...
Customer := TCustomer.Create;
Customer.Name := ''First customer'';
Manager.Save(Customer);
Customer.Name := ''Second customer'';
Manager.Save(Customer);
Does it add a new record or update existing one, or error with ambiguity? Of course, in real code you would do what was appropriate for the task, but I am curious as to how Aurelius handles these edge cases.
The reason I ask is that, for example you need to import data from an external source, and you have to make that distinction between updating and adding. How can you control Aurelius to do this ''neatly'' if you do not know the ID field before (or there isn''t one available)? Will it handle this gracefully, or is there some nifty way of ensuring it ''just knows'' when to update or add a record?
What happens if you do this:
Customer := TCustomer.Create;
Customer.Name := ''First customer'';
Manager.Save(Customer);
Customer.Name := ''First customer'';
Manager.Save(Customer);
Will Aurelius ignore the second save due to no changes, will it add another record, does it read the metadata to determine if duplicates of the Name field would be allowed and then add a new copy? Or can you define when it does each? If possible, how easy/messy is it to define these rules?
I''m really curious because I have written an ORM framework before (even using attributes in a similar way to your example above) and when dealing with poorly set up databases it can sometimes be challenging to remove ambiguities with ''generic'' ORM code.
Looking forward to hearing your response, and FWIW if Aurelius was around before I''d rolled my own I probably would have used that instead :)
Scalea
4. Thursday, February 7, 2013 at 6:09:45 AM
First of all, I would like to say Aurelius was designed to be very simple and very flexible. It doesn''t require you to have databases specifically built for Aurelius, you can use existing databases and even mix Aurelius code with "old" dataset-based code.
So one consequence is that you can map your classes as you want. Aurelius just require that you define an Id for every class - but this is basic database requirement, i.e., any table must have at least one key. Since in your case it''s name, you can map your class like this:
[Entity, Automapping]
[Id(''FName'')]
TCustomer = class
private
FName: string;
public
property Name: string read FName write FName;
end;
And so that is it, Name property will be the id and Aurelius will handle it just as if you had an integer Id.
So one consequence is that you can map your classes as you want. Aurelius just require that you define an Id for every class - but this is basic database requirement, i.e., any table must have at least one key. Since in your case it''s name, you can map your class like this:
[Entity, Automapping]
[Id(''FName'')]
TCustomer = class
private
FName: string;
public
property Name: string read FName write FName;
end;
And so that is it, Name property will be the id and Aurelius will handle it just as if you had an integer Id.
Wagner Landgraf
5. Thursday, February 7, 2013 at 6:19:32 AM
Thanks for your super-quick response!
That''s good to know, I have one last question:
Can Aurelius handle multiple unique fields, for example if you had a table with ID1, ID2 and ID3 and all must remain unique? I would imagine you''d just define more IDs but I have to check ;)
Thanks for your reply, I must say it looks like an impressive and adaptable framework.
That''s good to know, I have one last question:
Can Aurelius handle multiple unique fields, for example if you had a table with ID1, ID2 and ID3 and all must remain unique? I would imagine you''d just define more IDs but I have to check ;)
Thanks for your reply, I must say it looks like an impressive and adaptable framework.
Scalae
6. Thursday, February 7, 2013 at 6:31:38 AM
Yes, you can use composite id in Aurelius, and of course you can use even with associations (a subject for a future topic of crash course), which means that relationships (foreign keys) will use those composite ids. There is one interesting example of multiple composite ids in this topic of manual: http://www.tmssoftware.com.br/aurelius/doc/web/index.html?composite_id.htm
Wagner Landgraf
7. Saturday, March 30, 2013 at 4:03:49 AM
1. In documentation the TDataBaseManager.UpdateDatabase method is described, but such method doesn''t exist. Why?
2. You plan to implement support of the stored procedures in Aurelius?
2. You plan to implement support of the stored procedures in Aurelius?
Shevjakov Vladislav
8. Monday, April 1, 2013 at 6:48:18 PM
We have received this request through our support e-mail and answered properly.
Wagner R. Landgraf
All Blog Posts | Next Post | Previous Post
A. Bouchez