Что такое SQLite и зачем он нужен в C++?
SQLite — это легковесная, встраиваемая реляционная база данных, которая не требует отдельного серверного процесса. В отличие от MySQL или PostgreSQL, SQLite работает непосредственно с файлом базы данных на диске, что делает её идеальным выбором для настольных приложений, мобильных устройств, встраиваемых систем и небольших веб-проектов.
Для языка C++ существует несколько способов работы с SQLite. Самый популярный и стабильный — это использование официальной библиотеки sqlite3 (C API), которая поставляется в виде набора C-функций. Однако для более удобной работы в C++ часто используют обёртки, такие как SQLiteCpp или sqlite_modern_cpp. В этой статье мы сосредоточимся на прямом использовании C API (библиотека sqlite3), так как это даёт максимальный контроль и понимание происходящих процессов.
Основные преимущества SQLite:
- Нулевая конфигурация: не нужно устанавливать и настраивать сервер.
- Портативность: база данных — это один файл.
- Надёжность: поддержка транзакций (ACID).
- Скорость: для большинства операций работает быстрее клиент-серверных БД.
- Размер: библиотека занимает менее 600 КБ.
Установка SQLite для C++
Установка зависит от вашей операционной системы и компилятора. Мы рассмотрим три основных способа.
1. Установка через пакетный менеджер (Linux/macOS)
Ubuntu/Debian:
sudo apt-get update
sudo apt-get install libsqlite3-dev
macOS (Homebrew):
brew install sqlite3
Fedora:
sudo dnf install sqlite-devel
2. Установка на Windows (vcpkg)
Если вы используете vcpkg:
vcpkg install sqlite3:x64-windows
vcpkg integrate install
После этого в Visual Studio можно подключать #include <sqlite3.h>.
3. Компиляция из исходников (универсальный способ)
Скачайте файлы sqlite3.c и sqlite3.h с официального сайта sqlite.org. Затем просто добавьте их в ваш проект:
g++ main.cpp sqlite3.c -o myapp -lpthread -ldl
На Windows может потребоваться дополнительно указать -lws2_32.
Быстрый старт: первая программа с SQLite на C++
Создадим простое консольное приложение, которое открывает базу данных, создаёт таблицу и вставляет одну запись.
#include <iostream>
#include <sqlite3.h>
int main() {
sqlite3 *db;
char *errMsg = 0;
// 1. Открываем (или создаём) базу данных
int rc = sqlite3_open("test.db", &db);
if (rc) {
std::cerr << "Ошибка открытия БД: " << sqlite3_errmsg(db) << std::endl;
return 1;
}
// 2. Создаём таблицу
const char *sqlCreate = "CREATE TABLE IF NOT EXISTS users ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"name TEXT NOT NULL, "
"age INTEGER);";
rc = sqlite3_exec(db, sqlCreate, 0, 0, &errMsg);
if (rc != SQLITE_OK) {
std::cerr << "Ошибка SQL: " << errMsg << std::endl;
sqlite3_free(errMsg);
sqlite3_close(db);
return 1;
}
// 3. Вставляем данные
const char *sqlInsert = "INSERT INTO users (name, age) VALUES ('Alice', 30);";
rc = sqlite3_exec(db, sqlInsert, 0, 0, &errMsg);
if (rc != SQLITE_OK) {
std::cerr << "Ошибка вставки: " << errMsg << std::endl;
sqlite3_free(errMsg);
} else {
std::cout << "Запись добавлена, ID: " << sqlite3_last_insert_rowid(db) << std::endl;
}
// 4. Закрываем базу
sqlite3_close(db);
return 0;
}
Скомпилируйте и запустите. В текущей папке появится файл test.db.
Основные методы и функции SQLite C API
Рассмотрим 10 ключевых функций, которые покроют 90% ваших задач.
1. sqlite3_open()
Что делает: Открывает соединение с файлом базы данных. Если файл не существует, создаёт его.
Параметры:
const char *filename— путь к файлу БД (или ":memory:" для in-memory БД).sqlite3 **ppDb— указатель на указатель на объект соединения.
Возвращает: SQLITE_OK (0) при успехе, иначе код ошибки.
Пример:
sqlite3 *db;
int rc = sqlite3_open("mydb.sqlite", &db);
if (rc != SQLITE_OK) {
// Обработка ошибки
}
2. sqlite3_close()
Что делает: Закрывает соединение с БД и освобождает ресурсы.
Параметры:
sqlite3 *db— указатель на соединение.
Пример:
sqlite3_close(db);
3. sqlite3_exec()
Что делает: Выполняет один или несколько SQL-запросов, разделённых точкой с запятой. Удобен для DDL (CREATE, DROP) и простых DML.
Параметры:
sqlite3 *db— соединение.const char *sql— SQL-запрос(ы).int (*callback)(void*,int,char**,char**)— функция обратного вызова для обработки результатов (можно NULL).void *arg— пользовательские данные для callback.char **errmsg— указатель на строку с сообщением об ошибке.
Пример:
char *errMsg = 0;
const char *sql = "CREATE TABLE IF NOT EXISTS test (id INT);";
int rc = sqlite3_exec(db, sql, 0, 0, &errMsg);
if (rc != SQLITE_OK) {
std::cerr << errMsg;
sqlite3_free(errMsg);
}
4. sqlite3_prepare_v2()
Что делает: Компилирует SQL-запрос в подготовленный стейтмент. Это основной способ выполнения запросов с параметрами.
Параметры:
sqlite3 *db— соединение.const char *zSql— SQL-запрос.int nByte— длина строки (или -1, если строка нуль-терминирована).sqlite3_stmt **ppStmt— указатель на стейтмент.const char **pzTail— указатель на остаток строки (можно NULL).
Пример:
sqlite3_stmt *stmt;
const char *sql = "SELECT * FROM users WHERE age > ?";
sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
sqlite3_bind_int(stmt, 1, 25); // Подставляем значение
5. sqlite3_bind_*() (bind_int, bind_text, bind_double, bind_null)
Что делает: Привязывает значения к параметрам (плейсхолдерам) в подготовленном запросе. Параметры обозначаются знаком ? или ?NNN или :AAA.
Параметры:
sqlite3_stmt *stmt— стейтмент.int index— индекс параметра (начиная с 1).value— значение соответствующего типа.
Пример:
sqlite3_bind_int(stmt, 1, 42);
sqlite3_bind_text(stmt, 2, "John", -1, SQLITE_TRANSIENT);
sqlite3_bind_double(stmt, 3, 3.14);
sqlite3_bind_null(stmt, 4);
6. sqlite3_step()
Что делает: Выполняет один шаг стейтмента. Для SELECT возвращает строку результата (SQLITE_ROW), для INSERT/UPDATE/DELETE — SQLITE_DONE.
Параметры:
sqlite3_stmt *stmt— стейтмент.
Пример:
while (sqlite3_step(stmt) == SQLITE_ROW) {
int id = sqlite3_column_int(stmt, 0);
const char *name = sqlite3_column_text(stmt, 1);
std::cout << id << " " << name << std::endl;
}
7. sqlite3_column_*() (column_int, column_text, column_double, column_bytes)
Что делает: Извлекает значение из текущей строки результата по индексу колонки (начиная с 0).
Параметры:
sqlite3_stmt *stmt— стейтмент.int iCol— индекс колонки.
Пример:
int age = sqlite3_column_int(stmt, 2);
const unsigned char *name = sqlite3_column_text(stmt, 1);
int nameLen = sqlite3_column_bytes(stmt, 1);
8. sqlite3_finalize()
Что делает: Уничтожает подготовленный стейтмент и освобождает память. Обязателен после завершения работы со стейтментом.
Параметры:
sqlite3_stmt *stmt— стейтмент.
Пример:
sqlite3_finalize(stmt);
9. sqlite3_errmsg()
Что делает: Возвращает текст последней ошибки для данного соединения.
Параметры:
sqlite3 *db— соединение.
Пример:
if (rc != SQLITE_OK) {
std::cerr << "Ошибка: " << sqlite3_errmsg(db) << std::endl;
}
10. sqlite3_last_insert_rowid()
Что делает: Возвращает ID последней вставленной строки в текущем соединении (для таблиц с AUTOINCREMENT).
Параметры:
sqlite3 *db— соединение.
Пример:
sqlite3_exec Также в библиотеке
ORM для Java: сравнение Hibernate, MyBatis и JPA с примерами
Clikt: Мощный CLI фреймворк для Kotlin с декларативным синтаксисом
Кэширование в Node.js: Redis и альтернативы для ускорения приложений
Обработка изображений в Java: библиотеки и примеры кода
Мониторинг .NET-приложений с Prometheus и OpenTelemetry