ODB

ODBは、C++向けのクロスプラットフォーム・クロスデータベースORMライブラリです。C++オブジェクトをリレーショナルデータベースに永続化する際、手動のマッピングコードを必要とせず、テーブル、カラム、SQLを意識することなく開発が可能です。コンパイラベースのコード生成により、高性能かつ型安全なデータベースアクセスを実現します。

ORMC++クロスプラットフォームエンタープライズPostgreSQLMySQLSQLite

GitHub概要

codesynthesis-com/odb

Object-relational mapping (ORM) system for C++

スター28
ウォッチ3
フォーク4
作成日:2024年12月14日
言語:C++
ライセンス:Other

トピックス

なし

スター履歴

codesynthesis-com/odb Star History
データ取得日時: 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;
}