核心是显式指定连接名:db门面需先connection()再链式调用;eloquent可用on()临时切换或$connection固定;事务限单池;连接名须全小写无点号且预定义。

在 Hyperf 控制器中动态切换数据库连接,核心是**显式指定连接名**,而不是依赖默认配置。Hyperf 的 `Db` 门面和 Eloquent 模型都支持运行时绑定特定连接池,但方式不同、适用场景也不同。
一、用 Db 门面切换(适合原生查询或简单操作)
所有 `Db::` 静态调用必须先通过 connection('连接名') 明确指定连接,之后才能链式调用 table()、select()、insert() 等方法。顺序不能颠倒:
-
Db::connection('log_db')->table('logs')->where('level', 'error')->get()✅ 正确 -
Db::table('logs')->connection('log_db')->get()❌ 无效,connection()不是链式中间方法 -
Db::select('select * from logs where level = ?', ['error'])❌ 默认走default连接,不接受连接参数;必须写成:Db::connection('log_db')->select(...)
二、用 Eloquent 模型切换(适合带逻辑的业务模型)
有三种常用方式,按灵活性和控制粒度排序:
-
临时切换(推荐):在控制器中链式调用
on('连接名'),仅对当前查询生效。User::on('read_pool_1')->where('status', 1)->with(['profile'])->get() -
模型级固定:在模型类顶部定义
protected $connection = 'report_db';,该设置对find()、all()、where()->get()等静态方法有效,但对new User()->save()实例方法无效。 -
关联查询需逐层指定:
with()不会继承主模型的on(),必须手动为每个关联模型指定:User::on('read_pool_1')->with(['posts' => fn($q) => $q->on('read_pool_1')])->get()
三、事务必须单连接池内完成
Hyperf 的事务不支持跨连接池,例如以下写法会直接报错:
-
Db::connection('db.write')->transaction(function () { Db::connection('read_pool_1')->update(...); });❌ 报错:Connection not found 或 SQLSTATE[HY000]: General error - ✅ 正确做法:事务块内所有 DB 操作必须使用同一个连接名:
Db::connection('db.write')->transaction(function () { ... }); - 注意:MySQL 从库通常开启
--read-only,尝试在read_pool_1中执行insert或update会触发SQLSTATE[HY000]: General error: 1290
四、连接名必须合法且已预定义
配置文件中定义的连接名必须满足:全小写、无点号、无空格、无大写字母。否则 Db::connection('Read.Pool') 会抛出 Connection [Read.Pool] not found 错误。
- ✅ 推荐命名:
log_db、report_pg、read_pool_1 - ❌ 禁止命名:
LogDB、read.pool、notify(前后空格也不行) - 配置未生效?记得清空
runtime/container目录并重启服务,避免旧连接池实例残留











