Cập nhật View bằng kbmMWClientQuery

Tình huống phát sinh khi sử dụng kbmMWClientQuery với View: Giả sử có bảng vật lý t1, tạo View v1 từ t1, thực hiện truy vấn: ClientQuery.Query.Text:='Select f1,f2 from v1' Sau khi thao tác thêm/xóa/sửa bản ghi, gọi: ClientQuery.Resolve() để lưu thay đổi.

Lỗi xảy ra khi đặt: ClientQuery.TableName='v1' → Tạo SQL trống Insert into v1 ( ) values ( ) Trong khi ClientQuery.TableName='t1' thì hoạt động bình thường

Giải pháp: Cập nhật thuộc tính Origin của Field tại thời gian chạy: ClientQuery.FieldByName('f1').Origin:='v1.f1'; ClientQuery.FieldByName('f2').Origin:='v1.f2'; Hoặc: ClientQuery.FieldByName('f1').Origin:='.f1'; ClientQuery.FieldByName('f2').Origin:='.f2'; Hoặc: ClientQuery.FieldByName('f1').Origin:=''; ClientQuery.FieldByName('f2').Origin:='';

Chú ý: Phải thực hiện thay đổi Origin bằng mã nguồn trong runtime, thiết kế không hiệu quả

Nguyên nhân: Khi mở ClientQuery, thuộc tính Origin mặc định là 't1.f1' (tên bảng vật lý). Server resolver sẽ kiểm tra sự khớp nối giữa TableName và Origin. Khi TableName là 'v1' nhưng Origin vẫn là 't1.f1' → Không thể tạo SQL chính xác.

Chi tiết logic kiểm tra nằm trong phương thức TkbmMWCustomTableFieldResolver.BuildFieldFilter.

Khi để Origin rỗng, cần chú ý đến thuộc tính kbmMWUNIDACResolver.SkipFieldsWithoutOrigin:

  • True: Bỏ qua các field không có Origin
  • False: Xử lý các field không có Origin (mặc định)

Ứng dụng: Không chỉ cập nhật View, việc kết hợp ClientQuery.TableName và Field.Origin cho phép điều khiển hành vi cập nhật của ClientQuery.Resolve() theo ý muốn.

Dưới đây là ví dụ sử dụng Class Helper để tự động hóa quá trình này:

// Khai báo Helper class cho kbmMWClientQuery
  TkbmMWClientQueryHelper = class helper for TkbmMWClientQuery
    procedure Open;
  end;

// Triển khai phương thức mở lại
{ TkbmMWClientQueryHelper }

procedure TkbmMWClientQueryHelper.Open;
var
  i: Integer;
begin
  inherited Open;
  for I := 0 to Self.FieldCount-1 do
  begin
    if Self.Fields[i].Origin.Substring(0,1) <> '.' then // Trường vật lý
      Self.Fields[i].Origin := '.' + Self.Fields[i].FieldName
    else // Trường ảo
      Self.Fields[i].Origin := 'xxxxxxxxxx.' + Self.Fields[i].FieldName;
  end;
end;

Chú ý: Đơn vị sử dụng ClientQuery.Open() phải import đơn vị chứa Helper class, ngược lại đoạn mã trên sẽ không được thực thi.

Vấn đề mở rộng: Khi cập nhật View cần loại bỏ một số trường khỏi SQL: Cách 1:

  • Thiết lập Origin cho các trường cần cập nhật, để trống cho các trường còn lại
  • Cấu hình kbmMWUNIDACResolver.SkipFieldsWithoutOrigin := True

Cách 2:

  • Sử dụng thuộc tính ProviderFlags của TField để loại bỏ trường khỏi cập nhật:
procedure TPersonManagerFrame.qOneTableAfterOpen(DataSet: TDataSet);
begin
  inherited;
  // Loại bỏ 2 trường khỏi cập nhật
  DataSet.FieldByName('FPositionName').ProviderFlags := DataSet.FieldByName('FPositionName').ProviderFlags - [pfInUpdate,pfInWhere];
  DataSet.FieldByName('FPositionID').ProviderFlags := DataSet.FieldByName('FPositionID').ProviderFlags - [pfInUpdate,pfInWhere];
end;

Cách 3: Xử lý sự kiện ExcludeFromUpdateInsert và ExcludeFromWhere của Resolver để kiểm soát trường cập nhật.

Thẻ: kbmMWClientQuery Cập nhật View Database Resolver

Đăng vào ngày 13 tháng 6 lúc 18:03