ORM для C++: сравнение и примеры использования
В современной разработке на C++ работа с базами данных часто требует баланса между производительностью и удобством. В отличие от языков с богатой экосистемой ORM (например, Python или Java), C++ предлагает более узкий, но зрелый выбор инструментов. В этой статье мы сравним четыре популярные библиотеки: ODB, SQLiteCpp, Soci и sqlpp11. Рассмотрим их архитектуру, синтаксис, производительность и примеры кода.
1. ODB — промышленный стандарт
ODB — это полноценный ORM-компилятор для C++. Он генерирует код из заголовочных файлов, поддерживает реляционные базы (MySQL, PostgreSQL, SQLite, Oracle) и предоставляет мощный объектно-реляционный маппинг.
Ключевые особенности:
- Автоматическая генерация кода через прекомпилятор (odb)
- Поддержка наследования, полиморфизма, контейнеров STL
- Ленивая загрузка, кэширование, транзакции
Пример: модель и запрос
// person.hxx
#pragma once
#include <string>
#include <odb/core.hxx>
#pragma db object
class Person {
public:
Person() = default;
Person(const std::string& name, int age)
: name_(name), age_(age) {}
const std::string& name() const { return name_; }
int age() const { return age_; }
private:
friend class odb::access;
#pragma db id auto
unsigned long id_;
std::string name_;
int age_;
};
// main.cpp
#include <odb/database.hxx>
#include <odb/sqlite/database.hxx>
#include "person.hxx"
#include "person-odb.hxx"
int main() {
auto db = std::make_shared<odb::sqlite::database>("test.db");
// Вставка
Person p("Alice", 30);
db->persist(p);
// Запрос
using query = odb::query<Person>;
auto result = db->query<Person>(query::age > 25);
for (const auto& person : result) {
std::cout << person.name() << "\n";
}
return 0;
}
2. SQLiteCpp — лёгкая обёртка
SQLiteCpp — это минималистичная C++ обёртка над SQLite C API. Она не является полноценным ORM, но предоставляет удобный RAII-интерфейс для работы с SQL-запросами.
Ключевые особенности:
- Зависимость только от SQLite3
- Простой API: Database, Statement, Transaction
- Поддержка prepared statements и биндинга параметров
Пример: вставка и выборка
#include <SQLiteCpp/Database.h>
#include <iostream>
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 name, age FROM person WHERE age > ?");
query.bind(1, 20);
while (query.executeStep()) {
std::cout << query.getColumn(0).getText()
<< " - " << query.getColumn(1).getInt() << "\n";
}
return 0;
}
3. Soci — универсальный доступ к БД
Soci — это библиотека для работы с различными базами данных (MySQL, PostgreSQL, SQLite, Oracle) через единый интерфейс. Она предоставляет как динамические запросы, так и поддержку ORM-подобного маппинга.
Ключевые особенности:
- Поддержка нескольких бэкендов через плагины
- Биндинг параметров через оператор << и into()
- Поддержка транзакций и bulk-операций
Пример: работа с запросами
#include <soci/soci.h>
#include <iostream>
int main() {
soci::session sql(soci::sqlite3, "test.db");
sql << "CREATE TABLE IF NOT EXISTS person (id INTEGER PRIMARY KEY, name TEXT, age INT)";
// Вставка
sql << "INSERT INTO person (name, age) VALUES (:name, :age)",
soci::use("Charlie"), soci::use(35);