Страница 1 из 2

Работа с BLOB через IBX

Добавлено: 28 фев 2008, 12:00
kostyl
Использую IBQUERY для вставки записи:
пишу в .SQL:
insert into FILES (FILE_ID,FILE_DATA) values (:FILE_ID,:FILE_DATA)'
где FILE_DATA BLOB SUB_TYPE 0
Значение FILE_ID получаю темже IBQUERY чуть выше из генератора в
var NewFileID:Int64;
Передаю "динамически" в запрос:
IBQuery1.ParamByName('FILE_ID').AsInteger:=NewFileID;
А вот что делать с FILE_DATA - понятия не имею, потому как у меня он в TIBBlobStream.
Да можно попросту создать запись а потом "модифнуть" его IBDATASET ом. Но хочеться одним запросом и FILE_ID и FILE_DATA.
Как это реализовать?
PS1:TIBBlobStream изпользую потому как шифрую данные.
PS1: И вообще где достать нормальную документацию по использованию IBX 7.11. Во всех книгах одна фигня...

Добавлено: 28 фев 2008, 12:42
WildSery

Добавлено: 28 фев 2008, 13:20
kostyl
WildSery писал(а):Тут читал?
Читал перед тем как задал тему.
Ну вот от туда:
IBQuery1.ParamByName('blb').asBlob:=blobvar;
Ну и как мне в строку записать 1Гб из потока?

Добавлено: 28 фев 2008, 15:45
kostyl
А понял!
Прокритикуйте плиз:

Код: Выделить всё

procedure TForm1.BitBtn6Click(Sender: TObject);
var NewFileID:int64;
begin
 if IBDatabase1.Connected then
  if OpenDialog1.Execute then
    begin
     try
      IBTransaction1.RollbackRetaining;
      //Получаем значения генератора
      IBQuery1.Close;
      IBQuery1.SQL.Clear;
      IBQuery1.SQL.Add('select GEN_ID(GEN_FILE_ID,1) from RDB$DATABASE');
      IBQuery1.Open;
      NewFileID:=IBQuery1.FieldByName('GEN_ID').AsVariant;
      //Загружаем в поток данные файла
      ActiveBlob:=TIBBlobStream.Create;
      ActiveBlob.Mode:=bmReadWrite;
      ActiveBlob.LoadFromFile(OpenDialog1.FileName);
      //Делаем всякую фигню
      //формируем запрос на добавление записи
      IBQuery1.Close;
      IBQuery1.SQL.Clear;
      IBQuery1.SQL.Add('insert into FILES (FILE_ID,FILE_DATA) values (:FILE_ID,:FILE_DATA)');
      IBQuery1.ParamByName('FILE_ID').AsInteger:=NewFileID;
      IBQuery1.ParamByName('FILE_DATA').LoadFromStream(ActiveBlob,ftBlob);
      IBQuery1.Open;
      IBTransaction1.CommitRetaining;
      ActiveBlob.Free;
     except
      IBTransaction1.RollbackRetaining;
      ActiveBlob.Free;
     end;
    end;
end;

Добавлено: 28 фев 2008, 18:30
Attid
критикую. надо пользоваться тегами [code][/code] , а то никто код читать не будет.

Добавлено: 28 фев 2008, 18:39
Кузнецов Евгений
kostyl писал(а):Прокритикуйте плиз:
Это пожалуйста.
1) Конечно, ничто так не повышает читаемость кода, как отсутствие тега Code
2)

Код: Выделить всё

     try
      IBTransaction1.RollbackRetaining;
И зачем начинать с отката неподтвержденных изменений - не понимаю.
3)

Код: Выделить всё

 
   NewFileID:=IBQuery1.FieldByName('GEN_ID').AsVariant;
   ...
  IBQuery1.ParamByName('FILE_ID').AsInteger:=NewFileID;
Ну и зачем Int64, если в итоге оно приводится к Integer?
Вместо IBQuery здесь лучше использовать IBSQL - он легче, и есть поддержка BIGINT. Если NewFileID в коде больше нигде не используется, можно от него избавиться:

Код: Выделить всё

  insert into FILES (FILE_ID,FILE_DATA) 
  values ( GEN_ID(GEN_FILE_ID,1),:FILE_DATA)
4)

Код: Выделить всё

     try
        ...  
     except
      IBTransaction1.RollbackRetaining;
      ActiveBlob.Free;
     end;
Не стоит молча гасить исключение - пользователь будет пребывать в уверенности, что все данные сохранены и в случае отката. Лучше вернуть исключение к жизни с помощью ключевого слова raise.

5) С ActiveBlob лучше поступить так:

Код: Выделить всё

   ActiveBlob:=TIBBlobStream.Create;
   try
    // Работаем с  ActiveBlob
   finally
      FreeAndNil(ActiveBlob);
   end;
   ...
   IBTransaction1.CommitRetaining;
6) Стоит отделять логику от интерфейса

Код: Выделить всё

procedure TForm1.BitBtn6Click(Sender: TObject);
begin
   if IBDatabase1.Connected then
     if OpenDialog1.Execute then
        DoSaveBlob(OpenDialog1.FileName);
end;
Ну а за Retaining Вас KDV поругает, когда вернется.

Добавлено: 29 фев 2008, 00:46
kdv
тут и критиковать нечего. ужасные действия в коде.
кроме того, нифига ж не читал, если сначала Post, а потом Free.
И вообще где достать нормальную документацию по использованию IBX 7.11. Во всех книгах одна фигня...
документация не нужна. есть ibx.htm.
кстати, была дока - в хелпе D7, отдельным файлом. Но увы.
Кстати. как работать - есть описание в developer guide по дельфям.

Добавлено: 29 фев 2008, 20:40
Кузнецов Евгений
Доброго времени суток!
kdv писал(а):кроме того, нифига ж не читал, если сначала Post, а потом Free.
Загрузка через LoadFromStream ведется в параметр (т.е. TIBXSQLVAR), а не в TIBBlobStream непосредственно.
В TIBXSQLVAR.LoadFromStream создается и уничтожается временный TIBBlobStream, так что вроде бы ничего страшного.

Кстати, а в силу чего такой запрет (статью я читал :) )?
Физическая запись BLOB происходит при Post, и если мы убъем TIBBlobStream раньше, то он будет закрыт и не сохранен в БД?

Добавлено: 03 мар 2008, 10:03
kdv
Физическая запись BLOB происходит при Post, и если мы убъем TIBBlobStream раньше, то он будет закрыт и не сохранен в БД?
а пофиг. есть такое правило кодирования - ресурсы освобождаются в том порядке, в котором они аллокируются.
Физическая запись BLOB происходит при Post, и если мы убъем TIBBlobStream раньше
ты уверен? код IBX смотрел?

Добавлено: 03 мар 2008, 12:03
Кузнецов Евгений
Доброго времени суток!
kdv писал(а):а пофиг. есть такое правило кодирования - ресурсы освобождаются в том порядке, в котором они аллокируются.
В обратном :) Правда, не вижу связи с данным случаем.
ты уверен?

Там знак вопроса стоял.
код IBX смотрел?
В TIBBlobStream.Destroy есть только isc_close_blob - закрытие BLOB. isc_put_segment встречается только в IBBlob.WriteBlob
WriteBlob вызывается только в TIBXSQLVAR.Assign (кажется, не используется) и в TIBBlobStream.Finalize. Последний вызывается в TIBCustomDataSet.InternalPostRecord, TIBBlobStream.CloseBlob, TIBXSQLVAR.LoadFromStream.
CloseBlob не используется, InternalPostRecord вызывается при Post,
LoadFromStream вызывается в TIBCustomDataSet.InternalSetParamsFromCursor,
TIBXSQLVAR.SetAsString
Поэтому неясно, рабочий ли код:

Код: Выделить всё

  IBDataSet1.Edit;   
  B:=IBDataSet1.CreateBlobStream(IBDataSet1.FieldByName('BLB') as TBlobField, bmWrite);   
  B.LoadFromFile('c:\blob.bin');   
  B.Free;   
  IBDataSet1.Post;
Возможно, я что-то не учел?

Добавлено: 03 мар 2008, 12:17
kostyl
Огромное спасибо за коментарии. Почувствовать себя немощьным новичком - великое благо ибо оно стимулирует на большие подвиги.
kdv писал(а):
нифига ж не читал, если сначала Post, а потом Free
Читал, да наверно значение на предал. Виноват.
Кузнецов Евгений писал(а):
Ну а за Retaining Вас KDV поругает, когда вернется
А. Так и не поругал.... :)
P.S.:А вообщем хорошо, что есть такой сайт как этот, хотя всё равно трудно идёт "разбор" с клиент-сервером, как с непознанным доселе. Сейчас активно изучал статью про транзакции. Довольно сложно разобраться со всеми блокировками, параметрами, хоть там и даны маленькие примеры в конце, которые могут подойти, но хотелось бы по конкретнее - на реальном примере. Все равно большое спасибо. Вот подучусь и напишу новую версию кода.

Добавлено: 03 мар 2008, 13:17
kdv
А. Так и не поругал...
а мне уже до лампочки. в статьях по транзакциям на сайте про retaining как на каждом столбе написано.
но хотелось бы по конкретнее - на реальном примере.
на реальном примере нужны только 2 типа транзакций, это по минимуму. read_committed rec_version nowait для просмотра и
concurrency nowait например для отчетов

все остальное - по вкусу и по ситуации. Читать - это правильно, причем не один раз, а пока дойдет. Я исходную доку по транзакциям IB 4.0 читал раз 5-6, пока дошло. Это при том что там страниц 10 всего (или меньше).

Добавлено: 03 мар 2008, 13:32
kdv
Поэтому неясно, рабочий ли код:
перед вставкой в FAQ я код проверял. ты тоже можешь проверить - всего-то две строчки местами переставить. Проверил-бы и уличил меня, если я неправ. Мало-ли что.
Я вот почему-то помню, что если Free ставили после Post, то были глюки, особенно в BDE. Отсюда и рекомендация.

Добавлено: 03 мар 2008, 22:44
Кузнецов Евгений
Доброго времени суток!
kdv писал(а):перед вставкой в FAQ я код проверял. ты тоже можешь проверить - всего-то две строчки местами переставить. Проверил-бы и уличил меня, если я неправ. Мало-ли что.
Не в чем Вас уличать :) Написано же,
Статья писал(а):В предыдущих версиях IBX (до 4.42 включительно) можно было работать с blob иначе.
Естественно, копание в IBX 6.08 ничего не скажет о работоспособности ранних версий.
Насколько я понял, в 6.08 CreateBlobStream возвращает не TIBBlobStream как раньше, а его достаточно беззубую обертку TIBDSBlobStream. LoadFromFile в последнем не реализован, поэтому для чтения из файла придется создавать дополнительный поток

Код: Выделить всё

var B: TIBDSBlobStream;
    fs:TFileStream;
begin
    IBDataSet1.Edit;
    B:=IBDataSet1.CreateBlobStream(IBDataSet1.FieldByName('DATA') as TBlobField, bmWrite) as TIBDSBlobStream;
    try
      fs:=TFileStream.Create('c:\blob.bin',fmOpenRead);
      try
        B.CopyFrom(fs,fs.Size)
      finally
        FreeAndNil(fs);
      end;
      IBDataSet1.Post;
    finally
      FreeAndNil(B);
    end;
    //IBDataSet1.Post;
end;
Так как в деструкторе класса TIBDSBlobStream сам TIBBlobStream не уничтожается (утечка памяти тут не грозит, поскольку TIBBlobStream хранится во внутреннем списке IBDataset.FBlobStreamList, который убивается при закрытии IBDataset), то особой разницы, где именно вызывать Post, нет - эксперимент показал успешность обоих вариантов.

Единственное отличие может быть только, если используются DB-Aware компоненты для отображения BLOB - деструктор TIBDSBlobStream посылает событие об изменении BLOB-поля, поэтому в этом случае логичнее делать Post после освобождения B.

Добавлено: 04 мар 2008, 11:17
kostyl
Так пост потом фри или фри потом пост. Вы уж договоритесь как и когда, а то я "непонимать" чё когда там вызывается, уничтожается и т. д. Очень хочеться узнать :wink:

Добавлено: 04 мар 2008, 12:25
kdv
мне больше интересно, почему "вызывается" без мягкого знака, а "хочеться" - с мягким знаком. Ты уж определись, пожалуйста. Или ставь его везде, или только там где надо.

Добавлено: 04 мар 2008, 13:43
kostyl
kdv писал(а):мне больше интересно, почему "вызывается" без мягкого знака, а "хочеться" - с мягким знаком. Ты уж определись, пожалуйста. Или ставь его везде, или только там где надо.
"хочеться" с мягким знаком, потому как "непонимать" в инфинитиве и слитно. Вот. А еще такой вопрос - что вы подразумеваете под словом "АЛЛОКИРУЕТСЯ" - привязывается или как. лок вроде замок. а значит замыкается. или как?

Добавлено: 04 мар 2008, 14:28
stix-s
kostyl писал(а):
kdv писал(а):мне больше интересно, почему "вызывается" без мягкого знака, а "хочеться" - с мягким знаком. Ты уж определись, пожалуйста. Или ставь его везде, или только там где надо.
"хочеться" с мягким знаком, потому как "непонимать" в инфинитиве и слитно. Вот. А еще такой вопрос - что вы подразумеваете под словом "АЛЛОКИРУЕТСЯ" - привязывается или как. лок вроде замок. а значит замыкается. или как?
с мягким знаком пишут хотеться, а не хочется :)
аллокировать в данном случае - выделять

Добавлено: 04 мар 2008, 14:38
kdv
"хочеться" с мягким знаком, потому как "непонимать" в инфинитиве и слитно.
бред. ты вслух произнеси "хочеться" и "хочется". Поймешь в чем разниться. :-)
АЛЛОКИРУЕТСЯ
это от английского слова allocate. см. словарь. lingvo.yandex.ru.

Добавлено: 04 мар 2008, 15:50
kostyl
Ну ладно отпечатался и всё...
allocate - выделять ресурсы под объект вроде. Да? А какая разница когда выделять и убивать? Типа чтоб память не фрагментировалсь?