CppDB

CppDBは、C++向けの軽量なSQL接続性ライブラリで、JDBC、ODBCなどと同様にプラットフォームとデータベースに依存しない統一APIを提供します。CppCMSプロジェクトの一部として開発され、C++アプリケーションでのデータベース操作を簡素化します。MySQL、PostgreSQL、SQLite3、Oracleなどの主要データベースをサポートし、プリペアドステートメントによる高いセキュリティとパフォーマンスを実現している軽量なデータベースアクセスライブラリです。

ORMC++データベース軽量SQLConnectivityクロスプラットフォーム

GitHub概要

artyom-beilis/cppdb

スター6
ウォッチ4
フォーク5
作成日:2023年5月16日
言語:C++
ライセンス:Other

トピックス

なし

スター履歴

artyom-beilis/cppdb Star History
データ取得日時: 2025/7/19 08:07

ライブラリ

CppDB

概要

CppDBは、C++向けの軽量なSQL接続性ライブラリで、JDBC、ODBCなどと同様にプラットフォームとデータベースに依存しない統一APIを提供します。CppCMSプロジェクトの一部として開発され、C++アプリケーションでのデータベース操作を簡素化します。MySQL、PostgreSQL、SQLite3、Oracleなどの主要データベースをサポートし、プリペアドステートメントによる高いセキュリティとパフォーマンスを実現している軽量なデータベースアクセスライブラリです。

詳細

CppDBは完全なORMフレームワークというよりも、生のSQLクエリとC++オブジェクト間の軽量なブリッジとして設計されています。複雑なオブジェクトマッピングよりも、シンプルで直接的なデータベースアクセスを重視し、開発者がSQLの制御を保持しながら型安全性とメモリ安全性を享受できます。プリペアドステートメントのサポートにより、SQLインジェクション攻撃を防ぎ、パフォーマンスを向上させます。接続プーリング、トランザクション管理、例外処理など、実用的なデータベースアクセスに必要な機能を包括的に提供します。

主な特徴

  • 軽量設計: 最小限の依存関係で高速なデータベースアクセスを実現
  • クロスプラットフォーム: Windows、Linux、macOSでの動作をサポート
  • マルチデータベース対応: MySQL、PostgreSQL、SQLite3、Oracleに対応
  • プリペアドステートメント: SQLインジェクション防止と性能向上
  • 接続プーリング: 効率的なリソース管理とスケーラビリティ
  • 型安全: C++の型システムを活用したタイプセーフなデータアクセス

メリット・デメリット

メリット

  • シンプルで学習コストが低く、C++開発者にとって直感的
  • 軽量でオーバーヘッドが少なく、組み込み用途にも適している
  • 生のSQLを直接記述でき、高度なクエリ最適化が可能
  • プリペアドステートメントによる高いセキュリティレベル
  • 複数データベースエンジンのサポートにより移植性が高い
  • CppCMSとの統合により、Webアプリケーション開発で威力を発揮

デメリット

  • 完全なORMではないため、複雑な関連性マッピングは手動実装が必要
  • 自動スキーマ生成やマイグレーション機能が限定的
  • 大規模なエンタープライズアプリケーションには機能不足の可能性
  • ドキュメントや学習リソースが他のC++ORMと比較して少ない
  • アクティブラッパーパターンなどの高レベル抽象化は提供されない
  • コミュニティが小さく、サードパーティツールが限定的

参考ページ

書き方の例

基本セットアップ

#include <cppdb/frontend.h>
#include <iostream>

int main() {
    try {
        // データベース接続の確立
        cppdb::session sql("mysql:host=localhost;database=test;user=root;password=password");
        
        // 接続の確認
        std::cout << "データベースに接続成功" << std::endl;
        
    } catch(std::exception const &e) {
        std::cerr << "エラー: " << e.what() << std::endl;
        return 1;
    }
    return 0;
}

モデル定義と基本操作

#include <cppdb/frontend.h>
#include <string>

struct User {
    int id;
    std::string name;
    std::string email;
    int age;
};

class UserRepository {
private:
    cppdb::session sql;
    
public:
    UserRepository(const std::string& connection_string) 
        : sql(connection_string) {}
    
    // ユーザー作成
    bool createUser(const User& user) {
        try {
            cppdb::statement st = sql << 
                "INSERT INTO users (name, email, age) VALUES (?, ?, ?)"
                << user.name << user.email << user.age;
            st.exec();
            return true;
        } catch(std::exception const &e) {
            std::cerr << "ユーザー作成エラー: " << e.what() << std::endl;
            return false;
        }
    }
    
    // ユーザー取得
    bool getUser(int id, User& user) {
        try {
            cppdb::result res = sql << 
                "SELECT id, name, email, age FROM users WHERE id = ?" << id;
            
            if(res.next()) {
                res >> user.id >> user.name >> user.email >> user.age;
                return true;
            }
            return false;
        } catch(std::exception const &e) {
            std::cerr << "ユーザー取得エラー: " << e.what() << std::endl;
            return false;
        }
    }
    
    // ユーザー更新
    bool updateUser(const User& user) {
        try {
            cppdb::statement st = sql << 
                "UPDATE users SET name = ?, email = ?, age = ? WHERE id = ?"
                << user.name << user.email << user.age << user.id;
            st.exec();
            return st.affected() > 0;
        } catch(std::exception const &e) {
            std::cerr << "ユーザー更新エラー: " << e.what() << std::endl;
            return false;
        }
    }
    
    // ユーザー削除
    bool deleteUser(int id) {
        try {
            cppdb::statement st = sql << "DELETE FROM users WHERE id = ?" << id;
            st.exec();
            return st.affected() > 0;
        } catch(std::exception const &e) {
            std::cerr << "ユーザー削除エラー: " << e.what() << std::endl;
            return false;
        }
    }
};

高度なクエリ操作

#include <cppdb/frontend.h>
#include <vector>

class AdvancedUserOperations {
private:
    cppdb::session sql;
    
public:
    AdvancedUserOperations(const std::string& connection_string) 
        : sql(connection_string) {}
    
    // 複数ユーザー取得(条件付き)
    std::vector<User> getUsersByAge(int min_age, int max_age) {
        std::vector<User> users;
        try {
            cppdb::result res = sql << 
                "SELECT id, name, email, age FROM users WHERE age BETWEEN ? AND ? ORDER BY age"
                << min_age << max_age;
            
            while(res.next()) {
                User user;
                res >> user.id >> user.name >> user.email >> user.age;
                users.push_back(user);
            }
        } catch(std::exception const &e) {
            std::cerr << "ユーザー検索エラー: " << e.what() << std::endl;
        }
        return users;
    }
    
    // ページネーション付きクエリ
    std::vector<User> getUsersWithPagination(int page, int limit) {
        std::vector<User> users;
        int offset = (page - 1) * limit;
        
        try {
            cppdb::result res = sql << 
                "SELECT id, name, email, age FROM users ORDER BY id LIMIT ? OFFSET ?"
                << limit << offset;
            
            while(res.next()) {
                User user;
                res >> user.id >> user.name >> user.email >> user.age;
                users.push_back(user);
            }
        } catch(std::exception const &e) {
            std::cerr << "ページネーションエラー: " << e.what() << std::endl;
        }
        return users;
    }
    
    // 集約クエリ
    struct UserStatistics {
        int total_users;
        double average_age;
        int min_age;
        int max_age;
    };
    
    UserStatistics getUserStatistics() {
        UserStatistics stats = {0, 0.0, 0, 0};
        try {
            cppdb::result res = sql << 
                "SELECT COUNT(*), AVG(age), MIN(age), MAX(age) FROM users";
            
            if(res.next()) {
                res >> stats.total_users >> stats.average_age >> stats.min_age >> stats.max_age;
            }
        } catch(std::exception const &e) {
            std::cerr << "統計取得エラー: " << e.what() << std::endl;
        }
        return stats;
    }
};

リレーション操作

#include <cppdb/frontend.h>

struct Post {
    int id;
    std::string title;
    std::string content;
    int user_id;
    std::string created_at;
};

struct UserWithPosts {
    User user;
    std::vector<Post> posts;
};

class RelationOperations {
private:
    cppdb::session sql;
    
public:
    RelationOperations(const std::string& connection_string) 
        : sql(connection_string) {}
    
    // ユーザーと投稿を結合して取得
    std::vector<UserWithPosts> getUsersWithPosts() {
        std::map<int, UserWithPosts> user_map;
        
        try {
            cppdb::result res = sql << 
                "SELECT u.id, u.name, u.email, u.age, "
                "       p.id, p.title, p.content, p.created_at "
                "FROM users u "
                "LEFT JOIN posts p ON u.id = p.user_id "
                "ORDER BY u.id, p.id";
            
            while(res.next()) {
                int user_id;
                User user;
                res >> user.id >> user.name >> user.email >> user.age;
                
                // ユーザーが初回の場合、マップに追加
                if(user_map.find(user.id) == user_map.end()) {
                    UserWithPosts uwp;
                    uwp.user = user;
                    user_map[user.id] = uwp;
                }
                
                // 投稿が存在する場合、追加
                Post post;
                if(!res.is_null(4)) { // post.id が null でない場合
                    res >> post.id >> post.title >> post.content >> post.created_at;
                    post.user_id = user.id;
                    user_map[user.id].posts.push_back(post);
                }
            }
        } catch(std::exception const &e) {
            std::cerr << "関連データ取得エラー: " << e.what() << std::endl;
        }
        
        // マップから配列に変換
        std::vector<UserWithPosts> result;
        for(auto& pair : user_map) {
            result.push_back(pair.second);
        }
        return result;
    }
    
    // 特定ユーザーの投稿を取得
    std::vector<Post> getPostsByUserId(int user_id) {
        std::vector<Post> posts;
        try {
            cppdb::result res = sql << 
                "SELECT id, title, content, user_id, created_at FROM posts WHERE user_id = ? ORDER BY created_at DESC"
                << user_id;
            
            while(res.next()) {
                Post post;
                res >> post.id >> post.title >> post.content >> post.user_id >> post.created_at;
                posts.push_back(post);
            }
        } catch(std::exception const &e) {
            std::cerr << "投稿取得エラー: " << e.what() << std::endl;
        }
        return posts;
    }
};

実用例

#include <cppdb/frontend.h>
#include <cppdb/pool.h>
#include <thread>
#include <memory>

class DatabaseManager {
private:
    std::unique_ptr<cppdb::pool> db_pool;
    
public:
    DatabaseManager(const std::string& connection_string, int pool_size = 10) {
        // 接続プールの設定
        db_pool = std::make_unique<cppdb::pool>(connection_string, pool_size);
    }
    
    // トランザクション付きの複雑な操作
    bool transferUserData(int from_user_id, int to_user_id, const std::string& data_type) {
        try {
            cppdb::session sql = db_pool->open();
            
            // トランザクション開始
            cppdb::transaction tr(sql);
            
            // データ移行の実行
            cppdb::statement move_data = sql << 
                "UPDATE user_data SET user_id = ? WHERE user_id = ? AND data_type = ?"
                << to_user_id << from_user_id << data_type;
            move_data.exec();
            
            // ログの記録
            cppdb::statement log_entry = sql << 
                "INSERT INTO data_transfer_log (from_user, to_user, data_type, transfer_time) "
                "VALUES (?, ?, ?, NOW())"
                << from_user_id << to_user_id << data_type;
            log_entry.exec();
            
            // トランザクションのコミット
            tr.commit();
            return true;
            
        } catch(std::exception const &e) {
            std::cerr << "データ移行エラー: " << e.what() << std::endl;
            return false;
        }
    }
    
    // バッチ処理の例
    bool batchInsertUsers(const std::vector<User>& users) {
        try {
            cppdb::session sql = db_pool->open();
            cppdb::transaction tr(sql);
            
            cppdb::statement st = sql << 
                "INSERT INTO users (name, email, age) VALUES (?, ?, ?)";
            
            for(const auto& user : users) {
                st.reset();
                st << user.name << user.email << user.age;
                st.exec();
            }
            
            tr.commit();
            return true;
            
        } catch(std::exception const &e) {
            std::cerr << "バッチ挿入エラー: " << e.what() << std::endl;
            return false;
        }
    }
    
    // 非同期処理のサポート(マルチスレッド環境)
    void processUsersAsync(const std::function<void(const User&)>& processor) {
        std::vector<std::thread> workers;
        
        try {
            cppdb::session sql = db_pool->open();
            cppdb::result res = sql << "SELECT id, name, email, age FROM users";
            
            while(res.next()) {
                User user;
                res >> user.id >> user.name >> user.email >> user.age;
                
                // 非同期でユーザー処理を実行
                workers.emplace_back([user, processor]() {
                    processor(user);
                });
            }
            
            // 全スレッドの完了を待機
            for(auto& worker : workers) {
                worker.join();
            }
            
        } catch(std::exception const &e) {
            std::cerr << "非同期処理エラー: " << e.what() << std::endl;
            
            // 残りのスレッドを待機
            for(auto& worker : workers) {
                if(worker.joinable()) {
                    worker.join();
                }
            }
        }
    }
};

// 使用例
int main() {
    try {
        DatabaseManager db_manager("mysql:host=localhost;database=test;user=root;password=password");
        
        // ユーザーデータの移行
        bool success = db_manager.transferUserData(1, 2, "profile_data");
        if(success) {
            std::cout << "データ移行が完了しました" << std::endl;
        }
        
        // バッチ処理
        std::vector<User> new_users = {
            {0, "田中太郎", "[email protected]", 30},
            {0, "佐藤花子", "[email protected]", 25},
            {0, "鈴木一郎", "[email protected]", 35}
        };
        
        if(db_manager.batchInsertUsers(new_users)) {
            std::cout << "バッチ挿入が完了しました" << std::endl;
        }
        
        // 非同期処理
        db_manager.processUsersAsync([](const User& user) {
            std::cout << "Processing user: " << user.name << std::endl;
            // 何らかの時間のかかる処理...
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        });
        
    } catch(std::exception const &e) {
        std::cerr << "アプリケーションエラー: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}