ODB
ODBは、C++向けのクロスプラットフォーム・クロスデータベースORMライブラリです。C++オブジェクトをリレーショナルデータベースに永続化する際、手動のマッピングコードを必要とせず、テーブル、カラム、SQLを意識することなく開発が可能です。コンパイラベースのコード生成により、高性能かつ型安全なデータベースアクセスを実現します。
GitHub概要
codesynthesis-com/odb
Object-relational mapping (ORM) system for C++
スター28
ウォッチ3
フォーク4
作成日:2024年12月14日
言語:C++
ライセンス:Other
トピックス
なし
スター履歴
データ取得日時: 2025/7/19 02:41
ライブラリ
ODB
概要
ODBは、C++向けのクロスプラットフォーム・クロスデータベースORMライブラリです。C++オブジェクトをリレーショナルデータベースに永続化する際、手動のマッピングコードを必要とせず、テーブル、カラム、SQLを意識することなく開発が可能です。コンパイラベースのコード生成により、高性能かつ型安全なデータベースアクセスを実現します。
詳細
ODB 2025年版は、C++ ORM分野で最も成熟した選択肢として確立されています。PostgreSQL、MySQL、SQLite、Oracle、SQL Serverをサポートし、企業レベルのC++アプリケーション開発で安定した採用を維持しています。コンパイラベースのアプローチにより、実行時のオーバーヘッドを最小限に抑えながら、強力な型チェックとコード最適化を提供します。
主な特徴
- コンパイラベース: ODBコンパイラによる自動コード生成
- マルチデータベース: PostgreSQL、MySQL、SQLite、Oracle、SQL Server対応
- 高性能: 実行時オーバーヘッドを最小化
- 型安全: コンパイル時の強力な型チェック
- トランザクション: ACIDトランザクションの完全サポート
- クエリ言語: ネイティブC++構文によるクエリ記述
メリット・デメリット
メリット
- 成熟した安定性と企業での豊富な実績
- 複数の主要データベースへの対応
- コンパイル時の最適化による高パフォーマンス
- 既存のC++コードベースとの優れた統合性
- 包括的なドキュメントとサポート
- 商用ライセンスによるプロフェッショナルサポート
デメリット
- GPL/商用のデュアルライセンス(商用利用には有償)
- コード生成のためのビルドプロセスの複雑化
- 学習曲線が比較的急峻
- モダンC++機能への対応が保守的
- NoSQLデータベースのサポートなし
参考ページ
書き方の例
基本セットアップ
// person.hxx - エンティティ定義
#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)
{
}
// アクセサ
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_;
};
// ODBコンパイラでコード生成
// odb -d mysql --generate-query --generate-schema person.hxx
データベース操作
// main.cpp - 基本的なCRUD操作
#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
{
// データベース接続
unique_ptr<database> db(
new odb::mysql::database("user", "password", "database", "host"));
// CREATE - 新規作成
{
person john("John", "Doe", 33);
person jane("Jane", "Smith", 28);
transaction t(db->begin());
// オブジェクトの永続化
db->persist(john);
db->persist(jane);
t.commit();
cout << "Created person with id: " << john.id() << endl;
}
// READ - 読み取り
{
transaction t(db->begin());
// IDによる検索
unique_ptr<person> p(db->load<person>(1));
cout << "Loaded: " << p->first() << " " << p->last() << endl;
// クエリによる検索
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 - 更新
{
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 - 削除
{
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;
}
高度な機能
// リレーションシップとクエリ
#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() {}
// リレーションシップ
#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_;
};
// 複雑なクエリ
void complex_queries(database& db)
{
transaction t(db.begin());
// JOIN操作
typedef odb::query<employee> query;
typedef odb::result<employee> result;
// 特定の雇用主の従業員を検索
result r(db.query<employee>(
query::employer->name == "ABC Corporation"));
// 条件付きクエリ
std::string pattern = "John%";
result r2(db.query<employee>(
query::first_name.like(pattern) &&
query::employer->id == 1));
// 集計
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();
}
// ビューの使用
#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();
}
実用例
// ユーザー管理システムの例
#include <vector>
#include <algorithm>
class user_repository
{
private:
std::shared_ptr<database> db_;
public:
user_repository(std::shared_ptr<database> db) : db_(db) {}
// ページネーション付き検索
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());
// 総数を取得
std::size_t total = db_->query_value<std::size_t>(
odb::query<person>::true_expr);
// ページネーション
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};
}
// 検索条件付き検索
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;
}
// バッチ操作
void batch_insert(const std::vector<person>& persons)
{
transaction t(db_->begin());
for (const auto& p : persons)
{
db_->persist(p);
}
t.commit();
}
// トランザクション処理
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);
// ビジネスロジック実行
// ...
db_->update(*from);
db_->update(*to);
t.commit();
return true;
}
catch (const odb::object_not_persistent&)
{
return false;
}
}
};
// 使用例
int main()
{
auto db = std::make_shared<odb::mysql::database>(
"user", "password", "database");
user_repository repo(db);
// ページネーション検索
auto page = repo.find_all(1, 20);
cout << "Total users: " << page.total_count << endl;
// キーワード検索
auto results = repo.search("John", 25, 40);
cout << "Found " << results.size() << " users" << endl;
return 0;
}