简体中文
RelationQuery 是基于 Drogon ORM 深度封装的链式关联查询增强库,专为
C++ Drogon 后端设计。无需手写复杂联表 SQL,通过链式风格优雅实现一对一、一对多、
多对多、子查询关联,自动完成结果集去重、嵌套对象映射、分页排序、列别名兼容,大幅
简化复杂业务联表开发。插件同时支持同步与协程异步双调用模式,无缝适配 Drogon 异步
编程模型。
- ✅ 支持三种主流关系:hasOne 一对一、hasMany 一对多、manyToMany 多对多
- ✅ 支持子查询关联查询,可直接嵌套使用
- ✅ 关联查询采用 LEFT JOIN,保证主表数据不丢失
- ✅ 自动去重合并一对多 / 多对多结果,避免联表重复数据
- ✅ 支持自定义表别名、列别名,可解决 MySQL 同名字段冲突
- ✅ 极简链式调用,语义直观、代码高度简洁
- ✅ 内置条件筛选、排序、分页、偏移
- ✅ 自动映射为
std::tuple嵌套结构,支持 optional / vector 自动组装 - ✅ 完全兼容原生 Drogon Model,无侵入式接入
- ✅ 原生支持 C++20 协程,与 Drogon 框架协程体系完全兼容,非阻塞执行数据库查询
确保您已经在开发环境中安装并正确配置了 drogon 框架,并安装了
至少一个数据库的开发端(可以通过drogon_ctl --version确认)。
要安装 RelationQuery 插件,可以按照以下步骤执行:
cd your_project/plugins
git clone https://github.com/tanglong3bf/RelationQuery.git-I./plugins/RelationQuery/src协程支持说明:使用协程接口需确保项目开启 C++20 及以上编译标准,Drogon 框架 默认已启用协程支持,无需额外配置。若编译器不支持 C++20 标准协程,插件会通过特性 宏自动屏蔽协程接口,仅保留同步接口,编译与运行完全不受影响。
以下是一个在yaml文件配置此插件的示例:
plugins:
- name: tl::sql::RelationQuery
config:
# 使用哪一个数据库客户端完成查询,默认值:default
db_client_name: default以下是一个在json文件配置此插件的示例:
{
"plugins": [
{
"name": "tl::sql::RelationQuery",
"config": {
// 使用哪一个数据库客户端完成查询,默认值:default
"db_client_name": "default"
}
}
]
}下面提供常用场景示例:
- 一对一查询(用户 --- 部门)
- 一对一 + 一对多(部门 --- 父部门 --- [用户])
- 多对多 + 子查询(部门 --- [用户 --- [角色]])
using namespace std;
using namespace drogon;
using namespace drogon::orm;
using namespace tl::sql;
using namespace drogon_model::relation_query_test; // 测试数据库名
int main()
{
app().registerBeginningAdvice([] {
// 获取插件
const auto *plugin = app().getPlugin<RelationQuery>();
// 构建一对一查询
const auto query =
Query<SysUser>("u")
.hasOne<SysDept>(JoinOn{"u.dept_id", "d.dept_id"}, "d")
.orderBy("u.user_id");
// 查询结果(推荐使用auto,这里只做展示)
const vector<tuple<SysUser, optional<SysDept>>> result =
plugin->findAll(query);
});
app().registerBeginningAdvice([] {
const auto *plugin = app().getPlugin<RelationQuery>();
// 构建一对一以及一对多查询
const auto query =
Query<SysDept>("d")
.hasOne<SysDept>(JoinOn{"d.parent_id", "p.dept_id"}, "p")
.hasMany<SysUser>(JoinOn{"d.dept_id", "u.dept_id"}, "u")
.orderBy("d.dept_id")
.orderBy("u.user_id");
// 查询结果(推荐使用auto,这里只做展示)
const vector<tuple<SysDept, optional<SysDept>, vector<SysUser>>>
result = plugin->findAll(query);
});
app().registerBeginningAdvice([] {
const auto *plugin = app().getPlugin<RelationQuery>();
// 构建多对多子查询
auto subQuery =
Query<SysUser>("u", {"", "", "u_dept_id"}) // 列别名可选
.manyToMany<SysUserRole, SysRole>(
JoinOn{"u.user_id", "ur.user_id"},
JoinOn{"ur.role_id", "r.role_id"},
"ur",
"r",
{"ur_user_id",
"ur_role_id"}, // 为避免列名冲突,为中间表的列起别名
{"", "role_name"}); // 列别名可选
// 使用子查询
const auto query =
Query<SysDept>("d", {"", "dept_name"}) // 列别名可选
.withSub(subQuery,
JoinOn{"d.dept_id", "user_info.u_dept_id"},
"user_info")
.orderBy("d.dept_id")
.orderBy("user_info.user_id")
.orderBy("user_info.ur_role_id");
// 查询结果(推荐使用auto,这里只做展示)
const vector<
tuple<SysDept,
vector<tuple<SysUser, vector<pair<SysUserRole, SysRole>>>>>>
result = plugin->findBy(query, "d.name LIKE '%部'");
});
app().loadConfigFile("../config.yaml");
app().run();
return 0;
}插件提供与同步接口一一对应的协程版本:findAllCoro、findByCoro。查询构建逻辑
与同步模式完全一致,仅执行入口不同,可在 Drogon 协程 Handler 中通过 co_await
调用,全程不阻塞 IO 线程,适配高并发异步场景。
using namespace std;
using namespace drogon;
using namespace drogon::orm;
using namespace tl::sql;
using namespace drogon_model::relation_query_test; // 测试数据库名
int main()
{
app().registerBeginningAdvice([] {
const auto *plugin = app().getPlugin<RelationQuery>();
const auto query =
Query<SysUser>("u")
.hasOne<SysDept>(JoinOn{"u.dept_id", "d.dept_id"}, "d")
.orderBy("u.user_id");
// 仅有这一行发生变动
// 协程函数中可以直接 co_await 调用
const auto result = drogon::async_run(plugin->findAllCoro(query));
});
app().loadConfigFile("../config.yaml");
app().run();
return 0;
}