Blog
All Blog Posts | Next Post | Previous PostCrash Course TMS Aurelius – Inheritance and Polymorphism
Friday, March 8, 2013
Inheritance is one of my favorite features in Aurelius. One of benefits of using an ORM is abstracting the SQL and start thinking (almost) purely in OOP. Inheritance and polymorphism are fundamental features of Object-oriented programming, and if when designing your model you can't use it, then the "object-relational" mapping would just become a simple "property>column" mapping in the end.Aurelius allows you to build a class hierarchy that can be persisted, and provides you with two strategies to persist it: joined tables and single table. The former will create a different table for each class and add the proper relationships, and the later will save the whole hierarchy in the same table. You can learn more about it reading the topic "Inheritance Strategies" in documentation.
Let me illustrate how it works. Considering the following classes and mapping:
type [Entity, Automapping] [Inheritance(TInheritanceStrategy.JoinedTables)] TPerson = class private FId: integer; FName: string; public property Id: integer read FId write FId; property Name: string read FName write FName; end; [Entity, Automapping] TEmployee = class(TPerson) private FSalary: Currency; public property Salary: Currency read FSalary write FSalary; end;
function SavePerson(Manager: TObjectManager): integer; var Person: TPerson; begin Person := TPerson.Create; Person.Name := 'John Person'; Manager.Save(Person); Result := Person.Id; end; function SaveEmployee(Manager: TObjectManager): integer; var Employee: TEmployee; begin Employee := TEmployee.Create; Employee.Name := 'James Employee'; Employee.Salary := 1999.99; Manager.Save(Employee); Result := Employee.Id; end;
procedure OutputPerson(Person: TPerson); begin if Person <> nil then WriteLn(Format('Class: %s; Name: %s', [Person.ClassName, Person.Name])) else WriteLn('nil'); end; procedure OutputEmployee(Employee: TEmployee); begin if Employee <> nil then WriteLn(Format('Class: %s; Name: %s; Salary: %s', [Employee.ClassName, Employee.Name, FloatToStr(Employee.Salary)])) else WriteLn('nil'); end; procedure CheckPersonAndEmployee(Manager: TObjectManager; PersonId, EmployeeId: integer); var Person: TPerson; Employee: TEmployee; begin Person := Manager.Find<TPerson>(PersonId); OutputPerson(Person); Person := Manager.Find<TPerson>(EmployeeId); OutputPerson(Person); Employee := Manager.Find<TEmployee>(EmployeeId); OutputEmployee(Employee); Employee := Manager.Find<TEmployee>(PersonId); OutputEmployee(Employee); end;
Class: TPerson; Name: John Person Class: TEmployee; Name: James Employee Class: TEmployee; Name: James Employee; Salary: 1999.99 nil
The last two Find calls ask for a TEmployee object. When the EmployeeId is provided, the correct TEmployee object is retrieved. But when we ask for a TEmployee object passing PersonId as Id, nil is returned - although the object is in database with that id, it's not returned because the object is not a TEmployee, but only a TPerson.
As in the previous posts, I will provide here some SQL statements generated by Aurelius, for a better understanding. When using joined tables strategy, Aurelius will create the following database structure (SQL Server syntax):
CREATE TABLE PERSON ( ID INTEGER IDENTITY(1,1) NOT NULL, NAME VARCHAR(255) NOT NULL, CONSTRAINT PK_PERSON PRIMARY KEY (ID)); CREATE TABLE EMPLOYEE ( ID INTEGER NOT NULL, SALARY NUMERIC(20, 4) NOT NULL, CONSTRAINT PK_EMPLOYEE PRIMARY KEY (ID)); ALTER TABLE EMPLOYEE ADD CONSTRAINT FK_EMPLOYEE_PERSON_ID FOREIGN KEY (ID) REFERENCES PERSON (ID);
SELECT A.ID AS A_ID, A.SALARY AS A_SALARY, B.ID AS B_ID, B.NAME AS B_NAME FROM EMPLOYEE A LEFT JOIN PERSON B ON (B.ID = A.ID) WHERE B.ID = :p_0
[Entity, Automapping] [Inheritance(TInheritanceStrategy.SingleTable)] [DiscriminatorColumn('PERSON_TYPE', TDiscriminatorType.dtString)] [DiscriminatorValue('Person')] TPerson = class private FId: integer; FName: string; public property Id: integer read FId write FId; property Name: string read FName write FName; end; [Entity, Automapping] [DiscriminatorValue('Employee')] TEmployee = class(TPerson) private FSalary: Nullable<Currency>; public property Salary: Nullableread FSalary write FSalary; end;
CREATE TABLE PERSON ( ID INTEGER IDENTITY(1,1) NOT NULL, NAME VARCHAR(255) NOT NULL, PERSON_TYPE VARCHAR(30) NOT NULL, SALARY NUMERIC(20, 4) NULL, CONSTRAINT PK_PERSON PRIMARY KEY (ID));
SELECT A.ID AS A_ID, A.NAME AS A_NAME, A.PERSON_TYPE AS A_PERSON_TYPE, A.SALARY AS A_SALARY FROM PERSON A WHERE A.PERSON_TYPE = :p_1 AND A.ID = :p_0 p_0 = "1" (ftInteger) p_1 = "Employee" (ftString)
Wagner Landgraf
This blog post has not received any comments yet.
All Blog Posts | Next Post | Previous Post