应优先用activerecord处理业务实体操作,如需软删除、事件、验证等;dao适用于批量更新、原生sql等场景,但须注意参数绑定、事务一致性及缓存差异。

Yii控制器里直接写数据库查询,不是不行,但混用模型和DAO容易导致事务不一致、AR缓存失效、或ORM层特性被绕过——关键看你要查什么、改不改数据、是否要复用验证逻辑。
什么时候该用 ActiveRecord 而不是 createCommand()
如果你的操作涉及业务实体(比如用户、订单)、需要自动类型转换、软删除、事件触发、或后续要赋值给模型做校验/保存,就别绕开 ActiveRecord。硬切 DAO 会丢掉这些隐式契约。
- 查单条且要后续调
save()或delete()?必须用User::findOne($id),别用createCommand()->queryOne() - 要批量更新字段但不触发
beforeSave?这时 DAO 更合适:createCommand()->update('user', ['status' => 1], ['id' => $ids])->execute() -
ActiveRecord::find()默认加了WHERE deleted_at IS NULL(如果启用了软删除),而 DAO 不会自动加——漏掉这个条件就可能读到“已删”数据
createCommand() 执行原生 SQL 的三个雷区
DAO 灵活,但 Yii 对参数绑定、事务边界、连接复用有默认行为,不注意就会出错。
- 拼接 SQL 字符串(如
"WHERE id = " . $id)——必然导致 SQL 注入,createCommand()的安全前提是**所有变量必须走参数绑定**:createCommand("SELECT * FROM user WHERE status = :status")->bindValue(':status', $status)->queryAll() - 在事务外执行
createCommand()->insert(),然后又在另一个地方手动$transaction->commit()——DAO 操作不会自动加入当前 AR 事务,得显式传入连接:Yii::$app->db->createCommand(...)->execute()才能复用同一连接 - 查大表用
queryAll()加载全部结果到内存,而不是用each()流式处理——10 万行数据可能直接 OOM
混合使用时怎么保持事务一致性
一个请求里既查模型又跑原生 SQL,事务必须由同一 DB 连接驱动,否则回滚只对部分操作生效。
- 统一从
Yii::$app->db开启事务:$transaction = Yii::$app->db->beginTransaction(),而不是User::getDb()->beginTransaction()(可能拿到不同连接实例) - 所有 DAO 操作必须基于该连接:
$transaction->getConnection()->createCommand(...)->execute() - AR 操作默认走
Yii::$app->db,所以只要没改模型的getDb()方法,它天然在同一个事务里 - 别在事务中调用
Yii::$app->db->close()——这会提前释放连接,导致后续语句报Connection is not active
最常被忽略的是:AR 查询默认开启查询缓存(如果配置了 queryCache),而 DAO 完全不走缓存。同个请求里先 DAO 查了一次,再 AR 查同一张表,结果可能不一致——要么关掉 AR 缓存,要么 DAO 也手动加缓存键,别指望它们自动同步。











