ORM for C++: Comparison and Usage Examples (ODB, SOCI, SQLiteCpp)

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

ORM for C++: Comparison and Usage Examples



Unlike dynamically typed languages (Python, PHP), C++ does not have built-in support for reflection and metaprogramming sufficient to create a "magical" ORM. Nevertheless, there are several powerful solutions that automate working with databases. In this article, we will compare three popular libraries: ODB, SOCI, and SQLiteCpp, and provide examples of their use.



Comparison Criteria

When choosing an ORM for C++, it is worth paying attention to:

  • Code Generation Type — external generator or C++ templates.
  • DBMS Support — SQLite, PostgreSQL, MySQL, Oracle.
  • Performance — overhead of mapping.
  • Integration Complexity — whether a separate build step is required.


1. ODB — Industrial ORM for C++

ODB is a commercial (with a free version for open-source) ORM based on code generation from annotated header files. It supports relational databases (SQLite, PostgreSQL, MySQL, Oracle) and even NoSQL (MongoDB).



Example with ODB (SQLite)

First, declare the entity class:

#include <odb/core.hxx>

#pragma db objectclass Person {public: Person(const std::string& name, int age) : name_(name), age_(age) {}

unsigned long id() const { return id_; } 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_;};


Usage:

#include <odb/database.hxx>#include <odb/sqlite/database.hxx>#include <odb/transaction.hxx>#include "person.hxx"#include "person-odb.hxx"

int main() { auto db = std::make_shared<odb::sqlite::database>("test.db"); odb::transaction t(db->begin());

Person p("Alice", 30); db->persist(p); // INSERT

auto result = db->query<Person>( odb::query<Person>::age > 25); for (const auto& person : result) { std::cout << person.name() << std::endl; } t.commit();}


Pros: full object model, relationship support, lazy loading. Cons: requires a code generator (build step), commercial license.



2. SOCI — Lightweight "Data Access Layer"

SOCI is not a full-fledged ORM, but a library for working with SQL from C++ that provides a convenient syntax for executing queries and mapping results. It works with SQLite, PostgreSQL, MySQL, Oracle, and ODBC.



Example with SOCI

#include <soci/soci.h>#include <soci/sqlite3/soci-sqlite3.h>#include <iostream>

struct Person { int id; std::string name; int age;};

int main() { soci::session sql(soci::sqlite3, "test.db"); sql << "CREATE TABLE IF NOT EXISTS person (" "id INTEGER PRIMARY KEY, " "name TEXT, age INTEGER)";

// Insert sql << "INSERT INTO person (name, age) VALUES (:name, :age)", soci::use("Bob"), soci::use(25);

// Query with mapping to struct Person p; soci::indicator ind; sql << "SELECT id, name, age FROM person WHERE id = :id", soci::use(1), soci::into(p.id), soci::into(p.name, ind), soci::into(p.age);

if (ind == soci::i_ok) { std::cout << p.name << " " << p.age << std::endl; }}


Pros: easy integration, no code generation, support for prepared statements. Cons: no automatic object mapping

Recommendations