Search code examples
mysqldelphidelphi-xe3zeos

How to persist tables with master-detail relationship within a single transaction?


I'm trying to persist two tables with master-detail relationship in MySQL 5.6 using Delphi XE3 and Zeos 7.0.4. When I do ApplyUpdates on the master, the auto increment field stays with 0 as value. I need the auto increment value, so I can link the detail table with the master table's ID field coming from ApplyUpdates. I'm using ZConnection with AutoCommit = FALSE and TransactionIsolationLevel = tiReadCommitted, ZQuery with CachedUpdates = TRUE. What am I missing?

ZQPerson.Append;
ZQEmployee.Append;
try
  ZQPersonName.Value := Edit1.Text;
  ZQPerson.ApplyUpdates; //Here I expected to have the auto increment value on the Id field of ZQPerson, but it returns always 0
  ZQEmployeePersonID.Value := ZQPersonId.Value; //Here I'd link Employee to it's Person record
  ZQEmployeeRegNo.Value := StrToInt(Edit2.Text);
  ZQEmployee.ApplyUpdates;
  ZConnection1.Commit; //Here I would persist both tables in a single transaction to avoid master table without details
except
  ZQPerson.CancelUpdates;
  ZQEmployee.CancelUpdates;
  ZConnection1.Rollback; //In case of exceptions rollback everything
  raise;
end;
ZQPerson.CommitUpdates;
ZQEmployee.CommitUpdates;

My ZSQLMonitor trace is this:

2013-08-29 00:01:23 cat: Execute, proto: mysql-5, msg: INSERT INTO person (Id, name) VALUES (NULL, 'Edit1') --> This is just after ZQPerson.ApplyUpdates
2013-08-29 00:01:50 cat: Execute, proto: mysql-5, msg: INSERT INTO employee (Id, RegNo, ProductId) VALUES (NULL, 1000, 0), errcode: 1452, error: Cannot add or update a child row: a foreign key constraint fails (`test`.`employee`, CONSTRAINT `FK_A6085E0491BDF8EE` FOREIGN KEY (`PersonId`) REFERENCES `person` (`Id`) --> This is just after ZQEmployee.ApplyUpdates
2013-08-29 00:02:05 cat: Execute, proto: mysql-5, msg: Native Rollback call --> Rollback after Exception on the ZQEmployee.ApplyUpdates

Solution

  • The workaround I found was this one. It not satiesfies me completely because it doesn't make transparent the use of the database's auto increment feature, making me use Last_Insert_ID() function. I'm in contact with zeos develpers to check this out.

    function LastInsertID(ATableName: string): Integer;
    var DBQuery: TZQuery;
    begin
      DBQuery := TZQuery.Create(Self);
      with DBQuery do
      begin
        Connection := ZConnection1;
        SQL.Clear;
        SQL.Add('Select Last_Insert_ID() as Last_Insert_ID from ' + ATableName);
        Open;
        Result := FieldByName('Last_Insert_ID').Value;
        Free;
      end;
    end;
    
    procedure Persist;
    var LastID: Integer;
    begin
      ZQPerson.Append;
      ZQEmployee.Append;
      try
        ZQPersonName.Value := Edit1.Text;
        ZQPerson.ApplyUpdates; // Here I expected to have the auto increment value on the Id field of ZQPerson, but it returns always 0
        LastID := LastInsertID('Person'); //Getting the Last_Insert_ID(), even on the uncommitted transction, works
        ZQEmployeePersonId.Value := LastID; //Link the two tables using the Last_Insert_ID() result
        ZQEmployeeRegNo.Value := StrToInt(Edit2.Text);
        ZQEmployee.ApplyUpdates;
        ZConnection1.Commit; // Here I persist both tables in a single transaction to avoid master table without details
      except
        ZQPerson.CancelUpdates;
        ZQEmployee.CancelUpdates;
        ZConnection1.Rollback; // In case of exceptions rollback everything
        raise;
      end;
      ZQPerson.CommitUpdates;
      ZQEmployee.CommitUpdates;