Blog

All Blog Posts  |  Next Post  |  Previous Post

Curso Rápido de TMS Aurelius - Associações (Chaves Estrangeiras)

Friday, March 8, 2013

Além de mapear tabelas a classes e campos a propriedades, Aurelius também mapeia relacionamentos (chaves estrangeiras) a associações entre objetos. Um ponto interessante do Aurelius é que essas associações são definidas de forma bem simples: apenas uma referência a outro objeto. Considere as seguintes classes com o seguinte mapeamento:
type
  [Entity, Automapping]
  TCountry = class
  private
    FId: integer;
    FName: string;
  public
    property Id: integer read FId write FId;
    property Name: string read FName write FName;
  end;

  [Entity, Automapping]
  TCustomer = 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;
Observe que TCustomer tem uma associação com TCountry, o que significa que todo cliente tem um país associado a ele. O código a seguir mostra como você deveria salvar um objeto TCustomer com uma associação TCountry:
function CreateCustomerWithCountry(Manager: TObjectManager): integer;
var
  Customer: TCustomer;
  USACountry: TCountry;
begin
  USACountry := TCountry.Create;
  USACountry.Name := 'USA';
  Customer := TCustomer.Create;
  Customer.Name := 'John';
  Customer.Country := USACountry;
  Manager.Save(Customer);
  Result := Customer.Id;
end;
Simples e direto. Note que nós não precisamos salvar o objeto TCountry - quando o cliente é salvo, o país é salvo automaticamente porque um está associado ao outro (este é o comportamento default do automapeamento. Você pode configurar o mapeamento para evitar que o país seja salvo automaticamente).

Também é muito simples buscar um objeto e suas associações do banco de dados. Considere o seguinte código que recebe um id do cliente e retorna o nome do país associado a ele:
function GetCountryNameFromCustomer(Manager: TObjectManager; CustomerId: integer): string;
var
  Customer: TCustomer;
begin
  Customer := Manager.Find<TCustomer>(CustomerId);
  if Customer <> nil then
    Result := Customer.Country.Name
  else
    Result := '';
end;
O código retorna o objeto TCustomer baseado no id (o método Find será assunto de um próximo post). Para obter o nome do país, tudo que precisamos fazer é pegar o objeto TCountry association (através da propriedade TCustomer.Country) e retornar sua propriedade Name. Quando a instância do TCustomer foi retornada, seus objetos associados também foram retornados. Você pode configurar isso totalmente, e você pode até mesmo configurar de modo que o objeto TCountry seja buscado do banco apenas quando necessário (também um tópico para um próximo post).

Associações são um recurso básico de qualquer framework ORM e este post mostrou um exemplo bastante simples. O Aurelius tem vários recursos relacionados a associações, muitas formas de trabalhar com elas, salvar, buscar, etc.. Mas o propósito do post é apenas explicar o conceito.

Para deixar ainda mais claro, colocarei aqui os comandos SQL executados pelo Aurelius quando o código acima é executado. Os comandos usados aqui foram executados num banco SQL Server (a sintaxe pode ser diferente em outros bancos).

Os seguintes comandos foram executados para criar as tabelas, assim você pode ter uma ideia da estrutura:
CREATE TABLE COUNTRY (
  ID INTEGER IDENTITY(1,1) NOT NULL,
  NAME VARCHAR(255) NOT NULL,
  CONSTRAINT PK_COUNTRY PRIMARY KEY (ID));

CREATE TABLE CUSTOMER (
  ID INTEGER IDENTITY(1,1) NOT NULL,
  NAME VARCHAR(255) NOT NULL,
  COUNTRY_ID INTEGER NULL,
  CONSTRAINT PK_CUSTOMER PRIMARY KEY (ID));

ALTER TABLE CUSTOMER ADD CONSTRAINT 
  FK_CUSTOMER_COUNTRY_COUNTRY_ID FOREIGN KEY (COUNTRY_ID) REFERENCES COUNTRY (ID)
Ao salvar o objeto TCustomer (funtion CreateCustomerWithCountry), os seguintes comandos foram executados (o valor dos parâmetros são mostrados):
INSERT INTO COUNTRY (NAME) VALUES (:A_NAME);
A_NAME = "USA" (ftString)

SELECT CAST(IDENT_CURRENT('COUNTRY') AS INT);

INSERT INTO CUSTOMER (
  NAME, COUNTRY_ID)
VALUES (
  :A_NAME, :A_COUNTRY_ID);

A_NAME = "John" (ftString)
A_COUNTRY_ID = "1" (ftInteger)

SELECT CAST(IDENT_CURRENT('CUSTOMER') AS INT)
Finalmente, e o mais interessante na minha opinião, é o comando SQL executado para retornar o objeto TCustomer. Note que neste exemplo dois objetos TObjectManager diferentes foram usados para forçar a execução do comando SELECT. Se um único manager fosse usado, ele teria retornado o objeto diretamente da memória e não iria precisar executar um comando SELECT aditional.
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 CUSTOMER A
  LEFT JOIN COUNTRY B ON (B.ID = A.COUNTRY_ID)
WHERE  A.ID = :p_0

p_0 = "1" (ftInteger)


Wagner Landgraf




This blog post has not received any comments yet.



Add a new comment

You will receive a confirmation mail with a link to validate your comment, please use a valid email address.
All fields are required.



All Blog Posts  |  Next Post  |  Previous Post