ORM para C++: comparação e exemplos
No mundo do C++, o trabalho com bancos de dados historicamente foi mais de baixo nível do que em linguagens como Python ou Java. No entanto, projetos modernos exigem conveniência e produtividade, então os desenvolvedores estão cada vez mais recorrendo ao ORM (Object-Relational Mapping). ORM para C++ permite trabalhar com tabelas como objetos C++ comuns, gerar automaticamente consultas SQL e gerenciar migrações de esquema. Neste artigo, veremos cinco bibliotecas populares: ODB, SQLiteCpp, SOCI, cppdb e sqlpp11, compararemos suas características e daremos exemplos práticos.
1. ODB — padrão corporativo
ODB é um poderoso framework ORM para C++ que suporta vários SGBDs: MySQL, PostgreSQL, SQLite, Oracle, SQL Server. A principal característica é a geração de código: você descreve o modelo em um arquivo de cabeçalho com anotações, e o compilador ODB (odb) cria o código para trabalhar com o BD.
// person.hxx#pragma once#include <string>#include <odb/core.hxx>
#pragma db objectclass 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_;};Após executar odb -d mysql person.hxx, são gerados arquivos com a implementação. Em seguida, usamos o padrão padrão:
#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); // Carregamento auto loaded = db->load<Person>(1); return 0;}Prós: ORM completo, suporte a migrações, lazy loading, trabalho com transações. Contras: configuração complexa, necessidade de geração de código, peso para projetos pequenos.
2. SQLiteCpp — leveza e simplicidade
SQLiteCpp é um wrapper sobre SQLite3 no estilo C++ RAII. Não é um ORM completo, mas fornece uma interface conveniente para trabalhar com consultas.
#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(); // Leitura SQLite::Statement query(db, "SELECT * FROM person WHERE age > ?"); query.bind(1, 20); while (query.executeStep()) { std::cout << query.getColumn(1) << " " << query.getColumn(2) << ""; } return 0;}Prós: minimalista, não requer geração de código, funciona apenas com SQLite. Contras: sem mapeamento de objetos, escrita manual de SQL.
3. SOCI — abstração universal
SOCI (The C++ Database Access Library) não é um ORM puro, mas uma abstração sobre diferentes SGBDs (SQLite, PostgreSQL, MySQL, Oracle). Fornece uma sintaxe conveniente para executar consultas e vincular resultados a estruturas 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