Mysql 如何在单个事务中持久化具有主-详细关系的表?
我试图使用DelphiXE3和Zeos7.0.4在MySQL 5.6中持久化两个具有主-细节关系的表。当我在主机上应用更新时,自动增量字段的值保持为0。我需要自动递增值,这样我就可以将细节表与来自ApplyUpdate的主表ID字段链接起来。我使用的是ZConnection,AutoCommit=FALSE,TransactionIsolationLevel=tiReadCommitted,ZQuery,CachedUpdates=TRUE。我错过了什么Mysql 如何在单个事务中持久化具有主-详细关系的表?,mysql,delphi,delphi-xe3,zeos,Mysql,Delphi,Delphi Xe3,Zeos,我试图使用DelphiXE3和Zeos7.0.4在MySQL 5.6中持久化两个具有主-细节关系的表。当我在主机上应用更新时,自动增量字段的值保持为0。我需要自动递增值,这样我就可以将细节表与来自ApplyUpdate的主表ID字段链接起来。我使用的是ZConnection,AutoCommit=FALSE,TransactionIsolationLevel=tiReadCommitted,ZQuery,CachedUpdates=TRUE。我错过了什么 ZQPerson.Append; ZQE
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跟踪如下:
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
您是否正在使用ZConnection1.StartTransaction启动事务?我还认为,在调用ZQuery1.applyUpdate获取新id后,必须刷新ZQuery1- 在阅读您的评论时,您必须执行select*操作,而不使用where子句?正确的?我建议您使用这种方法: 1) 选择并递增当前自动递增值
2) 从主表中选择,其中id=[step1 id]//它当然是空的
3) 使用步骤1中的id添加详细信息
4) 在主数据集中分配id
5) 应用两个更新我找到的解决方法就是这个。它不能完全满足我,因为它不能使数据库的自动增量功能的使用透明化,使我使用Last_Insert_ID()函数。我正在和zeos develpers联系以核实这一点
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;
我在一个简单的数据库中对它进行了测试,其中有两个主表和明细表,它们与TDataSource嵌套,并通过明细表的
进行关联,其中:
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')
启动事务后,如果发生错误,所有更新必须等待确认或回滚:
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;
问题是刷新将指向数据集中的第一条记录,并且不会返回下一个自动增量值。如何选择当前自动增量值以完成步骤1?我正在使用select*from product for ZQProduct和select*from category,product where category.ProductId=product.Id对于ZQCategory,它只是测试主-细节关系,而不是以正确的方式处理关系。请参见您建议的方式,使用last_insert_Id解决问题,但不需要增加第一步的值。我做了一个很好的解决办法,但这不是这个问题的理想解决方案,因为使用数据库的自动增量功能是不透明的。请考虑添加一两个句子来解释您所提供的代码是如何解决问题的OP的,而不是使细节改变客户端而不是EOF循环,为什么不使用一次更新来更新所有详细信息行?是的,当然,对TZSQLProcessor组件使用相同的技术,相同的try except
块,通过只执行一个更新命令或它们的列表(如有必要)来产生相同的结果,从连接启动事务,在sql脚本中输入命令,执行并确认,或在发生错误时回滚。