Мне интересно, почему данные, размещенные в моем наборе клиентских данных, не обновляются в таблице базы данных MySQL с помощью ApplyUpdates.
Я работаю над 2 базами данных.
DB1.CustomerTable1: нет проблем, первичный ключ в «ListID» —> CDS.Append —> CDS.ApplyUpdates
DB2.CustomerTable2: с проблемой, первичный ключ в 'Guid' —> CDS.Edit —> CDS.ApplyUpdates.
Я добавил первичный ключ в DB2.CustomerTable2 с «ListID» и все еще не работает.
Ниже коды, с которыми я работаю.
procedure TfrmMain.spbExportClick(Sender: TObject);
var
Guid , VarAccountId, VarListSasId, VarListDspId : Variant;
Status : String;
begin
with dm.dmForm do
begin
cdsCustomer2.first;
while not cdsCustomer2.eof do
begin
//variable data for CDS.Customer1.AllFields
Guid := cdsCustomer2.FieldByName ('Guid').AsString;
VarAccountId := cdsCustomer2.FieldByName('ListID').AsString;
VarListSasId := cdsCustomer2.FieldByName('FullName').AsString;
VarListDspId := cdsCustomer2.FieldByName('Name').AsString;
Status := 'Out';
//posting to CDS.Customer1.AllFields
cdsCustomer1.DisableControls;
cdsCustomer1.Append;
cdsCustomer1.FieldByName('GUID').AsString := Guid;
cdsCustomer1.FieldByName('AccountId').AsString := VarAccountId;
cdsCustomer1.FieldByName('ListSasID').AsString := VarListSasId;
cdsCustomer1.FieldByName('ListDspID').AsString := VarListDspId;
cdsCustomer1.FieldByName('Status').AsString := Status;
cdsCustomer1.EnableControls;
cdsCustomer1.Fields[1].ProviderFlags := [pfInKey];
cdsCustomer1.Post;
//posting Guid value back to CDS.Customer2
if cdsCustomer2.locate('ListID', VarAccountId, []) then
begin
cdsCustomer2.DisableControls;
cdsCustomer2.Edit;
cdsCustomer2.FieldByName('ExternalGUID').AsString := Guid;
cdsCustomer2.EnableControls;
cdsCustomer2.Fields[0].ProviderFlags := [pfInKey];
cdsCustomer2.Post;
end;
cdsCustomer2.Next;
end;
//ApplyUpdates to mysql Customer1.Table and Customer2.Table
cdsCustomer1.ApplyUpdates(-1);
cdsCustomer2.ApplyUpdates(-1);
end;
end;
Я ожидаю, что эти коды будут просто публиковаться в моей базе данных MySQL2 точно так же, как и в базе данных 1. Все работает вплоть до публикации в оба набора ClientDataSet, кроме публикации cdsCustomer2 из DB2.
Дайте мне знать, если я пропустил какую-то информацию, которая может вам понадобиться.
P.S. Вот, кстати, структура базы данных: UniConnection -> MySQLUniProvider -> UniQuery -> DataSetProvider -> ClientDataSet -> DataSource -> DBGrid
@UweRaabe Спасибо. Извиняюсь за путаницу.






Диагностика проблем с ApplyUpdates может быть немного утомительной, потому что иногда это вопрос перебора нескольких возможностей, пока вы не найдете тот, который применим к вашей ситуации, поэтому я не могу сказать вам «просто сделайте это ...», и это исправит твоя проблема.
Однако, прежде чем вы начнете рассматривать возможности, есть пара проблем с вашим кодом, которые нужно исправить, иначе вы вряд ли что-то добьетесь.
Ваши звонки в ApplyUpdates
cdsCustomer1.ApplyUpdates(-1);
cdsCustomer2.ApplyUpdates(-1);
Измените это на
var Count : Integer;
[...]
Count := cdsCustomer1.ApplyUpdates(0);
Assert(Count = 0);
Count := cdsCustomer2.ApplyUpdates(0);
Assert(Count = 0);
Дело в том, что указание -1 в качестве аргумента для ApplyUpdates делает прямо противоположное тому, что вы хотите, т.е. допускает любое количество ошибок для создания в процессе ApplyUpdates. Вы хотите, чтобы это останавливаться при любой ошибке, что и будет делать 0, чтобы вы могли узнать какая ошибка возвращается во время ApplyUpdates.
Также
Внесите эти изменения, скомпилируйте и запустите свое приложение и, возможно, одно из двух
Count := [...] выдаст вам сообщение об исключении, которое идентифицирует
причина вашей проблемы. Если это не так:
Убедитесь, что на вашем сервере для обеих таблиц определен первичный ключ, и
затем убедитесь, что в правильных полях ваших CDS указан поставщик pfInkey
набор флагов. Если это сработает, попробуйте установить для них значение pfInWhere.
В исходном файле VCL Provider.Pas найдите процедуру
процедура TSQLResolver.InternalDoUpdate(Tree: TUpdateTree; UpdateKind: TUpdateKind);
Его последняя строка должна быть
DoExecSQL(FSQL, FParams);
Поставьте на него точку останова, запустите ваше приложение, и когда оно остановится на БП, оцените FSQL и посмотрите, правильно ли он выглядит. Если это так, попробуйте выполнить тот же SQL из любой утилиты MySql, которую вы используете для работы с вашим сервером.
Как обычно, я ценю ваш очень подробный ответ. Я исправил ошибку cdsCustomer2.DisableControls на cdsCustomer1.DisableControls. С другой стороны, я смог разобраться в проблеме. Я упомянул о 2 базах данных, с которыми я работаю. В моем модуле данных у меня есть только одна база данных TUniConnection, и она предназначена для cdsCustomer1. У меня нет TUniConnection для другой базы данных, предназначенной для cdsCustomer2.
Но что более удивительно, я смог прочитать данные таблицы для cdsCustomer2 при отсутствии его базы данных TUniConnection. За исключением уровня редактирования, который был моей главной проблемой. После создания отдельного TUniConnection я смог отправить данные в DB2.CustomerTable2.
Что касается ApplyUpdates, я просто хочу знать, является ли это стандартным кодированием, которое я буду использовать в будущем, или только в этом случае. Прямо сейчас я разделил 2 ApplyUpdates и прикрепил их сразу после каждого сообщения?
Вам решать, будете ли вы вызывать .ApplyUpdates после каждого .Post, в зависимости от требований к целостности данных вашего приложения - откладывание их рискует, а также сильно раздражает ваших пользователей, если они не узнают сразу, если обновления не могут быть применены. .
Кстати, @MartynA, я уже изменил на 0 вместо -1. Я просто немного смущен, если после разделения я все еще буду делать это Count := cdsCustomer1.ApplyUpdates(0); Утвердить (Количество = 0);?
Строго говоря, вам не нужна Assert(), но полезно привлечь ваше внимание, если есть проблема.
Добавьте исключение повышения в OnUpdateError DataSetProvider — по умолчанию исключения молчат, чего я никогда не понимал. Итак, что-то вроде Raise Exception (E.Message) - вы можете обнаружить, что пытаетесь обнулить поле, которое должно быть ненулевым или подобным.
После воспроизведения этого случая, чтобы определить, откуда именно исходит ошибка. Я понял, что не создал соединение для другой базы данных. Таким образом, решение для этого случая состоит в том, чтобы создать соединения для каждой базы данных, чтобы беспрепятственно взаимодействовать с двумя базами данных.
Чтобы проиллюстрировать это, вот подключение, которое у меня есть сейчас, и я пока не получаю никаких ошибок при синхронизации двух баз данных:
Database1 —> TUniConnection1 —> MySQLUniProvider1 —> TUniQuery1 —> TDataSetProvider1 —> TClientDataSet1 —> TDataSource1 —> TDBGrid1
Database2 —> TUniConnection2 —> MySQLUniProvider2 —> TUniQuery2 —> TDataSetProvider2 —> TClientDataSet2 —> TDataSource2 —> TDBGrid2
Я не уверен, есть ли лучший способ управлять несколькими подключениями к базе данных одновременно в проекте. Но это работает для меня.
Вызов cdsCustomer2.locate выглядит излишним, учитывая, что значение ключа берется из текущей записи ранее. Также комментарий перед вызовом DisableControls выглядит не связанным с фактическим кодом.