При работе с базами данных в Delphi и Pascal разработчики часто сталкиваются с ошибкой "database is locked" (база данных заблокирована). Эта проблема особенно актуальна при переходе с локальных форматов данных (как TDbf) на SQLite. В этой статье мы разберем причины этой ошибки и покажем правильные способы работы с транзакциями в SQLite.
Проблема: база данных остается заблокированной
Как видно из обсуждения на форуме, пользователь столкнулся с ситуацией, когда после добавления записей и закрытия приложения изменения не сохранялись, а при повторном запуске возникала ошибка "database is locked". Основная причина - неправильное завершение работы с базой данных.
Решение: правильное использование транзакций
1. Основной подход с явным Commit
Перед закрытием приложения необходимо явно завершать транзакции:
procedure TFrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// Закрываем запросы
if QryMain.Active then
QryMain.Close;
// Завершаем транзакцию
if SQLTransaction1.Active then
begin
try
SQLTransaction1.Commit;
except
on E: Exception do
begin
// В случае ошибки выполняем откат
SQLTransaction1.Rollback;
ShowMessage('Ошибка при сохранении данных: ' + E.Message);
end;
end;
end;
// Закрываем соединение
if SQLite3Connection1.Connected then
SQLite3Connection1.Connected := False;
end;
2. Автоматический Commit через настройки запроса
Более простой способ - использование автоматического Commit:
procedure TFrmMain.FormCreate(Sender: TObject);
begin
SQLQuery1.Options := [sqoAutoApplyUpdates, sqoAutoCommit];
// Другие настройки...
end;
Важные рекомендации
Одно соединение на базу данных:
// Неправильно (для SQLite):
ConnectContacts := TSQLite3Connection.Create(nil);
ConnectCountries := TSQLite3Connection.Create(nil);
// Правильно:
DBConnection := TSQLite3Connection.Create(nil); // Одно соединение для всех запросов
Разделение логики доступа к данным:
Создайте отдельный модуль данных (DataModule)
Разместите там соединение и транзакцию
Все формы должны работать через этот модуль
Пример модуля данных:
unit DataModuleUnit;
interface
uses
SysUtils, Classes, SQLite3Conn, SQLDB, DB;
type
TDataModule1 = class(TDataModule)
SQLite3Connection1: TSQLite3Connection;
SQLTransaction1: TSQLTransaction;
procedure DataModuleCreate(Sender: TObject);
procedure DataModuleDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
DataModule1: TDataModule1;
implementation
{$R *.dfm}
procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin
SQLite3Connection1.DatabaseName := 'my_database.sqlite';
SQLite3Connection1.Connected := True;
SQLTransaction1.Active := True;
end;
procedure TDataModule1.DataModuleDestroy(Sender: TObject);
begin
if SQLTransaction1.Active then
SQLTransaction1.Commit;
SQLite3Connection1.Connected := False;
end;
end.
Работа с несколькими таблицами:
// В модуле данных добавляем запросы
SQLQueryContacts: TSQLQuery;
SQLQueryCountries: TSQLQuery;
// Настройка запросов
procedure TDataModule1.SetupQueries;
begin
SQLQueryContacts.Database := SQLite3Connection1;
SQLQueryContacts.Transaction := SQLTransaction1;
SQLQueryContacts.SQL.Text := 'SELECT * FROM Contacts';
SQLQueryCountries.Database := SQLite3Connection1;
SQLQueryCountries.Transaction := SQLTransaction1;
SQLQueryCountries.SQL.Text := 'SELECT * FROM Countries';
end;
Работа с формами редактирования
Для форм редактирования справочников (стран, категорий и т.д.) используйте следующий подход:
procedure TMainForm.OpenCountriesForm(Sender: TObject);
var
FrmCountries: TFrmCountries;
begin
FrmCountries := TFrmCountries.Create(nil);
try
// Передаем подключение к данным
FrmCountries.SetDataModule(DataModule1);
FrmCountries.ShowModal;
finally
FrmCountries.Free;
end;
end;
Логирование SQL-запросов
Для отладки добавьте логирование:
procedure TDataModule1.SQLite3Connection1Log(Sender: TSQLConnection;
EventType: TDBEventType; const Msg: String);
var
LogFile: TextFile;
begin
AssignFile(LogFile, 'sql_log.txt');
if FileExists('sql_log.txt') then
Append(LogFile)
else
Rewrite(LogFile);
try
WriteLn(LogFile, FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + ' - ' + Msg);
finally
CloseFile(LogFile);
end;
end;
// Включите логирование при создании модуля
SQLite3Connection1.LogEvents := LogAllEvents;
SQLite3Connection1.OnLog := @SQLite3Connection1Log;
Заключение
Основные правила работы с SQLite в Delphi:
1. Используйте одно соединение на базу данных
2. Всегда завершайте транзакции (Commit/Rollback) перед закрытием
3. Разделяйте логику доступа к данным и пользовательский интерфейс
4. Для простых случаев используйте автоматический Commit
5. Добавляйте логирование для отладки проблем
Следуя этим рекомендациям, вы избежите ошибок "database is locked" и других проблем при работе с SQLite в Delphi и Lazarus.
Описание проблемы и методов сохранения изменений в базах данных SQLite в Delphi и Pascal с использованием транзакций Commit.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.