ODB
ODB is a cross-platform, cross-database ORM library for C++. It enables persistence of C++ objects to relational databases without manual mapping code, allowing development without awareness of tables, columns, or SQL. Through compiler-based code generation, it achieves high-performance and type-safe database access.
GitHub Overview
codesynthesis-com/odb
Object-relational mapping (ORM) system for C++
Topics
Star History
Library
ODB
Overview
ODB is a cross-platform, cross-database ORM library for C++. It enables persistence of C++ objects to relational databases without manual mapping code, allowing development without awareness of tables, columns, or SQL. Through compiler-based code generation, it achieves high-performance and type-safe database access.
Details
ODB 2025 edition is established as the most mature choice in the C++ ORM field. Supporting PostgreSQL, MySQL, SQLite, Oracle, and SQL Server, it maintains stable adoption in enterprise-level C++ application development. Its compiler-based approach provides powerful type checking and code optimization while minimizing runtime overhead.
Key Features
- Compiler-based: Automatic code generation via ODB compiler
- Multi-database: Support for PostgreSQL, MySQL, SQLite, Oracle, SQL Server
- High Performance: Minimized runtime overhead
- Type Safety: Strong compile-time type checking
- Transactions: Full ACID transaction support
- Query Language: Native C++ syntax for query writing
Pros and Cons
Pros
- Mature stability with extensive enterprise track record
- Support for multiple major databases
- High performance through compile-time optimization
- Excellent integration with existing C++ codebases
- Comprehensive documentation and support
- Professional support through commercial licensing
Cons
- GPL/Commercial dual licensing (paid for commercial use)
- Complex build process due to code generation
- Relatively steep learning curve
- Conservative support for modern C++ features
- No NoSQL database support
References
Examples
Basic Setup
// person.hxx - Entity definition
#pragma once
#include <string>
#include <memory>
#include <odb/core.hxx>
#pragma db object
class person
{
public:
person() {}
person(const std::string& first,
const std::string& last,
unsigned short age)
: first_(first), last_(last), age_(age)
{
}
// Accessors
const std::string& first() const { return first_; }
void first(const std::string& f) { first_ = f; }
const std::string& last() const { return last_; }
void last(const std::string& l) { last_ = l; }
unsigned short age() const { return age_; }
void age(unsigned short a) { age_ = a; }
private:
friend class odb::access;
#pragma db id auto
unsigned long id_;
std::string first_;
std::string last_;
unsigned short age_;
};
// Generate code with ODB compiler
// odb -d mysql --generate-query --generate-schema person.hxx
Database Operations
// main.cpp - Basic CRUD operations
#include <iostream>
#include <memory>
#include <odb/database.hxx>
#include <odb/transaction.hxx>
#include <odb/mysql/database.hxx>
#include "person.hxx"
#include "person-odb.hxx"
using namespace std;
using namespace odb::core;
int main()
{
try
{
// Database connection
unique_ptr<database> db(
new odb::mysql::database("user", "password", "database", "host"));
// CREATE - Create new records
{
person john("John", "Doe", 33);
person jane("Jane", "Smith", 28);
transaction t(db->begin());
// Persist objects
db->persist(john);
db->persist(jane);
t.commit();
cout << "Created person with id: " << john.id() << endl;
}
// READ - Read records
{
transaction t(db->begin());
// Load by ID
unique_ptr<person> p(db->load<person>(1));
cout << "Loaded: " << p->first() << " " << p->last() << endl;
// Query-based search
typedef odb::query<person> query;
typedef odb::result<person> result;
result r(db->query<person>(query::age > 25));
for (result::iterator i(r.begin()); i != r.end(); ++i)
{
cout << "Found: " << i->first() << " " << i->last()
<< ", age " << i->age() << endl;
}
t.commit();
}
// UPDATE - Update records
{
transaction t(db->begin());
unique_ptr<person> p(db->load<person>(1));
p->age(34);
db->update(*p);
t.commit();
cout << "Updated person age to: " << p->age() << endl;
}
// DELETE - Delete records
{
transaction t(db->begin());
unique_ptr<person> p(db->load<person>(1));
db->erase(*p);
t.commit();
cout << "Deleted person" << endl;
}
}
catch (const odb::exception& e)
{
cerr << "Database error: " << e.what() << endl;
return 1;
}
return 0;
}
Advanced Features
// Relationships and queries
#pragma db object
class employer
{
public:
employer() {}
employer(const std::string& name) : name_(name) {}
const std::string& name() const { return name_; }
void name(const std::string& n) { name_ = n; }
private:
friend class odb::access;
#pragma db id auto
unsigned long id_;
std::string name_;
};
#pragma db object
class employee
{
public:
employee() {}
// Relationship
#pragma db not_null
shared_ptr<employer> employer() const { return employer_; }
void employer(shared_ptr<employer> e) { employer_ = e; }
private:
friend class odb::access;
#pragma db id auto
unsigned long id_;
std::string first_name_;
std::string last_name_;
#pragma db not_null
shared_ptr<employer> employer_;
};
// Complex queries
void complex_queries(database& db)
{
transaction t(db.begin());
// JOIN operations
typedef odb::query<employee> query;
typedef odb::result<employee> result;
// Find employees of specific employer
result r(db.query<employee>(
query::employer->name == "ABC Corporation"));
// Conditional queries
std::string pattern = "John%";
result r2(db.query<employee>(
query::first_name.like(pattern) &&
query::employer->id == 1));
// Aggregation
typedef odb::query<person> pquery;
auto count = db.query_value<std::size_t>(
pquery::age >= 18 && pquery::age <= 65);
cout << "Working age people: " << count << endl;
t.commit();
}
// Using views
#pragma db view object(person)
struct person_stat
{
#pragma db column("count(" + person::id_ + ")")
std::size_t count;
#pragma db column("avg(" + person::age_ + ")")
double avg_age;
#pragma db column("max(" + person::age_ + ")")
unsigned short max_age;
};
void view_example(database& db)
{
transaction t(db.begin());
typedef odb::result<person_stat> result;
result r(db.query<person_stat>());
const person_stat& ps(*r.begin());
cout << "Total: " << ps.count << endl;
cout << "Average age: " << ps.avg_age << endl;
cout << "Max age: " << ps.max_age << endl;
t.commit();
}
Practical Example
// User management system example
#include <vector>
#include <algorithm>
class user_repository
{
private:
std::shared_ptr<database> db_;
public:
user_repository(std::shared_ptr<database> db) : db_(db) {}
// Search with pagination
struct page_result
{
std::vector<std::shared_ptr<person>> items;
std::size_t total_count;
std::size_t page;
std::size_t page_size;
};
page_result find_all(std::size_t page = 1, std::size_t page_size = 10)
{
transaction t(db_->begin());
// Get total count
std::size_t total = db_->query_value<std::size_t>(
odb::query<person>::true_expr);
// Pagination
typedef odb::query<person> query;
typedef odb::result<person> result;
result r(db_->query<person>(
query::true_expr +
" ORDER BY " + query::last_ + ", " + query::first_ +
" LIMIT " + std::to_string(page_size) +
" OFFSET " + std::to_string((page - 1) * page_size)));
std::vector<std::shared_ptr<person>> items;
for (auto& p : r)
{
items.push_back(std::make_shared<person>(p));
}
t.commit();
return {items, total, page, page_size};
}
// Search with conditions
std::vector<std::shared_ptr<person>> search(
const std::string& keyword,
unsigned short min_age = 0,
unsigned short max_age = 100)
{
transaction t(db_->begin());
typedef odb::query<person> query;
typedef odb::result<person> result;
query q(query::age >= min_age && query::age <= max_age);
if (!keyword.empty())
{
std::string pattern = "%" + keyword + "%";
q = q && (query::first_.like(pattern) ||
query::last_.like(pattern));
}
result r(db_->query<person>(q));
std::vector<std::shared_ptr<person>> results;
for (auto& p : r)
{
results.push_back(std::make_shared<person>(p));
}
t.commit();
return results;
}
// Batch operations
void batch_insert(const std::vector<person>& persons)
{
transaction t(db_->begin());
for (const auto& p : persons)
{
db_->persist(p);
}
t.commit();
}
// Transaction processing
bool transfer_ownership(unsigned long from_id, unsigned long to_id)
{
try
{
transaction t(db_->begin());
auto from = db_->load<person>(from_id);
auto to = db_->load<person>(to_id);
// Execute business logic
// ...
db_->update(*from);
db_->update(*to);
t.commit();
return true;
}
catch (const odb::object_not_persistent&)
{
return false;
}
}
};
// Usage example
int main()
{
auto db = std::make_shared<odb::mysql::database>(
"user", "password", "database");
user_repository repo(db);
// Paginated search
auto page = repo.find_all(1, 20);
cout << "Total users: " << page.total_count << endl;
// Keyword search
auto results = repo.search("John", 25, 40);
cout << "Found " << results.size() << " users" << endl;
return 0;
}