One-to-one mapping workaround error

I am trying to do an One-to-One mapping following the instructions of this forum post: https://www.tmssoftware.com/site/forum/forum_posts.asp?TID=12928&title=how-to-map-onetoone-association as follows:

*** CODE BEGIN

  [Entity]
  [table("person_address")]
  [Id("FPersonId", TIdGenerator.None)]
  TPersonAddress = class
  strict private
    [Column("person_id", [TColumnProp.Unique, TColumnProp.Required, TColumnProp.NoUpdate])]
    FPersonId: Integer;
    [Column("zip_code", [], 60)]
    FZipCode: String;
  published
    property PersonId: Integer read FPersonId write FPersonId;
    property ZipCode: String read FZipCode write FZipCode;
  end;

  [Entity]
  [Table("person")]
  [Sequence("seq_person_id")]
  [Id("FId", TIdGenerator.IdentityOrSequence)]
  [Inheritance(TInheritanceStrategy.JoinedTables)]
  TPerson = class
  strict private
    [Column("id", [TColumnProp.Unique, TColumnProp.Required, TColumnProp.NoUpdate])]
    FId: Integer;
    [Column("name", [], 60)]
    FName: String;
    [ManyValuedAssociation([], CascadeTypeAllRemoveOrphan)]
    [ForeignJoinColumn("person_id", [TColumnProp.Required])]
    FAddress: TList<TPersonAddress>;
  private
    function GetAddress: TPersonAddress;
    procedure SetAddress(const Value: TPersonAddress);
  public
    constructor Create;
  published
    property Id: Integer read FId write FId;
    property Name: String read FName write FName;
    property Address: TPersonAddress read GetAddress write SetAddress;
  end;

  [Entity]
  [Table("customer")]
  [PrimaryJoinColumn("person_id")]
  TCustomer = class(TPerson)
  strict private
    [Column("total_invoices", [])]
    FTotalInvoices: Integer;
  published
    property TotalInvoices: Integer read FTotalInvoices write FTotalInvoices;
  end;

CODE END ***

In the application, we have three classes: a base class "TPerson" (pk_fieldname: "id"); a child class TCustomer (pk_fieldname: "person_id"); and the TPersonAddress (pk_fieldname: "person_id") class which have a One-to-One relationship with the base class TPerson.

When i try to persist an instance of TCustomer with an assigned Address property i get the error "Column person_address.person_id cannot be repeated in INSERT statement".

Srdic,


I had a similar problem in one of my projects.  Here's what I did for FID field in TPerson that worked for me:

    [Column('ID', [TColumnProp.Required])]
    FId: Integer;

I also wonder why you are not using a Person: TPerson association instead of FPersonID. Recommend you try:

    [Association([TAssociationProp.Lazy], CascadeTypeAll - [TCascadeType.Remove])]
    [JoinColumn('person_id', [TColumnProp.Required,TColumnProp.NoUpdate])]
    FPerson: Proxy<TPerson>;
   function GetPerson: TPerson;
   procedure SetPerson(Value: TPerson);
   property Person: TPerson read GetPerson write SetPerson;

implementation
TPerson.GetPerson: TPerson;
begin
  Result := FPerson.Value;
end;

TPerson.SetPerson(Value: TPerson);
begin
  FPerson.Value := Value;
end;

Last, I notice you are using the quotation character (") instead of apostrophe (') for strings in your attribute definitions.  I am not sure if this makes a difference, but i have not seen it used this way in the documentation. 

Hope it works for you.  If not, I'm sure Wagner will have the right solution.
Terry,

I submitted a feature request (req. 2564) to implement a native one-to-one mapping like the frameworks of other languages (for example, Java Hibernate). Please add your vote.

I will wait for Wagner's reply before apply your workaround. Thanks for your suggestion.

Regards
 

Fabricio, I see that you have also sent the same question via private e-mail. I will repeat the answer here so it might benefit others:


The root of the problem is here:

 TPersonAddress = class
 strict private
   [Column("person_id", [TColumnProp.Unique, TColumnProp.Required, TColumnProp.NoUpdate])]
   FPersonId: Integer;

PersonId is an association to a TPerson object. It should be declared this way:

 TPersonAddress = class
 strict private
   [Association([], CascadeTypeAllButRemove)]
   [JoinColumn("person_id", [TColumnProp.Unique, TColumnProp.Required, TColumnProp.NoUpdate])]
   FPersonId: TPerson;

And then in TPerson you should map it this way:

   [ManyValuedAssociation([], CascadeTypeAllRemoveOrphan, ‘FPersonId')]
   FAddress: TList<TPersonAddress>;

Pointing that the other side of TPerson.FAddress association is TPersonAddress.FPersonId.