直接使用 $this->db 前必须确保已正确加载数据库类,常见“调用非对象上的 query()”错误源于 $this->db 为 NULL,主因是未加载、加载顺序错误或配置失效。

模型里直接用 $this->db 就行,但必须确保数据库类已加载、且没被覆盖或误初始化。 常见的“调用非对象上的成员函数 query()”错误,90% 是因为 $this->db 根本不是数据库实例——要么没加载,要么在构造函数里写错了顺序,要么手动赋值时搞混了返回值。
模型构造函数中加载数据库的正确写法
CodeIgniter 的模型继承自 CI_Model,它本身不自动加载数据库类。你得显式触发加载,且要注意 $this->load->database() 的返回行为:
- 不带参数或传
FALSE:直接把数据库对象挂到$this->db上(推荐用于单数据库场景) - 传
TRUE:返回一个新的数据库对象,不会覆盖$this->db,必须手动赋值,例如$this->db = $this->load->database('second_db', TRUE) - 构造函数里如果先调用了
parent::__construct(),再加载数据库,顺序不能反;否则$this->load还没就绪
错误示范:$this->db = $this->load->database(); —— 这句其实多余,且可能破坏默认行为;正确写法就是 $this->load->database(); 然后直接用 $this->db->get()。
多数据库切换时模型怎么指定连接
当配置了多个数据库组(如 $db['default'] 和 $db['log_db']),模型要连非默认库,有两种稳定方式:
- 在构造函数里用
$this->db = $this->load->database('log_db', TRUE);,此后所有$this->db->xxx()都走这个库 - 不改
$this->db,改用临时对象:$log_db = $this->load->database('log_db', TRUE); $log_db->insert(...);—— 适合偶尔跨库查写,不影响主逻辑 - 别用
$this->db->db_select('log_db'):这是旧版 CI2 的写法,CI3/CI4 不支持,会静默失败或报错
注意:数据库组名必须和 application/config/database.php 中定义的键完全一致,大小写敏感,比如 'second_db' 写成 'Second_DB' 就会加载失败,$this->db 变成 NULL。
为什么 $this->db->query() 报“非对象”错误
这个致命错误只说明一件事:$this->db 当前是 NULL 或未初始化的对象。常见原因有:
- 忘了在模型构造函数里调用
$this->load->database() - 在
parent::__construct()之前就尝试访问$this->load或$this->db - 配置文件
database.php里数据库组定义语法错误(比如漏了逗号、引号不匹配),导致整个$db数组没载入 - CI3 中启用了
db_debug但数据库连不上,框架可能跳过初始化而没抛异常,只留个空$this->db
调试建议:在模型方法开头加 var_dump($this->db); die;,看输出是不是 NULL 或 object(CI_DB_mysqli_driver)。不是后者,就回头检查加载逻辑和配置文件。
动态数据库连接(运行时传参)怎么在模型里做
如果你需要根据用户输入或配置实时连不同数据库(比如数据迁移、多租户),不能靠 database.php 静态配置。做法是构造一个数组,再喂给 $this->load->database():
$dynamic_config = [
'hostname' => $host,
'username' => $user,
'password' => $pass,
'database' => $dbname,
'dbdriver' => 'mysqli',
'char_set' => 'utf8',
'dbcollat' => 'utf8_general_ci'
];
$external_db = $this->load->database($dynamic_config, TRUE);
$query = $external_db->get('users');
关键点:
- 必须传
TRUE,否则$this->load->database()会试图覆盖默认$this->db,引发冲突 - 动态配置里至少要有
hostname、username、password、database、dbdriver,缺一不可 - 不要复用这个
$external_db对象跨请求,它不带连接池,每次都要重建
最易忽略的是字符集配置——漏掉 char_set 和 dbcollat,中文可能乱码,且某些 MySQL 版本会拒绝连接。


















