ORM для C++: сравнение и примеры
В мире C++ работа с базами данных исторически была более низкоуровневой, чем в языках вроде Python или Java. Однако современные проекты требуют удобства и продуктивности, поэтому разработчики всё чаще обращаются к ORM (Object-Relational Mapping). ORM для C++ позволяет работать с таблицами как с обычными C++ объектами, автоматически генерировать SQL-запросы и управлять миграциями схемы. В этой статье мы рассмотрим пять популярных библиотек: ODB, SQLiteCpp, SOCI, cppdb и sqlpp11, сравним их особенности и приведём практические примеры.
1. ODB — корпоративный стандарт
ODB — это мощный ORM-фреймворк для C++, который поддерживает множество СУБД: MySQL, PostgreSQL, SQLite, Oracle, SQL Server. Основная особенность — кодогенерация: вы описываете модель в заголовочном файле с аннотациями, а компилятор ODB (odb) создаёт код для работы с БД.
// person.hxx
#pragma once
#include <string>
#include <odb/core.hxx>
#pragma db object
class Person {
public:
Person() {}
Person(const std::string& name, int age)
: name_(name), age_(age) {}
const std::string& getName() const { return name_; }
int getAge() const { return age_; }
private:
friend class odb::access;
#pragma db id auto
unsigned long id_;
std::string name_;
int age_;
};
После запуска odb -d mysql person.hxx генерируются файлы с реализацией. Далее используем стандартный паттерн:
#include <odb/database.hxx>
#include <odb/mysql/database.hxx>
#include "person-odb.hxx"
int main() {
auto db = std::make_shared<odb::mysql::database>("user", "pass", "mydb");
Person p("Alice", 30);
db->persist(p);
// Загрузка
auto loaded = db->load<Person>(1);
return 0;
}
Плюсы: полный ORM, поддержка миграций, lazy loading, работа с транзакциями. Минусы: сложная настройка, необходимость кодогенерации, тяжеловесность для небольших проектов.
2. SQLiteCpp — лёгкость и простота
SQLiteCpp — это обёртка над SQLite3 в стиле C++ RAII. Не является полноценным ORM, но предоставляет удобный интерфейс для работы с запросами.
#include <SQLiteCpp/Database.h>
#include <SQLiteCpp/Statement.h>
int main() {
SQLite::Database db("test.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
db.exec("CREATE TABLE IF NOT EXISTS person (id INTEGER PRIMARY KEY, name TEXT, age INT)");
SQLite::Statement insert(db, "INSERT INTO person (name, age) VALUES (?, ?)");
insert.bind(1, "Bob");
insert.bind(2, 25);
insert.exec();
// Чтение
SQLite::Statement query(db, "SELECT * FROM person WHERE age > ?");
query.bind(1, 20);
while (query.executeStep()) {
std::cout << query.getColumn(1) << " " << query.getColumn(2) << "\n";
}
return 0;
}
Плюсы: минималистичность, не требует кодогенерации, работает только с SQLite. Минусы: нет маппинга объектов, ручное написание SQL.
3. SOCI — универсальный абстрактор
SOCI (The C++ Database Access Library) — это не ORM в чистом виде, а абстракция над разными СУБД (SQLite, PostgreSQL, MySQL, Oracle). Предоставляет удобный синтаксис для выполнения запросов и связывания результатов с C++ структурами.
#include <soci/soci.h>
#include <soci/sqlite3.h>
struct Person {
int id;
std::string name;
int age;
};
int main() {
soci::session sql(soci::sqlite3, "test.db");
sql << "CREATE TABLE person (id INTEGER PRIMARY KEY, name TEXT, age INT)";
Person p{1, "Charlie", 35};
sql << "INSERT INTO person (id, name, age) VALUES (:id, :name, :age)",
soci::use(p.id), soci::use(p.name), soci::use(p.age);
// Чтение с маппингом
Person loaded;
sql << "SELECT id, name, age FROM person WHERE id = :id",
soci::into(loaded.id), soci::into(loaded.name), soci::into(loaded.age),
soci::use(1);
return 0;
}
Плюсы: кроссплатформенность, поддержка нескольких СУБД, простота интеграции. Мину
Также в библиотеке
ImageSharp для C# (.NET): полное руководство с примерами
GraphQL Java — серверная библиотека для реализации GraphQL API на Java
Кэширование в PHP: Redis, Memcached, APCu и файловое кэширование
ORM для JavaScript (Node.js): сравнение Sequelize, TypeORM, Prisma