当delta包含对数据库具有UNIQUE约束的字段时,我有一个问题来解决缓存更新。我有一个具有以下DDL模式的数据库(内存中的SQLite可用于复制):
create table FOO
(
ID integer primary key,
DESC char(2) UNIQUE
);
初始数据库表包含一条ID = 1且DESC = R1的记录
使用TFDQuery (select * from FOO)访问该表,如果执行以下步骤,生成的增量将正确地应用于ApplyUpdates:
- 更新记录ID = 1到DESC = R2
- 添加ID = 2的DESC = R1
Delta包括以下内容:
- R2
- R1
ApplyUpdates上不会产生错误,因为delta上的第一个操作将是更新。第二个是插入。由于记录1现在是R2,因此可以进行插入,因为在此事务上没有违反惟一约束。
现在,执行以下步骤,将生成完全相同的增量(请查看FDQuery)。Delta属性),但会生成一个UNIQUE约束违反。
- 用DESC = TT添加新的临时记录ID = 2
- 将第一个记录ID = 1更新为DESC = R2
- 将临时记录2 - TT更新为DESC = R1
Delta包括以下内容:
- R2
- R1
请注意,FireDAC在两种情况下生成相同的增量,这可以通过FDquery的delta属性查看。
以下步骤可用于重现错误:
File>新建VCL表单申请;删除表单上的FDConnection和FDQuery设置FDConnection使用SQLite驱动(在内存数据库中使用);在表单上放置两个按钮,一个用于再现正确的行为,另一个用于再现错误,如下所示:
按钮:
procedure TFrmMain.btnOkClick(Sender: TObject);
begin
// create the default database with a FOO table
con.Open();
con.ExecSQL('create table FOO' + '(ID integer primary key, DESC char(2) UNIQUE)');
// insert a default record
con.ExecSQL('insert into FOO values (1,''R1'')');
qry.CachedUpdates := true;
qry.Open('select * from FOO');
// update the first record to T2
qry.First();
qry.Edit();
qry.Fields[1].AsString := 'R2';
qry.Post();
// append the second record to T1
qry.Append();
qry.Fields[0].AsInteger := 2;
qry.Fields[1].AsString := 'R1';
qry.Post();
// apply will not generate a unique constraint violation
qry.ApplyUpdates();
end;
按钮错误:
// create the default database with a FOO table
con.Open();
con.ExecSQL('create table FOO' + '(ID integer primary key, DESC char(2) UNIQUE)');
// insert a default record
con.ExecSQL('insert into FOO values (1,''R1'')');
qry.CachedUpdates := true;
qry.Open('select * from FOO');
// append a temporary record (TT)
qry.Append();
qry.Fields[0].AsInteger := 2;
qry.Fields[1].AsString := 'TT';
qry.Post();
// update R1 to R2
qry.First();
qry.Edit();
qry.Fields[1].AsString := 'R2';
qry.Post();
qry.Next();
// update TT to R1
qry.Edit();
qry.Fields[1].AsString := 'R1';
qry.Post();
// apply will generate a unique contraint violation
qry.ApplyUpdates();
Update自从写这个答案的原始版本以来,我做了一些更多的调查,我开始认为要么是ApplyUpdates等问题,在FireDAC对Sqlite的支持中(至少在西雅图),或者我们没有正确使用FD组件。它需要FireDAC的作者(他是这里的贡献者)来说明它是哪个。
暂时撇开ApplyUpdates
业务,您的代码存在许多其他问题,即您的数据集导航对qry
中的行排序及其Fields
的编号进行了假设。
(1, 'R1')
然后,执行以下Delphi代码,同时使用外部应用程序(FireFox的Sqlite Manager插件)监视Foo的内容。代码执行时没有在应用程序中报告错误,但是请注意,它没有调用ApplyUpdates
。
Con.Open();
Con.StartTransaction;
qry.Open('select * from FOO');
qry.InsertRecord([2, 'TT']);
assert(qry.Locate('ID', 1, []));
qry.Edit;
qry.FieldByName('DESC').AsString := 'R2';
qry.Post;
assert(qry.Locate('ID', 2, []));
qry.Edit;
qry.FieldByName('DESC').AsString := 'R1';
qry.Post;
Con.Commit;
qry.Close;
Con.Close;
添加的行(ID = 2)是不可见的外部应用程序,直到Con.Close
已经执行,我发现困惑。一旦调用了Con.Close
,外部应用程序显示Foo包含
(1, 'R2')
(2, 'R1')
然而,无论我对代码进行任何其他更改,包括在第一个Post
之后添加对ApplyUpdates
的调用,我都无法避免调用ApplyUpdates
的约束违反错误。
所以,在我看来,ApplyUpdates
的操作是有缺陷的,或者它没有被正确使用。
我提到FireDAC的作者。他的名字是Dmitry Arefiev,他回答了很多关于SO的问题,尽管我在过去的几个月里没有注意到他。你可以试着在EMBA的FireDAC NG论坛(https://forums.embarcadero.com/forum.jspa?forumID=502)上发帖来引起他的注意。