Show/Hide Toolbars

TMS Aurelius Documentation

Navigation: Mapping

Composite Id

Scroll Prev Top Next More

You can use composite identifier in TMS Aurelius. Although possible, it's strongly recommended that you use single-attribute, single-column identifiers. The use of composite id should be used only for legacy applications where you already have a database schema that uses keys with multiple columns. Still in those cases you could try to add an auto-generated field in the table and use it as id.


Using composite Id's is straightforward: you just use the same attributes used for single Id: Id, JoinColumn, ForeignJoinColumn and PrimaryJoinColumn attributes. The only difference is that you add those attributes two or more times to the classes. For example, the following TAppointment class has a composite Id using the attributes AppointmentDate and Patient (you can use associations as well):




[Id('FLastName', TIdGenerator.None)]

[Id('FFistName', TIdGenerator.None)]

TPerson = class

strict private

  [Column('LAST_NAME', [TColumnProp.Required], 50)]

  FLastName: string;

  [Column('FIRST_NAME', [TColumnProp.Required], 50)]

  FFirstName: string;


  property LastName: string read FLastName write FLastName;

  property FirstName: string read FFiratName write FFiratName;





[Id('FAppointmentDate', TIdGenerator.None)]

[Id('FPatient', TIdGenerator.None)]

TAppointment = class

strict private

  [Association([TAssociationProp.Lazy, TAssociationProp.Required], [TCascadeType.Merge, TCascadeType.SaveUpdate])]

  [JoinColumn('PATIENT_LASTNAME', [TColumnProp.Required])]

  [JoinColumn('PATIENT_FIRSTNAME', [TColumnProp.Required])]

  FPatient: Proxy<TPerson>;

  [Column('APPOINTMENT_DATE', [TColumnProp.Required])]

  FAppointmentDate: TDateTime;

  function GetPatient: TPerson;

  procedure SetPatient(const Value: TPerson);


  property Patient: TPerson read GetPatient write SetPatient;

  property AppointmentDate: TDateTime read FAppointmentDate write FAppointmentDate;



Note that while TAppointment has a composite Id of two attributes, the number of underlying database table columns is three. This is because Patient attribute is part of Id, and the TPerson class itself has a composite Id. So primary key columns of table APPOINTMENT will be APPOINTMENT_DATE, PATIENT_LASTNAME and PATIENT_FIRSTNAME.


Also pay attention to the usage of JoinColumn attributes in field FPatient. Since TPerson has a composite Id, you must specify as many JoinColumn attributes as the number of table columns used for the referenced table. This is the same for ForeignJoinColumn and PrimaryJoinColumn attributes.


As illustrated in the previous example, you can have association attributes as part of a composite identifier. However, there is one limitation: you can't have lazy-loaded associations as part of the Id. All associations that are part of an Id are loaded in eager mode. In the previous example, although FPatient association was declared with TAssociationProp.Lazy, using a proxy, this settings will be ignored and the TPerson object will be fully loaded when a TAppointment object is loaded from the database.


When using composite Id, the generator specified in the Id attribute is ignored, and all are considered as TIdGenerator.None.


When using Id values for finding objects, for example when using Find method of object manager or using IdEq expression in a query, you are required to provide an Id value. The type of this value is Variant. For composite Id's, you must provide an array of variant (use VarArrayCreate method for that) where each item of the array refers to the value of a table column. For associations in Id's, you must provide a value for each id of association (in the example above, to find a class TAppointment you should provide a variant array of length = 3, with the values of appointment data, patient's last name and first name values.