ORM for C++: Comparison and Usage Examples
In modern C++ development, working with databases often requires a balance between performance and convenience. Unlike languages with a rich ORM ecosystem (such as Python or Java), C++ offers a narrower but mature selection of tools. In this article, we will compare four popular libraries: ODB, SQLiteCpp, Soci, and sqlpp11. We will examine their architecture, syntax, performance, and code examples.
1. ODB — The Industry Standard
ODB is a full-featured ORM compiler for C++. It generates code from header files, supports relational databases (MySQL, PostgreSQL, SQLite, Oracle), and provides powerful object-relational mapping.
Key Features:
- Automatic code generation via a precompiler (odb)
- Support for inheritance, polymorphism, and STL containers
- Lazy loading, caching, transactions
Example: Model and Query
// person.hxx#pragma once#include <string>#include <odb/core.hxx>
#pragma db objectclass 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"); // Insert Person p("Alice", 30); db->persist(p); // Query using query = odb::query<Person>; auto result = db->query<Person>(query::age > 25); for (const auto& person : result) { std::cout << person.name() << ""; } return 0;}2. SQLiteCpp — Lightweight Wrapper
SQLiteCpp is a minimalist C++ wrapper over the SQLite C API. It is not a full ORM, but it provides a convenient RAII interface for working with SQL queries.
Key Features:
- Dependency only on SQLite3
- Simple API: Database, Statement, Transaction
- Support for prepared statements and parameter binding
Example: Insert and Select
#include <SQLiteCpp/Database.h>#include <iostream>
int main() { SQLite::Database db("test.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); // Create table db.exec("CREATE TABLE IF NOT EXISTS person (id INTEGER PRIMARY KEY, name TEXT, age INT)"); // Insert with parameters SQLite::Statement insert(db, "INSERT INTO person (name, age) VALUES (?, ?)"); insert.bind(1, "Bob"); insert.bind(2, 25); insert.exec(); // Select 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() << ""; } return 0;}3. Soci — Universal Database Access
Soci is a library for working with various databases (MySQL, PostgreSQL, SQLite, Oracle) through a unified interface. It provides both dynamic queries and support for ORM-like mapping.
Key Features:
- Support for multiple backends via plugins
- Parameter binding using the << operator and into()
- Support for transactions and bulk operations
Example: Working with Queries
Also in library
Recommendations