首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在单个事务中持久化具有主细节关系的表?

如何在单个事务中持久化具有主细节关系的表?
EN

Stack Overflow用户
提问于 2013-08-28 18:14:58
回答 3查看 1.6K关注 0票数 2

我试图在MySQL 5.6中使用Delphi、XE3和Zeos 7.0.4维护两个具有主细节关系的表。当我在主服务器上执行ApplyUpdates时,自动增量字段将保持0作为值。我需要自动增量值,所以我可以将细节表与来自ApplyUpdates的主表ID字段链接起来。我使用ZConnection和AutoCommit = FALSE,TransactionIsolationLevel = tiReadCommitted,ZQuery和CachedUpdates = TRUE。我遗漏了什么?

代码语言:javascript
复制
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;

我的ZSQLMonitor跟踪记录如下:

代码语言:javascript
复制
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
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-08-29 20:27:52

我找到的解决办法就是这个。它没有完全满足我的需求,因为它没有透明地使用数据库的自动增量特性,使我使用了Last_Insert_ID()函数。我正在和zeos develpers联系看这个。

代码语言:javascript
复制
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;
票数 0
EN

Stack Overflow用户

发布于 2013-08-29 01:28:59

您是否使用ZConnection1.StartTransaction启动事务?我也认为您必须在调用ZQuery1.ApplyUpdate之后刷新ZQuery1以获得新的id-

阅读您的评论,您必须做一个选择*没有where子句?对吗?我可以建议您使用以下方法:

1)选择和增加当前的自动增量值

2)当然,从主表中选择id=step1 id //它将为空

3)在步骤1中使用id添加详细信息

4)在主数据集中分配id

5)同时应用两种更新

票数 2
EN

Stack Overflow用户

发布于 2020-04-11 20:06:10

我在一个简单的数据库中测试了它,其中两个主表和细节表嵌套在TDataSource中,并由详细表的where关联:

代码语言:javascript
复制
object conMysql: TZConnection
     TransactIsolationLevel = tiReadCommitted
object zqryMaster: TZQuery
     Connection = conMysql
SQL.Strings = (
       'select * from temp.master')
object dsNestedMaster: TDataSource
     DataSet = zqryMaster
object zqryDetail: TZQuery
     Connection = conMysql
     SQL.Strings = (
       'select * from temp.detail'
       'where id_master =: id')

启动事务后,如果发生错误,所有更新都必须等待确认或回滚:

代码语言:javascript
复制
  try
    zqryMaster.Connection.StartTransaction;
    zqryMaster.Edit;
    zqryDetail.Edit;
    zqryMaster.FindField('dt_mov').Value := Now;
    while not zqryDetail.Eof do
    begin
      zqryDetail.Edit;
      zqryDetail.FindField('dt_mov').Value := Now;
      zqryDetail.ApplyUpdates;
      zqryDetail.Next;
      //raise Exception.Create('simple error'); //use for tests, check database after perform
    end;
    zqryMaster.ApplyUpdates;
    zqryMaster.Connection.Commit;
  except
    zqryMaster.Connection.Rollback;
    zqryMaster.CancelUpdates;
    zqryDetail.CancelUpdates;
  end;
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18495309

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档