CppDB
CppDBは、C++向けの軽量なSQL接続性ライブラリで、JDBC、ODBCなどと同様にプラットフォームとデータベースに依存しない統一APIを提供します。CppCMSプロジェクトの一部として開発され、C++アプリケーションでのデータベース操作を簡素化します。MySQL、PostgreSQL、SQLite3、Oracleなどの主要データベースをサポートし、プリペアドステートメントによる高いセキュリティとパフォーマンスを実現している軽量なデータベースアクセスライブラリです。
GitHub概要
artyom-beilis/cppdb
スター6
ウォッチ4
フォーク5
作成日:2023年5月16日
言語:C++
ライセンス:Other
トピックス
なし
スター履歴
データ取得日時: 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;
}