当前位置 : 主页 > 编程语言 > delphi >

如何使用Delphi FireDAC对数据库应用UNIQUE约束来应用缓存更新FDQuery

来源:互联网 收集:自由互联 发布时间:2021-06-23
当delta包含对数据库具有UNIQUE约束的字段时,我有一个问题来解决缓存更新.我有一个具有以下DDL架构的数据库(内存中的SQLite可用于重现): create table FOO( ID integer primary key, DESC char(2) UNIQ
当delta包含对数据库具有UNIQUE约束的字段时,我有一个问题来解决缓存更新.我有一个具有以下DDL架构的数据库(内存中的SQLite可用于重现):

create table FOO
(
  ID integer primary key,
  DESC char(2) UNIQUE
);

初始数据库表包含一个ID = 1且DESC = R1的记录

使用TFDQuery(从FOO中选择*)访问此表,如果执行以下步骤,将使用ApplyUpdates正确应用生成的增量:

>将记录ID = 1更新为DESC = R2
>使用DESC = R1附加新记录ID = 2

Delta包括以下内容:

> R2
> R1

ApplyUpdates不会生成错误,因为delta上的第一个操作将是更新.第二个是插入.由于记录1现在是R2,因此可以完成插入,因为在此事务中没有违反唯一约束.

现在,执行以下步骤将生成完全相同的delta(查看FDQuery.Delta属性),但将生成UNIQUE约束违例.

>使用DESC = TT附加新的临时记录ID = 2
>将第一个记录ID = 1更新为DESC = R2
>更新临时记录2 – TT到DESC = R1

Delta包括以下内容:

> R2
> R1

请注意,FireDAC在两种情况下都会生成相同的增量,可以通过FDquery的Delta属性查看.

此步骤可用于重现错误:

文件>新的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();
更新自编写此答案的原始版本以来,我已经做了一些调查,并开始认为在FireDAC支持Sqlite(至少在西雅图),或者我们不是,在ApplyUpdates等方面存在问题正确使用FD组件.它需要FireDAC的作者(这里是一个贡献者)来说明它是什么.

暂且不谈ApplyUpdates业务,您的代码还存在许多其他问题,即数据集导航会假设qry中行的排序及其Fields的编号.

我使用的测试用例是使用包含单行的Foo表启动(在执行应用程序之前)

(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;

在Con.Close执行之后,外部应用程序看不到添加的行(ID = 2),我觉得这很令人费解.一旦调用了Con.Close,外部应用程序就会将Foo显示为包含

(1, 'R2')
(2, 'R1')

但是,如果我调用ApplyUpdates,我无法避免约束违规错误,无论我对代码做出任何其他更改,包括在第一个Post之后添加对ApplyUpdates的调用.

所以,在我看来,ApplyUpdates的操作有缺陷或者没有正确使用.

我提到了FireDAC的作者.他的名字是德米特里·阿雷菲耶夫(Dmitry Arefiev),他在SO上回答了很多关于他的问题,尽管在过去的几个月左右我没有在这里注意到他.您可以尝试通过在EMBA的FireDAC NG论坛https://forums.embarcadero.com/forum.jspa?forumID=502中发帖来吸引他的注意力.

网友评论