# mydatabase **Repository Path**: blueLotu/mydatabase ## Basic Information - **Project Name**: mydatabase - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-15 - **Last Updated**: 2026-07-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # database — 数据库公共接口模块 统一的、可扩展的 C++ 数据库访问中间件,基于 Qt 5.15.2 + QxOrm 构建。 ## 特性 - **跨平台**: Windows / Linux,**x86_64 + ARM64 (aarch64)**,适配银河麒麟桌面/服务器 - **多数据库**: SQLite 内置支持,达梦/金仓/神威 (ODBC) - **统一门面**: `Database::open()` 一行打开,`->` 操作符直通底层接口 - **QxOrm ORM**: `QX_REGISTER` 编译期注册,`qx::dao` CRUD,`QxSqlQuery` 条件查询 - **批量操作**: `insertMany` / `updateMany` 事务内批量写入 - **DDL 迁移**: Flyway 风格版本化脚本,支持 XML 描述 + SQL 脚本双模式,外部目录热加载 - **连接池**: 进程全局池,QMutex 线程安全,空闲回收,超时控制 - **自动维护**: 定时清理 (`DbMaintenanceScheduler`) + 容量守护 (`CapacityGuard`) - **离线工具**: `db_tool` CLI 导入导出 (JSON/CSV),分片导入导出 - **分片**: Hash / Range 分片策略 + `ShardingRouter` 路由 - **编码**: 端到端 UTF-8 --- ## 目录 - [快速开始](#快速开始) - [核心概念](#核心概念) - [Database 门面](#1-database-门面推荐入口) - [DbConfig 数据库配置](#2-dbconfig-数据库配置) - [DbError 与 Result\](#3-dberror-与-resultt-错误处理) - [事务](#4-事务) - [ORM 实体与 CRUD](#5-orm-实体与-crud) - [连接池](#6-连接池) - [DDL 迁移](#7-ddl-迁移) - [自动维护](#8-自动维护) - [备份与恢复](#9-备份与恢复) - [CLI 工具](#cli-工具-db_tool) - [导出](#导出) - [导入](#导入) - [分片导入导出](#分片导入导出) - [迁移](#迁移) - [分库分表](#分库分表) - [构建](#构建) - [新数据库适配](#新数据库适配) - [项目结构](#项目结构) - [测试与覆盖率](#测试与覆盖率) --- ## 快速开始 ### 30 秒示例 ```cpp #include "database.h" using namespace db; // 1. 打开数据库 auto db = Database::open(DbConfig::sqlite("app.db")).takeValue(); // 2. 执行 DDL db->executeDdl("CREATE TABLE IF NOT EXISTS t_user (" " id INTEGER PRIMARY KEY AUTOINCREMENT," " name TEXT NOT NULL, email TEXT UNIQUE)"); // 3. 插入数据 db->executeNonQuery( "INSERT INTO t_user (name, email) VALUES (?,?)", {"张三", "zhangsan@example.com"}); // 4. 查询 auto result = db->executeQuery("SELECT * FROM t_user WHERE name=?", {"张三"}); if (result.isOk() && result.value().next()) { qDebug() << result.value().value("email").toString(); } // 5. 事务 db->transaction([&]() { db->executeNonQuery("INSERT INTO t_user (name,email) VALUES (?,?)", {"李四", "lisi@example.com"}); db->executeNonQuery("INSERT INTO t_user (name,email) VALUES (?,?)", {"王五", "wangwu@example.com"}); }); // 自动 commit,异常自动 rollback ``` --- ## 核心概念 ### 1. Database 门面(推荐入口) `Database` 是用户唯一入口,封装了 `DbConfig` + `DbFactory` + `IDatabase`,一行代码完成所有初始化。 ```cpp #include "database.h" using namespace db; // ── 打开数据库 ── auto result = Database::open(DbConfig::sqlite("mydata.db")); if (result.isErr()) { qCritical() << "Open failed:" << result.error().message; return; } auto db = result.takeValue(); // std::unique_ptr // ── 访问底层接口 ── IDatabase& raw = db->db(); // 显式获取 IDatabase& raw2 = *db; // operator* 隐式转换 db->executeDdl("CREATE TABLE ..."); // operator-> 直通 IDatabase // ── Lambda 事务 ── db->transaction([&]() { // 你的业务逻辑 // 正常返回 → 自动 COMMIT // 抛出异常 → 自动 ROLLBACK }); // ── 自动清理 ── CleanupPolicy policy; policy.olderThanDays = 90; policy.tables = QStringList() << "t_log" << "t_metric"; db->enableAutoCleanup(policy, 3, 0); // 每天凌晨 3:00 清理 // ── 关闭 ── db->close(); // 停止维护调度器 + 关闭数据库连接 ``` ### 2. DbConfig 数据库配置 ```cpp // SQLite (最常用) auto cfg = DbConfig::sqlite("app.db"); cfg.poolMaxSize = 4; // 连接池大小 cfg.slowQueryThresholdMs = 200; // 慢查询阈值 (ms) // 达梦 (ODBC) auto cfg2 = DbConfig::dameng("192.168.1.100", 5236, "MYDB", "SYSDBA", "SYSDBA"); cfg2.encoding = "UTF-8"; // 金仓 / 神威 (ODBC) DbConfig cfg3; cfg3.type = DbType::Kingbase; cfg3.host = "10.0.0.1"; cfg3.port = 54321; cfg3.databaseName = "mydb"; cfg3.userName = "admin"; cfg3.password = "secret"; // 连接池调优 cfg.poolMinIdle = 2; // 最小空闲连接数 cfg.poolIdleTimeoutMs = 60000; // 空闲超时回收 cfg.poolAcquireTimeoutMs = 30000; // 获取连接超时 // 重试 cfg.retryMaxAttempts = 3; cfg.retryBackoffMs = 1000; // 监控 cfg.slowQueryThresholdMs = 500; // 超过此阈值记录慢查询日志 ``` ### 3. DbError 与 Result\(错误处理) 所有可能失败的操作都返回 `Result`,类似 Rust 的错误模式。 ```cpp // ── 检查结果 ── auto result = db->executeQuery("SELECT * FROM t_user", {}); if (result.isErr()) { const DbError& err = result.error(); qWarning() << "Code:" << err.code << "Message:" << err.message << "SQL:" << err.sql << "Elapsed:" << err.elapsedMs << "ms"; return; } // ── 使用值 ── QSqlQuery& query = result.value(); while (query.next()) { /* ... */ } // ── 移动语义 ── auto db = result.takeValue(); // 转移所有权 // ── 兜底值 ── int count = result.unwrapOr(0); // ── Result 用于无返回值操作 ── auto ddlResult = db->executeDdl("CREATE TABLE ..."); if (ddlResult.isOk()) { /* 成功 */ } // ── 自定义错误码 ── DbError err; err.code = DbError::ConnectionFailed; err.message = "Custom error message"; return Result::err(err); ``` ### 4. 事务 ```cpp // ── 手动事务 ── db->beginTransaction(); db->executeNonQuery("INSERT INTO t_user (name) VALUES (?)", {"张三"}); db->executeNonQuery("INSERT INTO t_user (name) VALUES (?)", {"李四"}); db->commitTransaction(); // 或 db->rollbackTransaction(); // ── Lambda 自动事务 (推荐) ── auto txResult = db->transaction([&]() { db->executeNonQuery("INSERT INTO t_user (name) VALUES (?)", {"王五"}); db->executeNonQuery("UPDATE t_user SET status=1 WHERE status=0", {}); }); if (txResult.isOk()) { qDebug() << "Transaction committed"; } else { qWarning() << "Transaction rolled back:" << txResult.error().message; } // ── 事务中抛异常自动回滚 ── db->transaction([&]() { db->executeNonQuery("INSERT INTO t_user (name) VALUES (?)", {"测试"}); if (someCondition) throw std::runtime_error("业务规则不满足"); // 异常被捕获,事务自动回滚 }); ``` ### 5. ORM 实体与 CRUD 使用 QxOrm 定义实体(完整注册宏),替代传统的 Q_GADGET 方式。 ```cpp #include "database.h" #include using namespace db; // ── 1. 定义实体 ── class Device { public: long m_id = 0; QString m_name; QString m_serialNumber; int m_status = 1; QDateTime m_createdAt; QDateTime m_updatedAt; }; QX_REGISTER_HPP(Device, qx::trait::no_base_class_defined, 0) // 在 .cpp 中: QX_REGISTER_CPP(Device) namespace qx { template<> void register_class(QxClass& t) { t.setName("t_device"); t.id(&Device::m_id, "id"); t.data(&Device::m_name, "name"); t.data(&Device::m_serialNumber, "serialNumber"); t.data(&Device::m_status, "status"); t.data(&Device::m_createdAt, "createdAt"); t.data(&Device::m_updatedAt, "updatedAt"); } } // ── 2. CRUD ── Device dev; dev.m_name = "传感器-01"; dev.m_serialNumber = "SN-001"; dev.m_createdAt = QDateTime::currentDateTime(); dev.m_updatedAt = QDateTime::currentDateTime(); // INSERT QSqlError err = qx::dao::insert(dev, &db->db().nativeConnection()); // dev.m_id 自动填充 // SELECT BY PK Device found; found.m_id = dev.m_id; qx::dao::fetch_by_id(found, &db->db().nativeConnection()); // UPDATE found.m_name = "传感器-01-更新"; qx::dao::update(found, &db->db().nativeConnection()); // DELETE BY PK qx::dao::delete_by_id(found, &db->db().nativeConnection()); // ── 3. 批量操作 ── QVector batch; for (int i = 0; i < 100; i++) { Device d; d.m_name = QString("batch-%1").arg(i); d.m_serialNumber = QString("SN-B-%1").arg(i); d.m_createdAt = now; d.m_updatedAt = now; batch.append(d); } qx::dao::insert(batch, &db->db().nativeConnection()); // 批量 INSERT qx::dao::update(batch, &db->db().nativeConnection()); // 批量 UPDATE // ── 4. 条件查询 ── qx::QxSqlQuery query("WHERE status = :status AND name LIKE :name"); query.bind(":status", 1).bind(":name", "%传感器%"); QVector list; qx::dao::fetch_by_query(query, list, &db->db().nativeConnection()); long count = 0; qx::dao::count(count, query, &db->db().nativeConnection()); // ── 5. 条件删除 ── qx::QxSqlQuery delQuery("WHERE status = :s"); delQuery.bind(":s", 0); qx::dao::delete_by_query(delQuery, &db->db().nativeConnection()); // ── 6. 获取所有记录 ── QVector all; qx::dao::fetch_all(all, &db->db().nativeConnection()); // ── 7. 中文支持 ── Device cnDev; cnDev.m_name = QString::fromUtf8("中文测试设备"); cnDev.m_serialNumber = QString::fromUtf8("CN-中文-001"); qx::dao::insert(cnDev, &db->db().nativeConnection()); ``` ### 6. 连接池 进程级连接池,线程安全,空闲回收。 ```cpp // ── 配置连接池 ── auto cfg = DbConfig::sqlite("app.db"); cfg.poolMaxSize = 10; // 最大连接数 cfg.poolMinIdle = 2; // 最小空闲连接 cfg.poolIdleTimeoutMs = 60000; // 空闲 60s 后回收 cfg.poolAcquireTimeoutMs = 5000;// 获取连接超时 // ── 直接使用连接池 ── DbConnectionPool pool(cfg); auto conn = pool.acquire(); if (conn.isOk()) { QSqlDatabase db = conn.value(); // 使用连接... pool.release(db); } // ── 监控 ── qDebug() << "Active:" << pool.activeCount() << "Idle:" << pool.idleCount() << "Total:" << pool.totalCount() << "Max:" << pool.maxPoolSize(); // ── 自定义同步机制 ── // 默认使用 ThreadPoolSync (QMutex + QWaitCondition) // 可替换为 CoroutinePoolSync 以支持协程环境 CoroutinePoolSync sync; pool.setSync(&sync); ``` ### 7. DDL 迁移 Flyway 风格版本化迁移,支持 SQL 脚本和 XML 描述两种格式。 #### 脚本命名规范 ``` resources/migrations/sqlite/ ├── V001__initial_schema.sql # 正向迁移 ├── V001__initial_schema.undo.sql # 回滚脚本 (可选) ├── V002__add_email_column.xml # XML 格式 └── V003__create_index.sql ``` #### SQL 脚本模式 ```sql -- V001__initial_schema.sql CREATE TABLE IF NOT EXISTS t_device ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, serialNumber TEXT, status INTEGER DEFAULT 1, createdAt TEXT, updatedAt TEXT ); CREATE INDEX IF NOT EXISTS idx_device_name ON t_device(name); ``` #### XML 描述模式 ```xml ``` XML 支持的操作: ``, ``, ``, ``, ``, ``, ``, `` #### 代码中执行迁移 ```cpp #include "database.h" #include "ddl/migration_runner.h" using namespace db; auto dbResult = Database::open(DbConfig::sqlite("app.db")); auto& db = dbResult.takeValue()->db(); MigrationRunner runner(db); // 1. 从 Qt 资源加载 (随程序发布) runner.loadFromResource(":/migrations/sqlite"); // 2. 从外部目录加载 (运维热加载,免编译) runner.loadFromDirectory("/opt/myapp/migrations/sqlite"); // 3. 预览待执行的迁移 auto previews = runner.preview(); for (const auto& p : previews.value()) { qDebug() << "Pending:" << p.version << p.description << "Risk:" << (int)p.risk; } // 4. 验证已应用迁移的校验和 auto checksums = runner.validateChecksums(); for (const auto& c : checksums.value()) { if (!c.match) { qWarning() << "Checksum mismatch:" << c.version; } } // 5. 执行迁移 (带安全选项) MigrationOptions opts; opts.allowDestructive = true; // 允许 DROP TABLE 等危险操作 opts.requireUndo = true; // 必须有 .undo.sql 才执行 opts.backupDir = "/backups"; // 执行前备份 opts.dryRun = false; // false = 真实执行 auto result = runner.migrate(opts); // 6. 回滚到指定版本 runner.rollback("V001"); // 7. 查看迁移状态 auto status = runner.status(); for (const auto& m : status.value()) { qDebug() << m.version << (m.appliedAt.isValid() ? "APPLIED" : "PENDING"); } // 8. 查询已应用版本列表 QStringList versions = runner.appliedVersions(); ``` #### 安全机制 | 特性 | 说明 | |------|------| | **风险分析** | 每条 SQL 自动分析风险等级: Safe / Warning / Dangerous / Unknown | | **危险操作拦截** | DROP TABLE / TRUNCATE / ALTER TABLE DROP 默认阻止,需显式 `allowDestructive=true` | | **并发锁** | SQLite 使用文件锁,ODBC 使用 `migration_lock` 表锁,防止并发执行 | | **校验和** | SHA256 校验已应用的迁移,防止脚本被篡改 | | **自动备份** | 执行 Warning/Dangerous 级别迁移前自动备份数据库 | | **事务保护** | ODBC 模式下每条迁移在事务内执行,失败自动回滚 | | **dryRun** | 预览模式不执行任何操作 | ### 8. 自动维护 #### DbCleaner — 清理引擎 ```cpp DbCleaner cleaner(db); CleanupPolicy policy; policy.tables = QStringList() << "t_log" << "t_metric"; policy.excludeTables = QStringList() << "t_metric_archive"; policy.olderThanDays = 90; // 清理 90 天前的数据 policy.timeColumn = "created_at"; // 时间列名 policy.batchSize = 10000; // 分批删除,每批 10000 条 policy.maxRowsPerTable = 1000000; // 保留最新 100 万行 // 预估算 auto estimate = cleaner.estimate(policy); qDebug() << "Will delete ~" << estimate.value().deletedRows << "rows"; // 预览 (dryRun=true) policy.dryRun = true; auto preview = cleaner.execute(policy); // 真实执行 policy.dryRun = false; auto report = cleaner.execute(policy); qDebug() << "Deleted:" << report.value().deletedRows << "rows across" << report.value().totalTables << "tables" << "in" << report.value().elapsedMs << "ms"; ``` #### DbMaintenanceScheduler — 定时调度 ```cpp auto* sched = new DbMaintenanceScheduler(db); // 按固定间隔 (每 60 分钟) sched->schedule(policy, 60); // 按每天固定时间 (凌晨 3:00) sched->scheduleDaily(policy, 3, 0); // 支持多个策略 CleanupPolicy policy2; policy2.tables = QStringList() << "t_cache"; policy2.maxRowsPerTable = 5000; sched->schedule(policy2, 30); // 每 30 分钟 // 连接信号 QObject::connect(sched, &DbMaintenanceScheduler::cleanupCompleted, [](const CleanupReport& r) { qDebug() << "Cleanup done:" << r.deletedRows << "rows"; }); QObject::connect(sched, &DbMaintenanceScheduler::cleanupFailed, [](const DbError& e) { qWarning() << "Cleanup failed:" << e.message; }); // 启动/停止 sched->start(); sched->stop(); // 立即执行一次 auto result = sched->runNow(); // 查询上次报告 if (sched->lastReport().has_value()) { auto& report = sched->lastReport().value(); } ``` #### CapacityGuard — 容量守护 ```cpp auto* guard = new CapacityGuard(db); // 监控单表: 超过 500 万行自动裁剪 guard->watch("t_log", 5000000); // 监控所有表 guard->watchAll(10000000); // 每表最多 1000 万行 // 设置检查间隔 (每 200 次写入检查一次) guard->setCheckInterval(200); // 启用/停用 guard->setEnabled(true); // 连接信号 QObject::connect(guard, &CapacityGuard::capacityExceeded, [](const QString& table, qint64 current, qint64 max) { qWarning() << table << "exceeded:" << current << ">" << max; }); // 通知写入 (每次写入后调用) guard->notifyWrite(); ``` #### Database 门面一键启用 ```cpp auto db = Database::open(cfg).takeValue(); db->enableAutoCleanup(policy, 3, 0); // 自动开始定时清理 db->maintenance()->runNow(); // 立即执行一次 ``` ### 9. 备份与恢复 ```cpp // ── 备份到文件 ── auto backupResult = db->backupToFile("/backups"); if (backupResult.isOk()) { QString backupPath = backupResult.value(); // backupPath 格式: /backups/app_backup_20250716_143000.db } // ── :memory: 数据库不支持备份 ── auto memResult = memDb->backupToFile("/backups"); // → 返回错误: "Cannot backup in-memory database" ``` --- ## CLI 工具 (db_tool) `db_tool` 是集成导入/导出/迁移的离线命令行工具。 ``` 用法: db_tool [options] 命令: export 导出数据库表为 JSON/CSV import 从 JSON/CSV 文件导入数据库 migrate 执行 DDL 迁移 status 查看迁移状态 ``` ### 导出 ```bash # 导出所有表为 JSON db_tool export --source test.db --format json --output /backups/ # 导出指定表为 CSV db_tool export --source test.db --format csv --tables t_device,t_sensor --output /backups/ # 条件过滤导出 db_tool export --source test.db --mode filtered --filter "status=1" --output /backups/ # 排除某些表 db_tool export --source test.db --exclude t_log --output /backups/ ``` ### 导入 ```bash # 全量导入 (先清空后导入) db_tool import --target target.db --input /backups/ --mode full # 增量导入 (不清空) db_tool import --target target.db --input /backups/ --mode incremental # 忽略错误继续 db_tool import --target target.db --input /backups/ --ignore-errors ``` ### 分片导入导出 ```bash # 从多个分片导出数据 db_tool shard-export --shards shard_0.db,shard_1.db \ --names s0,s1 --format json --output /shard_backups/ # 导入到多个分片 db_tool shard-import --shards new_0.db,new_1.db \ --names s0,s1 --input /shard_backups/ ``` ### 迁移 ```bash # 预览待执行的迁移 db_tool migrate --db app.db --preview # 执行迁移 db_tool migrate --db app.db --allow-destructive --backup /backups/ # 查看状态 db_tool migrate --db app.db --status # 回滚到指定版本 db_tool migrate --db app.db --rollback V001 # 验证校验和 db_tool migrate --db app.db --validate ``` ### 编程接口 ```cpp #include "db_export.h" #include "db_import.h" #include "db_shard_tool.h" // ── 导出 ── ExportConfig cfg; cfg.sourceDb = DbConfig::sqlite("source.db"); cfg.format = ExportConfig::JSON; cfg.outputDir = "/backups"; cfg.tables = QStringList() << "t_device"; cfg.batchSize = 1000; DbExporter exporter(cfg); exporter.setProgressCallback([](const ExportProgress& p) { qDebug() << "Exporting" << p.currentTable << p.exportedRows << "/" << p.percent << "%"; }); auto report = exporter.execute().value(); qDebug() << "Exported" << report.totalRows << "rows from" << report.totalTables << "tables"; // ── 导入 ── ImportConfig impCfg; impCfg.targetDb = DbConfig::sqlite("target.db"); impCfg.inputDir = "/backups"; impCfg.mode = ImportConfig::Full; impCfg.truncateBefore = true; impCfg.ignoreErrors = true; DbImporter importer(impCfg); importer.setProgressCallback([](const ImportProgress& p) { qDebug() << "Importing" << p.currentFile << p.importedRows; }); auto impReport = importer.execute().value(); qDebug() << "Imported" << impReport.totalRows << "rows"; // ── 分片导出 ── ShardExportConfig sCfg; sCfg.shardDatabases = QVector{ DbConfig::sqlite("shard_0.db"), DbConfig::sqlite("shard_1.db") }; sCfg.shardNames = QStringList() << "s0" << "s1"; sCfg.format = ExportConfig::JSON; sCfg.outputDir = "/shard_backups"; sCfg.tables = QStringList() << "t_device"; DbShardExporter shardExporter(sCfg); auto sReport = shardExporter.execute().value(); qDebug() << sReport.totalShards << "shards," << sReport.totalRows << "rows"; for (const auto& detail : sReport.shardDetails) { qDebug() << " " << detail.shardName << ":" << detail.rows << "rows"; } ``` --- ## 分库分表 ### Hash 分片 ```cpp // 4 个分片,基于 device_id 哈希 HashShardStrategy hashStrategy(4, "t_data"); // 路由到分片 auto target = hashStrategy.route(QVariant(deviceId)); qDebug() << "Shard:" << target.value().shardIndex << "Table:" << target.value().tableName("t_data"); // → tableName 返回 "t_data_0", "t_data_1", ... // 获取所有分片 (广播查询) auto all = hashStrategy.allTargets(); for (const auto& t : all) { qDebug() << "Querying shard" << t.shardIndex; // 在每个分片上执行查询... } ``` ### Range 分片 ```cpp // 按时间范围分片 (支持动态扩展) RangeShardStrategy rangeStrategy("t_metric"); // 注册分片范围 ShardTarget t0; t0.databaseId = "db_0"; t0.shardIndex = 0; t0.tableSuffix = "_2024"; rangeStrategy.registerRange(QVariant(0), QVariant(2025), t0); ShardTarget t1; t1.databaseId = "db_1"; t1.shardIndex = 1; t1.tableSuffix = "_2025"; rangeStrategy.registerRange(QVariant(2025), QVariant(2026), t1); // 路由 auto r = rangeStrategy.route(QVariant(2024)); // → db_0 auto r2 = rangeStrategy.route(QVariant(2025)); // → db_1 // 范围外 → 错误 auto r3 = rangeStrategy.route(QVariant(9999)); // r3.isErr() == true ``` ### ShardingRouter ```cpp ShardingRouter router; // 注册分片 auto db0 = DbFactory::create(DbConfig::sqlite("shard_0.db")).takeValue(); ShardTarget target0; target0.databaseId = "shard_0"; target0.shardIndex = 0; target0.tableSuffix = "_0"; router.registerShard(target0, std::move(db0)); // 运行时添加分片 auto db1 = DbFactory::create(DbConfig::sqlite("shard_1.db")).takeValue(); ShardTarget target1; target1.databaseId = "shard_1"; target1.shardIndex = 1; target1.tableSuffix = "_1"; router.addShard(target1, std::move(db1)); // 路由查询 HashShardStrategy strategy(2, "t_data"); IDatabase& routedDb = router.route(QVariant(42), strategy); routedDb.executeQuery("SELECT * FROM t_data_0 WHERE id=?", {42}); // 从 JSON 配置热加载 QString jsonConfig = R"({ "shards": [ {"id": "s0", "index": 0, "suffix": "_0"}, {"id": "s1", "index": 1, "suffix": "_1"} ] })"; router.reloadConfig(jsonConfig, [](const ShardTarget& target) { auto db = DbFactory::create(DbConfig::sqlite( QString("shard%1.db").arg(target.tableSuffix))).takeValue(); return db; }); ``` --- ## 构建 ### qmake ```bash # 全量构建 (lib + tools + tests + examples) d:/qt/5.15.2/mingw81_64/bin/qmake.exe database.pro -spec win32-g++ CONFIG+=debug mingw32-make # 仅构建库 cd lib && qmake && make # 单独构建测试项目 mkdir build_extra && cd build_extra qmake ../tests/tests_extra.pro && make ``` ### CMake (推荐) ```bash # 静态库 + 所有子项目 cmake -B build_cmake -G "MinGW Makefiles" \ -DCMAKE_PREFIX_PATH=d:/qt/5.15.2/mingw81_64 cmake --build build_cmake --parallel # 包含测试 cmake -B build_cmake -DBUILD_TESTS=ON cmake --build build_cmake --parallel ctest --test-dir build_cmake # 仅构建库 cmake -B build_cmake -DBUILD_TOOLS=OFF -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF # 动态库 cmake -B build_cmake -DBUILD_SHARED_LIBS=ON # ODBC 集成测试 (需要真实数据库) cmake -B build_cmake -DODBC_LIVE_TEST=ON \ -DODBC_HOST=192.168.1.100 -DODBC_PORT=5236 \ -DODBC_USER=SYSDBA -DODBC_PWD=SYSDBA -DODBC_DB=TEST ``` #### CMake 选项 | 选项 | 默认值 | 说明 | |------|--------|------| | `BUILD_SHARED_LIBS` | OFF | 构建动态库 | | `BUILD_TESTS` | ON | 构建测试 | | `BUILD_TOOLS` | ON | 构建 CLI 工具 | | `BUILD_EXAMPLES` | ON | 构建示例 | | `ODBC_LIVE_TEST` | OFF | 启用 ODBC 集成测试 | ### Linux ARM64 / 银河麒麟 ```bash cmake -B build -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-arm64.cmake cmake --build build --parallel ``` > **注意**: Git Bash 下 MinGW 的 `g++.exe` 不能直接调用,需通过 `cmd.exe` 包装: > ```bash > cmd.exe //c "set PATH=d:\qt\Tools\mingw810_64\bin;d:\qt\5.15.2\mingw81_64\bin;%PATH% && cd /d d:\path\to\build && mingw32-make" > ``` --- ## 新数据库适配 ### 1. 实现 ISqlDialect ```cpp class MyDialect : public ISqlDialect { public: QString quoteIdentifier(const QString& name) const override { return QString("`%1`").arg(name); // MySQL 风格 } QString placeholder(int index) const override { Q_UNUSED(index); return "?"; } QString limitOffset(int limit, int offset = -1) const override { QString sql = QString("LIMIT %1").arg(limit); if (offset >= 0) sql += QString(" OFFSET %1").arg(offset); return sql; } QString autoIncrementPK(const QString& col) const override { return QString("%1 BIGINT AUTO_INCREMENT PRIMARY KEY").arg(col); } QString lastInsertIdSql(const QString&, const QString&) const override { return "SELECT LAST_INSERT_ID()"; } QString mapType(int qtMetaTypeId, int length = -1) const override { // 映射 Qt 类型 → 目标数据库类型 switch (qtMetaTypeId) { case QMetaType::Int: return "INT"; case QMetaType::LongLong: return "BIGINT"; case QMetaType::QString: return length > 0 ? QString("VARCHAR(%1)").arg(length) : "TEXT"; // ... 其他类型映射 default: return "TEXT"; } } QString encodingPragma() const override { return {}; } QString upsertClause(const QStringList& cols) const override { return "ON DUPLICATE KEY UPDATE"; } }; ``` ### 2. 实现 IDatabase ```cpp class MyDatabase : public IDatabase { public: Result open(const DbConfig& config) override { /* ... */ } void close() override { /* ... */ } bool isOpen() const override { /* ... */ } DbType type() const override { return myDbType; } ISqlDialect& dialect() override { return m_dialect; } Result executeQuery(const QString& sql, const QVariantList& bindings = {}) override; Result executeNonQuery(const QString& sql, const QVariantList& bindings = {}) override; Result executeDdl(const QString& sql) override; Result beginTransaction() override; Result commitTransaction() override; Result rollbackTransaction() override; Result lastInsertId(const QString& table, const QString& pkCol) override; QSqlDatabase& nativeConnection() override; Result backupToFile(const QString& dir) override; // 维护接口 void setMaintenanceScheduler(DbMaintenanceScheduler* s) override; DbMaintenanceScheduler* maintenanceScheduler() const override; void setCapacityGuard(CapacityGuard* g) override; CapacityGuard* capacityGuard() const override; void setLogger(void* logger) override; void setSlowQueryThreshold(int ms) override; int slowQueryThreshold() const override; DbConnectionPool* pool() override; private: MyDialect m_dialect; // ... }; ``` ### 3. 注册到工厂 ```cpp // 注册自定义后端 DbFactory::registerType(DbType::MyDb, []() -> std::unique_ptr { return std::make_unique(); }); // 现在可以通过统一入口创建 auto db = Database::open(myConfig).takeValue(); ``` --- ## 项目结构 ``` database/ ├── CMakeLists.txt # CMake 顶层构建 ├── database.pro # qmake 顶层构建 ├── README.md # 本文档 ├── lib/ # 核心库 │ ├── database.h # 统一头文件 │ ├── database_global.h # 导出宏 │ ├── core/ # 接口 / 工厂 / 门面 / 连接池 / 维护 │ │ ├── db_config.h/cpp # DbConfig 数据库配置 │ │ ├── db_error.h # DbError + Result 错误处理 │ │ ├── db_interface.h # IDatabase 抽象接口 │ │ ├── db_factory.h/cpp # DbFactory 后端注册工厂 │ │ ├── db_facade.h/cpp # Database 门面 (用户入口) │ │ ├── db_connection_pool.h/cpp# DbConnectionPool 连接池 │ │ └── db_maintenance.h/cpp # DbCleaner + Scheduler + CapacityGuard │ ├── dialect/ # SQL 方言 │ │ ├── sql_dialect.h # ISqlDialect 抽象接口 │ │ ├── sqlite_dialect.h/cpp # SqliteDialect │ │ └── oracle_compat_dialect.h/cpp # Oracle 兼容方言 │ ├── impl/ # 数据库后端实现 │ │ ├── sqlite_database.h/cpp # SQLite │ │ └── odbc_database.h/cpp # ODBC (达梦/金仓/神威) │ └── sharding/ # 分库分表 │ ├── shard_strategy.h/cpp # HashShard + RangeShard │ └── shard_router.h/cpp # ShardingRouter ├── tools/ # CLI 工具 + DDL │ ├── main.cpp # db_tool 入口 │ ├── db_export.h/cpp # DbExporter 导出器 │ ├── db_import.h/cpp # DbImporter 导入器 │ ├── db_shard_tool.h/cpp # 分片导入导出 │ └── ddl/ # DDL 迁移 │ ├── migration.h # Migration 数据结构 │ ├── migration_runner.h/cpp # MigrationRunner 迁移引擎 │ ├── migration_file_lock.h/cpp# 文件锁 │ ├── sql_splitter.h/cpp # SQL 分词器 │ ├── sql_risk_analyzer.h/cpp # SQL 风险分析器 │ ├── xml_migration_parser.h/cpp# XML 迁移解析器 │ └── xml_sql_generator.h/cpp # XML→SQL 生成器 ├── tests/ # 单元测试 │ ├── tst_main.cpp # 核心库综合测试 (含 ORM + 连接池) │ ├── tst_ddl.cpp # DDL 工具测试 │ ├── tst_export_import.cpp # 导入导出测试 │ ├── tst_lib_extra.cpp # 扩展覆盖测试 │ ├── tst_main_odbc.cpp # ODBC 测试 (含 ODBC_LIVE_TEST 宏) │ └── run_tests.sh # 一键测试 + 覆盖率脚本 ├── examples/ # 使用示例 │ ├── main.cpp # 综合示例 (ORM + 备份 + 迁移 + 分片) │ ├── main_sharding.cpp # 分库分表示例 │ ├── main_migration.cpp # 迁移专项示例 │ └── main_odbc.cpp # ODBC 示例 ├── resources/ # Qt 资源 │ └── migrations/sqlite/ # 内置迁移脚本 ├── third_party/QxOrm/ # QxOrm ORM 库 └── doc/ # 设计文档 ├── 需求规格说明书_V3.md └── 软件设计说明书_V2.md ``` --- ## 测试与覆盖率 ```bash # 一键运行所有测试 + 生成覆盖率报告 QTDIR=d:/qt/5.15.2/mingw81_64 BUILD_DIR=build_cmake bash tests/run_tests.sh coverage-only # 分步执行 bash tests/run_tests.sh clean # 清理 bash tests/run_tests.sh test # 仅编译 + 测试 bash tests/run_tests.sh coverage # 完整流程 (清理→编译→测试→报告) bash tests/run_tests.sh rebuild # 全量重建 # 生成物 # coverage_summary.txt 文本摘要 # coverage_detail.txt 详细报告 (含分支) # coverage_report/coverage.html HTML 逐行着色报告 ``` | 测试套件 | 用例数 | 说明 | |----------|--------|------| | `database_tests` | 95 | 核心库综合 (ORM + 连接池 + 方言 + 迁移执行器 + 门面 + 维护) | | `database_tests_ddl` | 64 | DDL 工具 (SqlSplitter + SqlRiskAnalyzer + XmlParser/Generator + MigrationFileLock) | | `database_tests_ei` | 22 | 导入导出 (DbExporter CSV/JSON + DbImporter + DbShardTool) | | `database_tests_extra` | 167 | 扩展覆盖 (迁移执行器深度 + 工厂 + 风险分析 + 方言 + 导入器 + 分片 + 错误分类) | | `database_tests_odbc` | 33 + 20* | ODBC 接口 (含 `ODBC_LIVE_TEST` 宏保护的集成测试) | *\*20 个 ODBC 集成测试需 `cmake -DODBC_LIVE_TEST=ON` 启用* --- ## 许可 内部项目