Уже больше года прошло с момента публикации статьи «SQLite в Delphi 2010.» Надо сказать, что рассмотренный в статье SQLiteWrapper я успешно использовал и в Delphi 2010 и в Delphi XE. Знаю, что есть ещё несколько подобных реализаций для работы с SQLite в Delphi, но почему-то этот wrapper мне понравился более всего — небольшой по размеру, удобный в использовании, простой в изучении. Что ещё для счастья нужно? Никакого паролирования мне для баз SQLite мне не требовалось поэтому использовал себе и использовал разработку Тима Андерсона (Tim Anderson). Но, как говориться, прогресс не стоит на месте и вот вышла Delphi XE2, сама SQLite вроде бы тоже несколько раз «апнулась». Соответственно, поддержка x64, FireMonkey и прочие довольно обширные нововведения в Delphi, а также обновление библиотеки SQLIte.dll внесли свои коррективы в разработку…и мой любимый враппер дал сбой. Нет, он конечно и раньше требовал, например, передачу в параметры методов исключительно AnsiString, но это, можно сказать, мелочи. А тут вдруг после сборки проекта под XE2 перестали работать некоторые методы, например FieldAs…, Bind и пр. Посыпались исключения. Первым делом глянул в репозиторий, но там оказалась ревизия от февраля месяца, т.е. для XE2 не предназначенная, да и среди нововведений значился только бэкап баз данных.
Решил немного пошерстить Сеть в поисках подходящего враппера для SQLite в Delphi XE2. И был приятно удивлен, что разработка Тима Андерсона жива, здорова и даже теперь уже «пышет» кроссплатформенностью. Правда изменились/исчезли некоторые методы, но и добавилось достаточно. Собственно эту находку я сегодня и решил представить в своем блоге.
Итак, первым делом заходим в репозиторий на Google Code и скачиваем исходники себе на компьютер.
Если не знаете как работать с Google Code — читаем «Совместная работа над проектом. Основы работы с Google Code» и «Совместная работа над проектом. Работа в проектах на Code.Google«.
Итак, что появилось в обновленной версии. Прежде всего — это поддержка Unicode. Теперь все методы работают не с AnsiString как это было в предыдущей версии, а со String, что избавляет от некоторых лишних «телодвижений» при работе с SQLite в Delphi.
Появились типы TDate и TDateTime.
Для класса TSQLiteDatabase:
- Добавлен метод Attach для добавления к текущему соединению нового файла базу данных (подробнее см. документацию);
- Добавлен классовый метод AddNewSupportedColumnType;
- Удален метод BindData
- Удалены несколько перегруженных методов, таких как GetTable, GetTableString, GetTableValue
Для класса TSQLiteTable
- Добавлено свойство IsLastRow: boolean
- Удалено свойство FieldAsString и вместо него добавлено аналогичное — FieldValByNameAsString (зачем? Посмотрим ниже)
- Добавлен новый метод FindField(const AFieldName: string): TSQLiteField;
- Добавлены новые свойства FieldsVal, FieldByName, FieldByNameAsString, FieldIndex, SQL, Stmt
Добавлен новый класс TSQLitePreparedStatement
Добавлен новый клсс TSQLiteField
Ну, а раз появились новые классы, методы и свойства, то их надо изучить и попробовать использовать в работе. Для примера создадим простенькую базу данных, состоящую из одной таблички и будем читать/писать данные, по возможности использую новые типы данных.
Для создания базы данных можете выбрать любую из программ для работы с SQLite, например, рассмотренных в статье «Обзор программ для администрирования баз данных SQLite.«. Я буду пользоваться бесплатной версией SQLite Expert.
Итак, создаем новую базу данных с названием TestSQLite.db и таблицей:
CREATE TABLE [TestTable] ( [id] INTEGER PRIMARY KEY AUTOINCREMENT, [StringRow] TEXT, [NumberRow] INTEGER, [DateTimeRow] DATETIME);
Теперь создаем новое приложение в Delphi XE2, бросаем в папку с проектом модели SQLite3 и SQLiteTable3 и библиотеку sqlite3.dll.
Добавим на форму приложения 2 кнопки, 4 Label’а и Memo. Внешний вид приложения будет такой:
Теперь создадим три переменные в секции private класса формы:
Base: TSQLiteDatabase; Table: TSQLiteUniTable; Stmt: TSQLitePreparedStatement;//новый класс
На OnCreate будем подключаться к БД и получать количество строк в БД. Обработчик будет следующим:
procedure TForm13.FormCreate(Sender: TObject); var Rows: integer; begin Base := TSQLiteDatabase.Create(ExtractFilePath(Application.ExeName) + 'TestSQlite.db'); Stmt := Base.GetPreparedStatement('SELECT count(id) FROM [TestTable]'); Table:=Stmt.ExecQuery(); Label4.Caption := Table.FieldsAsString[0]; end;
Теперь напишем обработчик кнопки для записи 1000 значений в БД. Для этого будем использовать в запросе параметры следующим образом:
procedure TForm13.Button1Click(Sender: TObject); var i: integer; t: cardinal; begin t := GetTickCount; Stmt.SQL := 'INSERT INTO [TestTable] ([StringRow], [NumberRow], [DateTimeRow]) VALUES (:str, :int, :date)'; for i := 0 to 999 do begin Stmt.ClearParams; Stmt.SetParamInt(':int', i); Stmt.SetParamText(':str', IntToStr(i)); Stmt.SetParamDateTime(':date', Now); Stmt.PrepareStatement(Stmt.SQL); Stmt.ExecQuery(); end; t := GetTickCount - t; Label1.Caption := Format('Добавленно %d записей за %d мс', [i, t]); end;
Посмотрим за какое время при таком подходе в БД запишется 1000 записей:
Больше 86 секунд. Медленно. Попробуем ускорить процесс. Дописываем в обработчик следующий код:
Base.BeginTransaction; try for i := 0 to 999 do begin [...] end; finally Base.Commit; end;
Проверяем скорость:
Менее 1 секунды на запись 1000 элементов. Неплохо? Транзакции в SQLite рулят =)
Двигаемся далее. Теперь попробуем прочитать первые 1000 записей из базы данных и вывести эти значения в Memo. Так как объект типа TSQLitePreparedStatement у нас уже есть, то нам достаточно сменить SQL-запрос и получить таблицу данных (TSQLiteUniTable). Кстати, при чтении посмотрим в действии новый класс TSQLiteField. Итак, обработчик кнопки на чтение данных из базы:
procedure TForm13.Button2Click(Sender: TObject); const cStr = '%s - %d - %s'; var t:cardinal; begin Memo1.Lines.Clear; Stmt.SQL:='SELECT * FROM TestTable LIMIT 1000'; Table:=Stmt.ExecQuery; t := GetTickCount; while not Table.EOF do begin {получаем по имени поля объект TSQLiteField и получаем значение} Memo1.Lines.Add(Format(cStr,[Table.FieldByName['StringRow'].AsString, Table.FieldByName['NumberRow'].AsInteger, Table.FieldByName['DateTimeRow'].AsString])); Table.Next; end; t := GetTickCount - t; Label2.Caption := Format('Получено 1000 записей за %d мс', [t]); end;
У меня чтение 1000 записей и вывод их в Memo заняло чуть больше 1,5 секунд. В приведенном выше коде я использовал работу с новым классом TSQLiteField в котором содержатся методы и свойства необходимые для работы с полями БД.
Итак, что можно сказать по поводу обновленной библиотеки для SQLite в Delphi XE2:
- Библиотека стала более соответствовать реальному положению дел в Delphi — поддержка Unicode тому подтверждение.
- Новые классы библиотеки, такие как TSQLitePreparedStatement и TSQLiteField, на мой взгляд, делают работы с программой ещё более прозрачной. Так, с помощью TSQLiteDatabase мы можем подключаться к БД, делать резервное копирование, приаттачивать другие базы и т.д., с помощью TSQLitePreparedStatement — управлять запросами к базе данных и получать объекты TSQLiteUniTable, на, а с TSQLiteUniTable мы получаем доступ к не обходимым полям таблицы и, используя возможности TSQLiteField работать непосредственно со значениями полей. По-моему вполне логично и удобно.
Книжная полка
Описание Книга основана на материалах лекций и практических занятий, разработанных автором, и объединяет теоретические основы и практические аспекты разработки реляционных баз данных.
|
Купить на ЛитРес | 383 руб. | |
Автор: Анатолий Хомоненко, Владимир Гофман Название:Работа с базами данных в Delphi Описание: Рассматривается использование средств Delphi для разработки приложений баз данных. Даются понятия баз данных, характеризуются элементы и описываются этапы проектирования реляционных баз данных, изложена технология разработки информационных систем, освещаются приемы работы с данными, создание таблиц и приложений баз данных, подготовка отчетов. |
Купить на ЛитРес | 151 руб. |
Хотелось бы посмотреть на это через интерфейс FMX. т.к. ни одного внятного примера работы с гридами я не нашел.
В идеале Сверху Memo с запросом, снизу сетка с данными. И было бы неплохо все это расписать от и до.
elpik, попробую реализовать что-нибудь подходящее. Правда я DBGrid’ы использовал редко, а с SQLite вообще ни разу — как-то не довелось по роду деятельности, но что-то самому даже интересно стало — а и вправду как заработает с гридами? =)
Linas, классная обёртка =) Буду следить за обновлениями в репозитории
Thanks for using my wrapper. Accidentally found this post, glad I can understand Russian :) Your data fetch test lasts quite long because while your data is loading your memo control is repainting itself. You could try to write Memo1.Lines.BeginUpdate before loop and Memo1.Lines.EndUpdate after loop. That should speed up things. In some of my tests I can load 100000 records in less than half a second using TSQLiteUniTable.
а в FMX вроде как DBGrid отсутствует :) (есть TGrid и TStringGrid), Хотя если честно для меня это было неожиданностью.
elpik, я с FMX вообще мало знаком — один пост в блоге и тот чисто обзорный — ничего серьезного. Но, кстати, прикинул уже чуть-чуть как подружить SQLite и гриды…пока есть мысль связать TClientDataSet + TSQLiteTable. Вроде бы даже определился с алгоритмом. Подойдет такой вариант?
Я сам с SQLite тоже не работал :)
Да это стандартный набор для доступа к БД данным, но если надо сделать произвольный запрос, предполагаю должен использоваться TSQLiteQuery, т.к. в него можно запихать любой запрос. Там есть такой компонент?
если бы это было VCL то можно было бы использовать TSQLiteQuery и TDataSource, для свядивания с DBGrid, но в FMX DBGrid’a нет, зато в LiveBinding есть TBindScopeDB, предполагаю что для связи с гридом придется использовать его.
elpik
>>в LiveBinding есть TBindScopeDB, предполагаю что для связи с гридом придется использовать его
И такое предположение было =) Сейчас чего-нибудь соображу — надо ж мне когда-то с FMX по-подробнее разбираться.
elpik — если честно TDB*** давно бы надо убрать, если не совместимость.
Очень уже они ограниченны во первых и запутанны внутри из-за истории тянущейся с Delphi 1 16-bit
Почему нельзя сделать Array of TObject и его published-компоненты засунуть в grid ?
почему нельзя привязать TDBEdit к конкретной строковой переменной ?
В том же JediVCL делали свой IDataProvider, правда кроме демонстрашек так и ничего вроде не добавили — т.е. понимать его понимают вроде многие, а вот поставлять данные неоткуда ;-)
Блин, если бы FB Embedded научился работать с in-memory таблицами… Ведь почти всё уже сделали.
Но скорее уж NexusDB выпустят
Обнаружил, что не хочу пользоваться in-memory таблицами. Все эти master-detail, индексы.
Хочется SQL’я.
Zeus вечная альфа, SQLitePass автора уже год не видно…
Надо эту пробовать :-)
Хороший ресурс с актуальными разработками по SQLite http://blog.synopse.info, там окромя обертки еще много чего имеется :)
угу… только у меня mORMot даже демку логирования не отработал в XE2
искал «богатый логгер», так и не нашёл, плюнул пока :-)
[…] Если речь идёт о работе с базами данных, например, с SQLite, то тут на 99,9% случаев придётся использовать таблицы […]
кто знает можно ли как-нибудь использовать sqlite.dll из другой директории, а не корневой где ехешник?
а почему нет? я так даже Firebird использовал — а там не одна, а четыре DLLки, да ещё разбросанные по двум папкам.
вероятно придется разобраться как библиотека доступа находит и использует DLL — и подправить если нужно — ну так разобраться всегда полезно ;-)
попробовал, не получилось (
не разобрался как этот враппер подгружает ее
поспешил) скачал новую версию компонентов и все заработало
SQLite3_4_Delphi_RC2 + DelphiXe2 = при любой попытке использования вылетает с AccessVioletion at address 0000000…
Печально …
Переписал всю подсистему отображения данных и потом нашел компоненты с готовым датасетом — Sivak3.
Есть еще odbc драйвера, но в моем случае не подходит.
при чтение memo остается пустым!! Хотя подключение к базе проходит и записи добавляются. В чем проблема?
Brunnen G, без понятия. Код где?
Мда. Все утро и до сейчас, это около 6 часов, промучился с чтением. Пока не въехал:
Проблема из волшебной строчки
«Stmt := Base.GetPreparedStatement(‘SELECT count(id) FROM [TestTable]’);»
прописанной на событие OnCreate формы.
Строка
«Stmt.SQL:=’SELECT * FROM [TestTable] LIMIT 1000′;»
ничего вам не даст, пока перед ней вы не пропишите:
«Stmt := Base.GetPreparedStatement(‘SELECT * FROM [TestTable]’);»
На твоей странице))
Все копипастил с твоей статьи)
При попытке добавления пакета при Install выдает:
—————————
pck_SQLite3_XE2 — Delphi XE2 — Welcome Page [Built]: bds.exe — Entry Point Not Found
—————————
The procedure entry point @Data@Db@TDataSet@IsCursorOpen$qqrv could not be located in the dynamic link library dbrtl160.bpl.
OK