C++ ORM与数据库访问层设计:Repository模式实战

C++ ORM与数据库访问层设计:Repository模式实战

创建日期 : 2026-03-25
更新日期 : 2026-03-25
作者 : zry
标签: C++, ORM, Repository模式, 数据库, 数据访问层, MySQL


📋 目录

  1. 引言
  2. 为什么需要Repository模式
  3. Repository模式架构设计
  4. 核心代码实现
  5. CRUD操作详解
  6. 复杂查询实现
  7. 性能优化策略
  8. 最佳实践
  9. 总结

引言

在现代C++应用程序开发中,数据库访问层的设计直接影响系统的可维护性和可扩展性。Repository模式通过将数据访问逻辑与业务逻辑分离,提供了一种清晰、可测试的数据访问方案。

本文基于AIDC自动气象站数据收集系统的实践,深入讲解如何在C++中实现一个类型安全、高效的ORM风格的数据库访问层。


为什么需要Repository模式

传统数据访问的问题

传统方式:混乱的数据访问
直接SQL
直接SQL
直接SQL
业务逻辑
数据库
数据库
数据库

传统方式的痛点:

  • SQL语句散落在业务代码中,难以维护
  • 数据模型变更需要修改多处代码
  • 数据库切换成本高
  • 单元测试困难,难以Mock
  • 重复代码多,DRY原则被破坏

Repository模式的优势

Repository模式:清晰的分层
使用Repository
统一接口
ORM
业务逻辑层
Repository层
数据访问层
数据库

Repository模式的核心价值:

优势 说明
关注点分离 业务逻辑与数据访问解耦
可测试性 易于Mock,支持单元测试
可维护性 数据访问逻辑集中管理
类型安全 编译期检查,减少运行时错误
代码复用 通用CRUD操作抽象复用

Repository模式架构设计

整体架构

数据访问层架构
继承/使用
连接池
实现
依赖接口
Repository
DbManager
MySQL连接
具体仓储
StationRepository
DeviceRepository
DataRepository
业务服务

类层次结构

Repository<T>
+Insert(args...) : Result<int>
+QueryAll() : Result<vector<T>>
+QueryWhere(args...) : Result<vector<T>>
+Update(args...) : Result<int>
+Delete(args...) : Result<int>
StationRepository
+FindByStationNum(num) : Station
+FindActiveStations() : vector<Station>
DeviceRepository
+FindByType(type) : vector<Device>
+UpdateStatus(id, status) : bool


核心代码实现

1. 基础Repository模板类

cpp 复制代码
/**
 * @file db_repository.hpp
 * @brief 数据访问基类模板
 * @date 2026-03-25
 */

#ifndef ZRY_DB_REPOSITORY_HPP
#define ZRY_DB_REPOSITORY_HPP

#include "db_manager.hpp"
#include "db_all.hpp"
#include "../base/error.hpp"
#include <vector>
#include <optional>

namespace zry::db {

/**
 * @brief 通用Repository模板类
 * 
 * 提供类型安全的CRUD操作,所有具体仓储类都继承此类
 * 
 * @tparam T 实体类型,需要满足:
 *   - 有默认构造函数
 *   - 支持反射(YLT_REFL宏标记字段)
 */
template<typename T>
class Repository {
public:
    using Entity = T;
    
    /**
     * @brief 插入记录
     * @tparam Args 可变参数类型
     * @param args 插入参数
     * @return Result<int> 插入行数或错误
     * 
     * @code
     * auto result = repo.Insert(station);
     * if (result) {
     *     std::cout << "插入 " << result.Value() << " 行\n";
     * }
     * @endcode
     */
    template<typename... Args>
    Result<int> Insert(Args&&... args) {
        auto& db = DbManager::Instance().GetDb();
        auto result = db.insert(std::forward<Args>(args)...);
        if (result < 0) {
            return ErrorCode::ERROR_DB_INSERT;
        }
        return result;
    }
    
    /**
     * @brief 查询所有记录
     * @return Result<std::vector<T>> 记录列表或错误
     */
    Result<std::vector<T>> QueryAll() {
        auto& db = DbManager::Instance().GetDb();
        auto result = db.query<T>();
        return result;
    }
    
    /**
     * @brief 条件查询
     * @tparam Args 查询条件类型
     * @param args 查询条件(支持字符串条件或lambda谓词)
     * @return Result<std::vector<T>> 符合条件的记录
     * 
     * @code
     * // 方式1:字符串条件
     * auto result = repo.QueryWhere("status = 1");
     * 
     * // 方式2:lambda谓词
     * auto result = repo.QueryWhere([](const Station& s) {
     *     return s.status == 1;
     * });
     * @endcode
     */
    template<typename... Args>
    Result<std::vector<T>> QueryWhere(Args&&... args) {
        auto& db = DbManager::Instance().GetDb();
        auto result = db.query<T>(std::forward<Args>(args)...);
        return result;
    }
    
    /**
     * @brief 更新记录
     * @tparam Args 更新参数类型
     * @param args 更新参数
     * @return Result<int> 更新行数或错误
     */
    template<typename... Args>
    Result<int> Update(Args&&... args) {
        auto& db = DbManager::Instance().GetDb();
        auto result = db.update(std::forward<Args>(args)...);
        if (result < 0) {
            return ErrorCode::ERROR_DB_QUERY;
        }
        return result;
    }
    
    /**
     * @brief 删除记录
     * @tparam Args 删除条件类型
     * @param args 删除条件
     * @return Result<int> 删除行数或错误
     */
    template<typename... Args>
    Result<int> Delete(Args&&... args) {
        auto& db = DbManager::Instance().GetDb();
        auto result = db.delete_records<T>(std::forward<Args>(args)...);
        if (result < 0) {
            return ErrorCode::ERROR_DB_QUERY;
        }
        return result;
    }
    
    /**
     * @brief 根据ID查询单条记录
     * @tparam IdType ID类型
     * @param id 记录ID
     * @return std::optional<T> 记录或空
     */
    template<typename IdType>
    std::optional<T> FindById(IdType id) {
        auto result = QueryWhere([&id](const T& entity) {
            return entity.id == id;
        });
        
        if (result && !result.Value().empty()) {
            return result.Value()[0];
        }
        return std::nullopt;
    }
    
    /**
     * @brief 批量插入(事务保护)
     * @tparam Iterator 迭代器类型
     * @param begin 开始迭代器
     * @param end 结束迭代器
     * @return Result<int> 插入行数或错误
     */
    template<typename Iterator>
    Result<int> BatchInsert(Iterator begin, Iterator end) {
        auto& db = DbManager::Instance().GetDb();
        
        // 开启事务
        db.begin_transaction();
        
        int total = 0;
        try {
            for (auto it = begin; it != end; ++it) {
                auto result = db.insert(*it);
                if (result < 0) {
                    db.rollback();
                    return ErrorCode::ERROR_DB_INSERT;
                }
                total += result;
            }
            db.commit();
            return total;
        } catch (...) {
            db.rollback();
            return ErrorCode::ERROR_DB_INSERT;
        }
    }
};

} // namespace zry::db

#endif // ZRY_DB_REPOSITORY_HPP

2. 具体仓储类实现

cpp 复制代码
/**
 * @file station_repository.hpp
 * @brief 站点数据仓储
 * @date 2026-03-25
 */

#ifndef ZRY_STATION_REPOSITORY_HPP
#define ZRY_STATION_REPOSITORY_HPP

#include "db_repository.hpp"
#include "db_station.hpp"

namespace zry::db {

/**
 * @brief 站点数据仓储
 * 
 * 提供站点相关的数据访问方法
 */
class StationRepository : public Repository<StationInfo> {
public:
    /**
     * @brief 根据站点编号查询
     * @param station_num 站点编号
     * @return std::optional<StationInfo> 站点信息
     */
    std::optional<StationInfo> FindByStationNum(const std::string& station_num) {
        auto result = QueryWhere([&station_num](const StationInfo& s) {
            return s.station_num == station_num;
        });
        
        if (result && !result.Value().empty()) {
            return result.Value()[0];
        }
        return std::nullopt;
    }
    
    /**
     * @brief 查询活跃站点
     * @return std::vector<StationInfo> 活跃站点列表
     */
    std::vector<StationInfo> FindActiveStations() {
        auto result = QueryWhere([](const StationInfo& s) {
            return s.status == 1;
        });
        
        if (result) {
            return result.Value();
        }
        return {};
    }
    
    /**
     * @brief 根据区域查询站点
     * @param region 区域代码
     * @return std::vector<StationInfo> 站点列表
     */
    std::vector<StationInfo> FindByRegion(const std::string& region) {
        auto result = QueryWhere([&region](const StationInfo& s) {
            return s.region_code == region;
        });
        
        if (result) {
            return result.Value();
        }
        return {};
    }
    
    /**
     * @brief 更新站点状态
     * @param station_num 站点编号
     * @param status 新状态
     * @return bool 是否成功
     */
    bool UpdateStatus(const std::string& station_num, int status) {
        auto result = Update([&station_num, &status](StationInfo& s) {
            if (s.station_num == station_num) {
                s.status = status;
                s.update_time = TimeTool::Now();
                return true;
            }
            return false;
        });
        
        return result && result.Value() > 0;
    }
};

/**
 * @brief 设备数据仓储
 */
class DeviceRepository : public Repository<DeviceInfo> {
public:
    /**
     * @brief 根据设备类型查询
     * @param device_type 设备类型
     * @return std::vector<DeviceInfo> 设备列表
     */
    std::vector<DeviceInfo> FindByType(const std::string& device_type) {
        auto result = QueryWhere([&device_type](const DeviceInfo& d) {
            return d.device_type == device_type;
        });
        
        if (result) {
            return result.Value();
        }
        return {};
    }
    
    /**
     * @brief 查询站点下的所有设备
     * @param station_num 站点编号
     * @return std::vector<DeviceInfo> 设备列表
     */
    std::vector<DeviceInfo> FindByStation(const std::string& station_num) {
        auto result = QueryWhere([&station_num](const DeviceInfo& d) {
            return d.station_num == station_num;
        });
        
        if (result) {
            return result.Value();
        }
        return {};
    }
};

} // namespace zry::db

CRUD操作详解

创建(Create)

cpp 复制代码
/**
 * @brief 创建记录的最佳实践
 */
class CreateExamples {
public:
    // 方式1:直接插入实体
    void CreateStation() {
        StationRepository repo;
        
        StationInfo station;
        station.station_num = "54511";
        station.name = "北京站";
        station.latitude = 39.9042;
        station.longitude = 116.4074;
        station.status = 1;
        station.create_time = TimeTool::Now();
        
        auto result = repo.Insert(station);
        if (result) {
            ZRY_LOG_INFO("创建站点成功,ID: {}", result.Value());
        } else {
            ZRY_LOG_ERROR("创建站点失败: {}", result.Error().ToString());
        }
    }
    
    // 方式2:批量插入
    void BatchCreateStations() {
        StationRepository repo;
        
        std::vector<StationInfo> stations = {
            {"54511", "北京站", 39.9, 116.4},
            {"54512", "天津站", 39.1, 117.2},
            {"54513", "石家庄", 38.0, 114.5}
        };
        
        auto result = repo.BatchInsert(stations.begin(), stations.end());
        if (result) {
            ZRY_LOG_INFO("批量插入 {} 条记录", result.Value());
        }
    }
};

读取(Read)

cpp 复制代码
/**
 * @brief 查询操作的各种模式
 */
class ReadExamples {
public:
    // 简单查询
    void SimpleQuery() {
        StationRepository repo;
        
        // 查询所有
        auto all_result = repo.QueryAll();
        if (all_result) {
            for (const auto& station : all_result.Value()) {
                std::cout << station.name << "\n";
            }
        }
    }
    
    // 条件查询
    void ConditionalQuery() {
        StationRepository repo;
        
        // 查询活跃站点
        auto active = repo.FindActiveStations();
        
        // 根据编号查询
        auto station = repo.FindByStationNum("54511");
        if (station) {
            std::cout << "找到站点: " << station->name << "\n";
        }
    }
    
    // 复杂条件查询
    void ComplexQuery() {
        DeviceRepository repo;
        
        auto result = repo.QueryWhere([](const DeviceInfo& d) {
            return d.device_type == "TEMP" && 
                   d.status == 1 &&
                   d.last_report > TimeTool::AddDays(TimeTool::Today(), -7);
        });
        
        if (result) {
            ZRY_LOG_INFO("找到 {} 个活跃温度设备", result.Value().size());
        }
    }
};

更新(Update)

cpp 复制代码
/**
 * @brief 更新操作的各种模式
 */
class UpdateExamples {
public:
    // 单条更新
    void UpdateSingle() {
        StationRepository repo;
        
        auto result = repo.Update([](StationInfo& s) {
            if (s.station_num == "54511") {
                s.status = 0;
                s.update_time = TimeTool::Now();
                return true;  // 返回true表示需要更新
            }
            return false;
        });
        
        if (result && result.Value() > 0) {
            ZRY_LOG_INFO("更新成功");
        }
    }
    
    // 批量更新
    void BatchUpdate() {
        StationRepository repo;
        
        // 将所有北京区域的站点状态设为活跃
        auto result = repo.Update([](StationInfo& s) {
            if (s.region_code == "110000") {
                s.status = 1;
                return true;
            }
            return false;
        });
    }
};

删除(Delete)

cpp 复制代码
/**
 * @brief 删除操作的各种模式
 */
class DeleteExamples {
public:
    // 条件删除
    void ConditionalDelete() {
        StationRepository repo;
        
        // 删除已停用的站点
        auto result = repo.Delete([](const StationInfo& s) {
            return s.status == 0;
        });
        
        if (result) {
            ZRY_LOG_INFO("删除 {} 条记录", result.Value());
        }
    }
    
    // 软删除(推荐)
    void SoftDelete() {
        StationRepository repo;
        
        // 不真正删除,只标记状态
        auto result = repo.Update([](StationInfo& s) {
            if (s.station_num == "54511") {
                s.status = -1;  // -1表示已删除
                s.delete_time = TimeTool::Now();
                return true;
            }
            return false;
        });
    }
};

复杂查询实现

分页查询

cpp 复制代码
/**
 * @brief 分页查询支持
 */
template<typename T>
class PaginatedRepository : public Repository<T> {
public:
    struct PageResult {
        std::vector<T> data;
        int total_count;
        int page;
        int page_size;
        int total_pages;
    };
    
    /**
     * @brief 分页查询
     * @tparam Predicate 谓词类型
     * @param predicate 查询条件
     * @param page 页码(从1开始)
     * @param page_size 每页大小
     * @return PageResult 分页结果
     */
    template<typename Predicate>
    PageResult QueryPage(Predicate predicate, int page, int page_size) {
        auto& db = DbManager::Instance().GetDb();
        
        // 查询总数
        int total = db.query_count<T>(predicate);
        
        // 查询当前页数据
        int offset = (page - 1) * page_size;
        auto data = db.query<T>(predicate, offset, page_size);
        
        return {
            data,
            total,
            page,
            page_size,
            (total + page_size - 1) / page_size
        };
    }
};

// 使用示例
void UsePagination() {
    PaginatedRepository<StationInfo> repo;
    
    auto page = repo.QueryPage(
        [](const StationInfo& s) { return s.status == 1; },
        1,  // 第1页
        20  // 每页20条
    );
    
    std::cout << "总计: " << page.total_count << "\n";
    std::cout << "页数: " << page.total_pages << "\n";
    
    for (const auto& station : page.data) {
        std::cout << station.name << "\n";
    }
}

关联查询

cpp 复制代码
/**
 * @brief 关联查询支持
 */
class StationDeviceRepository {
public:
    struct StationWithDevices {
        StationInfo station;
        std::vector<DeviceInfo> devices;
    };
    
    /**
     * @brief 查询站点及其设备
     * @param station_num 站点编号
     * @return std::optional<StationWithDevices> 结果
     */
    std::optional<StationWithDevices> FindStationWithDevices(
        const std::string& station_num) {
        
        StationRepository station_repo;
        DeviceRepository device_repo;
        
        // 查询站点
        auto station = station_repo.FindByStationNum(station_num);
        if (!station) {
            return std::nullopt;
        }
        
        // 查询设备
        auto devices = device_repo.FindByStation(station_num);
        
        return StationWithDevices{*station, devices};
    }
    
    /**
     * @brief 查询所有站点及其设备数量
     */
    std::vector<std::pair<StationInfo, int>> FindStationsWithDeviceCount() {
        StationRepository station_repo;
        DeviceRepository device_repo;
        
        auto stations = station_repo.FindActiveStations();
        std::vector<std::pair<StationInfo, int>> result;
        
        for (const auto& station : stations) {
            auto devices = device_repo.FindByStation(station.station_num);
            result.emplace_back(station, devices.size());
        }
        
        return result;
    }
};

性能优化策略

1. 连接池复用

cpp 复制代码
/**
 * @brief 连接池优化
 */
class OptimizedRepository : public Repository<DataEntity> {
public:
    /**
     * @brief 批量操作使用同一连接
     */
    void BatchOperationWithSingleConnection() {
        auto& db = DbManager::Instance().GetDb();
        
        // 获取连接后保持,多次操作复用
        auto conn = db.get_connection();
        
        db.begin_transaction();
        
        try {
            for (int i = 0; i < 1000; ++i) {
                DataEntity data;
                // 填充数据...
                db.insert(data);
            }
            db.commit();
        } catch (...) {
            db.rollback();
            throw;
        }
    }
};

2. 缓存策略

cpp 复制代码
/**
 * @brief 带缓存的Repository
 */
template<typename T>
class CachedRepository : public Repository<T> {
private:
    mutable std::unordered_map<std::string, std::pair<T, 
        std::chrono::steady_clock::time_point>> cache_;
    mutable std::mutex cache_mutex_;
    std::chrono::seconds cache_ttl_{60};  // 默认60秒
    
public:
    /**
     * @brief 带缓存的查询
     */
    std::optional<T> FindByIdCached(const std::string& id) {
        {
            std::lock_guard<std::mutex> lock(cache_mutex_);
            auto it = cache_.find(id);
            if (it != cache_.end()) {
                // 检查缓存是否过期
                auto now = std::chrono::steady_clock::now();
                if (now - it->second.second < cache_ttl_) {
                    return it->second.first;
                }
            }
        }
        
        // 缓存未命中,查询数据库
        auto result = this->FindById(id);
        if (result) {
            std::lock_guard<std::mutex> lock(cache_mutex_);
            cache_[id] = {*result, std::chrono::steady_clock::now()};
        }
        
        return result;
    }
    
    /**
     * @brief 使缓存失效
     */
    void InvalidateCache(const std::string& id) {
        std::lock_guard<std::mutex> lock(cache_mutex_);
        cache_.erase(id);
    }
};

最佳实践

1. 错误处理

cpp 复制代码
/**
 * @brief Repository错误处理最佳实践
 */
void ErrorHandlingBestPractice() {
    StationRepository repo;
    
    // 使用Result类型处理错误
    auto result = repo.FindByStationNum("54511");
    
    if (!result) {
        // 错误处理
        switch (result.Error().Code()) {
            case ErrorCode::ERROR_DB_CONNECTION:
                ZRY_LOG_ERROR("数据库连接失败");
                // 尝试重连或降级
                break;
            case ErrorCode::ERROR_DB_QUERY:
                ZRY_LOG_ERROR("查询执行失败: {}", result.Error().Message());
                break;
            default:
                ZRY_LOG_ERROR("未知错误: {}", result.Error().ToString());
        }
        return;
    }
    
    // 处理结果
    auto station = result.Value();
    // ...
}

2. 事务管理

cpp 复制代码
/**
 * @brief 事务管理最佳实践
 */
class TransactionExample {
public:
    void TransferData() {
        auto& db = DbManager::Instance().GetDb();
        
        // RAII事务管理器
        struct TransactionGuard {
            DbManager& db_;
            bool committed_ = false;
            
            explicit TransactionGuard(DbManager& db) : db_(db) {
                db_.begin_transaction();
            }
            
            void Commit() {
                db_.commit();
                committed_ = true;
            }
            
            ~TransactionGuard() {
                if (!committed_) {
                    db_.rollback();
                }
            }
        };
        
        TransactionGuard tx(db);
        
        try {
            // 执行多个操作
            StationRepository station_repo;
            DeviceRepository device_repo;
            
            station_repo.UpdateStatus("54511", 0);
            device_repo.Delete([](const DeviceInfo& d) {
                return d.station_num == "54511";
            });
            
            tx.Commit();  // 提交事务
        } catch (const std::exception& e) {
            ZRY_LOG_ERROR("事务失败: {}", e.what());
            // 事务会在TransactionGuard析构时回滚
        }
    }
};

3. 单元测试

cpp 复制代码
/**
 * @brief Repository单元测试示例
 */
class MockDbManager {
public:
    MOCK_METHOD(Result<std::vector<StationInfo>>, query, ());
    MOCK_METHOD(Result<int>, insert, (const StationInfo&));
};

TEST(StationRepositoryTest, FindByStationNum) {
    // 使用Mock进行测试
    StationRepository repo;
    
    // 设置Mock期望
    // ...
    
    auto result = repo.FindByStationNum("54511");
    
    EXPECT_TRUE(result.has_value());
    EXPECT_EQ(result->station_num, "54511");
}

总结

本文详细介绍了在C++中实现Repository模式的完整方案:

  1. 基础Repository类:提供类型安全的CRUD操作
  2. 具体仓储类:针对特定实体的扩展方法
  3. 复杂查询:分页、关联查询的实现
  4. 性能优化:连接池、缓存策略
  5. 最佳实践:错误处理、事务管理、单元测试

关键设计决策

决策 选择 理由
ORM风格 轻量级ORM 平衡便利性和性能
模板实现 编译期类型检查 类型安全,零运行时开销
Result类型 显式错误处理 强制处理错误,避免异常滥用
连接池 单例管理 全局统一管理数据库连接

该Repository模式已在AIDC项目中广泛应用,支撑日均百万级的数据库操作。


本文基于AIDC项目数据库访问层实践编写,完整代码可在 src_refactored/include/db/ 目录查看。

https://github.com/0voice

相关推荐
浅念-8 小时前
Linux 开发环境与工具链
linux·运维·服务器·数据结构·c++·经验分享
旺仔.2919 小时前
容器适配器:stack栈 、queue队列、priority queue优先级队列、bitset位图 详解
c++
Arva .10 小时前
深分页与游标
数据库·oracle
潜创微科技--高清音视频芯片方案开发10 小时前
2026年C转DP芯片方案深度分析:从适配场景到成本性能的优选指南
c语言·开发语言
Thomas.Sir10 小时前
第三章:Python3 之 字符串
开发语言·python·字符串·string
idolao10 小时前
MySQL 5.7 安装教程:详细步骤+自定义安装+命令行客户端配置(Windows版)
数据库·windows·mysql
刘景贤10 小时前
C/C++开发环境
开发语言·c++
20年编程老鸟java+ai全栈11 小时前
mysql、pg、oracel数据库迁移避坑指南
数据库·mysql