资讯动态

Yii 2 DAO 数据库访问对象深度指南:连接管理、SQL 执行、事务控制与读写分离

发布时间:2026/9/24 8:41:21 来源:尧图企业网站定制
后端Web框架【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址https://gitcode.com/gh_mirrors/yi/yii2点击查看免费下载Yii 2 的 DAODatabase Access Objects数据库访问对象是构建在 PHP PDO 之上、面向对象的关系型数据库访问层也是查询构建器Query Builder与 Active Record 的底层基石。本文以 docs/guide-ru/db-dao.md 为核心骨架结合 framework/db 目录下的真实源码实现系统讲解从连接创建、SQL 执行、参数绑定、表名转义、事务控制到主从复制读写分离的完整实战方案读完即可在项目中直接落地、独立编写高性能且可移植的数据库访问代码。DAO 是什么PDO 之上的对象化访问层Yii 2 的 DAO 直接构建于 PHP 的 PDO 扩展之上为访问关系型数据库提供了一套面向对象的 API。它不需要你引入任何 ORM 概念日常使用中主要打交道的就是纯 SQL 语句和PHP 数组因此它是 Yii 2 中访问数据库效率最高的方式。同时 DAO 也是更高级数据库访问机制的基础包括查询构建器Query Builder和 Active Record 都构建在它之上。换句话说无论你最终用哪种方式访问数据库底层最终都会落到 DAO 的yii\db\Connection与yii\db\Command上。DAO 的另一面是由于不同数据库的 SQL 语法存在差异如果你坚持使用 DAO 写原生 SQL就需要额外投入精力来保证应用对数据库类型不敏感。Yii 通过统一的转义语法、抽象列类型与 Schema 封装见下文来帮你缓解这一问题。开箱即用的数据库支持根据官方指南Yii 2 DAO 默认支持以下数据库数据库版本要求MySQL—MariaDB—SQLite—PostgreSQL8.4 或更高CUBRID9.3 或更高Oracle—MSSQL2008 或更高注意面向 PHP 7 的新版 pdo_ociOracle 驱动目前仅以源码形式存在需要按社区提供的编译说明自行编译后再启用。这些数据库分别对应 framework/db 下的mysql/、sqlite/、pgsql/、cubrid/、oci/、mssql/子目录每个子目录中都包含了该数据库专属的 Schema、QueryBuilder、Command 实现这正是一套 DAO API、多种数据库后端的架构基础。创建数据库连接访问数据库的第一步是建立连接。最直接的方式是实例化yii\db\Connection$db new yii\db\Connection([ dsn mysql:hostlocalhost;dbnameexample, username root, password , charset utf8, ]);由于数据库连接经常在多个地方被使用更常见的做法是把它配置为应用组件return [ // ... components [ // ... db [ class yii\db\Connection, dsn mysql:hostlocalhost;dbnameexample, username root, password , charset utf8, ], ], // ... ];配置完成后即可通过Yii::$app-db在任意位置访问该连接。如果你的应用需要同时访问多个数据库可以配置多个连接组件如db、db2。DSN 格式速查配置连接时必须通过dsn属性指定数据源名称Data Source Name其格式因数据库而异详细语法可查阅 PHP 手册的 PDO 构造说明。以下是各数据库的常见示例MySQL、MariaDBmysql:hostlocalhost;dbnamemydatabaseSQLitesqlite:/path/to/database/filePostgreSQLpgsql:hostlocalhost;port5432;dbnamemydatabaseCUBRIDcubrid:dbnamedemodb;hostlocalhost;port33000MS SQL Serversqlsrv 驱动sqlsrv:Serverlocalhost;DatabasemydatabaseMS SQL Serverdblib 驱动dblib:hostlocalhost;dbnamemydatabaseMS SQL Servermssql 驱动mssql:hostlocalhost;dbnamemydatabaseOracleoci:dbname//localhost:1521/mydatabase通过 ODBC 连接时需显式声明驱动类型如果通过 ODBC 连接数据库由于 DSN 中无法推断底层数据库类型必须额外配置driverName属性告诉 Yii 实际使用的是哪种数据库否则 Schema、查询构建器等依赖驱动类型的逻辑将无法正常工作db [ class yii\db\Connection, driverName mysql, dsn odbc:Driver{MySQL};Serverlocalhost;Databasetest, username root, password , ],从源码看Connection::getDriverName() 的逻辑是若未显式设置driverName则从dsn中冒号前的部分推断驱动名如mysql:...推断出mysql若 DSN 无法推断如 ODBC才会回退到实际连接后读取PDO::ATTR_DRIVER_NAME。除dsn外通常还需要配置username与password。yii\db\Connection的完整可配置属性列表见其类定义 framework/db/Connection.php。懒连接机制真正的连接在首次 SQL 时才建立一个容易忽略的细节是创建Connection实例时并不会真正建立数据库连接只有执行第一条 SQL 或显式调用open()方法时才会建立。对应源码为 Connection::open()若pdo属性尚未初始化则调用createPdoInstance()创建 PDO 实例并执行initConnection()期间还会通过 Yii 日志与性能分析enableLogging/enableProfiling记录 Opening DB connection 事件。连接后初始化afterOpen 事件有时需要在连接建立后立即执行一些初始化 SQL例如设置时区或字符集。可通过注册afterOpen事件即EVENT_AFTER_OPEN来实现直接在应用配置中注册处理函数即可db [ // ... on afterOpen function($event) { // $event-sender 即为数据库连接对象 $event-sender-createCommand(SET time_zone UTC)-execute(); } ],该事件在 Connection::initConnection() 中触发open()的调用链之一用于连接建立后的环境初始化非常合适。补充若使用 MS SQL Server 且需要正确处理二进制数据官方英文版指南还建议在连接属性中显式指定编码例如配置attributes为\PDO::SQLSRV_ATTR_ENCODING \PDO::SQLSRV_ENCODING_SYSTEM。执行 SQL 查询拿到连接实例后执行一条 SQL 通常分三步通过 SQL 语句创建yii\db\Command命令对象绑定参数可选调用yii\db\Command中对应的执行方法。下面示例演示了从数据库取回数据的四种典型方式// 返回多行数据每行是列名 值的关联数组 // 若无结果则返回空数组 $posts Yii::$app-db-createCommand(SELECT * FROM post) -queryAll(); // 返回第一行数据 // 若无结果则返回 false $post Yii::$app-db-createCommand(SELECT * FROM post WHERE id1) -queryOne(); // 返回第一列数据所有行的该列值 // 若无结果则返回空数组 $titles Yii::$app-db-createCommand(SELECT title FROM post) -queryColumn(); // 返回标量值 // 若无结果则返回 false $count Yii::$app-db-createCommand(SELECT COUNT(*) FROM post) -queryScalar();各方法在 framework/db/Command.php 中的实现一目了然queryAll()L416、queryOne()L430、queryScalar()L442、queryColumn()L459最终都汇聚到内部方法queryInternal()只是底层调用的 PDO 取数方式fetchAll、fetch、fetchColumn等不同。注意为保持精度从数据库取回的数据一律以字符串形式表示即使对应数据库列类型是数值型。这一点对金额、大整数等场景尤其重要避免浮点精度丢失。绑定参数防 SQL 注入的第一道防线当 SQL 中包含参数时几乎总是应该使用参数绑定来防止 SQL 注入攻击。例如$post Yii::$app-db-createCommand(SELECT * FROM post WHERE id:id AND status:status) -bindValue(:id, $_GET[id]) -bindValue(:status, 1) -queryOne();SQL 语句中可以嵌入一个或多个参数占位符如上例中的:id占位符是以冒号开头的字符串。绑定参数有以下三种方法bindValue()按值绑定单个参数bindValues()一次调用绑定多个参数bindParam()与bindValue()类似但绑定的是引用。bindValues()与构造器传参是等价的便捷写法$params [:id $_GET[id], :status 1]; $post Yii::$app-db-createCommand(SELECT * FROM post WHERE id:id AND status:status) -bindValues($params) -queryOne(); // 等价写法把参数数组直接作为 createCommand 的第二个参数传入 $post Yii::$app-db-createCommand(SELECT * FROM post WHERE id:id AND status:status, $params) -queryOne();在源码层面Command::bindValue() 在未显式指定$dataType时会调用$this-db-getSchema()-getPdoType($value)见 Schema::getPdoType()根据 PHP 值类型自动推断 PDO 参数类型布尔、整型、字符串、NULL 等无需手工指定即可获得正确绑定。预编译语句一次准备、多次执行参数绑定底层基于 PDO 的**预处理语句prepared statements**实现。除了防止 SQL 注入它还能提升性能一条 SQL 只需准备一次即可用不同参数反复执行。例如$command Yii::$app-db-createCommand(SELECT * FROM post WHERE id:id); $post1 $command-bindValue(:id, 1)-queryOne(); $post2 $command-bindValue(:id, 2)-queryOne(); // ...由于bindParam()支持按引用绑定上面的代码还可以写成$command Yii::$app-db-createCommand(SELECT * FROM post WHERE id:id) -bindParam(:id, $id); $id 1; $post1 $command-queryOne(); $id 2; $post2 $command-queryOne(); // ...注意这里是在执行前就把占位符:id与变量$id绑定随后在每次执行前修改该变量的值常见于循环中。这样批量执行查询的效率远高于为每个参数值重新执行一条新查询。信息参数绑定只用于包含纯 SQL 字符串的场景。在查询构建器、Active Record 等更高抽象层中你通常直接传入值数组Yii 会在内部自动完成参数绑定无需手动指定参数。执行非 SELECT 查询前面介绍的queryXyz()系列方法用于 SELECT 查询。对于不返回数据的语句应调用execute()方法Yii::$app-db-createCommand(UPDATE post SET status1 WHERE id1) -execute();Command::execute() 返回 SQL 语句影响的行数。对于 INSERT、UPDATE、DELETE与其手写纯 SQL不如直接调用insert()、update()、delete()方法由 Yii 负责正确地转义表名、列名并绑定参数值// INSERT表名, 列名值的数组 Yii::$app-db-createCommand()-insert(user, [ name Sam, age 30, ])-execute(); // UPDATE表名, 要更新的列值, 条件 Yii::$app-db-createCommand()-update(user, [status 1], age 30)-execute(); // DELETE表名, 条件 Yii::$app-db-createCommand()-delete(user, status 0)-execute();这三个方法insert()、update()、delete()内部都委托给QueryBuilder生成 SQL再通过bindValues()绑定参数。批量插入batchInsert需要一次性插入多行时可以调用batchInsert()它比逐条 INSERT 高效得多// 表名, 列名数组, 行值数组 Yii::$app-db-createCommand()-batchInsert(user, [name, age], [ [Tom, 30], [Jane, 20], [Linda, 25], ])-execute();对应实现见 Command::batchInsert()它会把多行数据组装成一条多值 INSERT 语句大幅减少网络往返与语句解析开销。补充yii\db\Command还提供了upsert()方法L563它是一条原子操作当记录不存在依据唯一约束匹配时插入存在时则更新。例如upsert(pages, [...新记录...], [visits new \yii\db\Expression(visits 1)])可安全地实现插入新页面或原子地让访问计数 1。注意以上方法insert/update/delete/batchInsert 等只是构建查询必须再调用execute()才会真正执行。表名与列名的转义编写数据库无关代码时正确转义表名和列名往往很麻烦因为不同数据库的转义规则不同MySQL 用反引号、MSSQL 用方括号等。Yii 提供了统一的转义语法[[column name]]用双中括号包裹列名{{table name}}用双花括号包裹表名。Yii DAO 会自动把这些写法转换成对应数据库的正确转义形式。例如// 在 MySQL 上执行的是SELECT COUNT(id) FROM employee $count Yii::$app-db-createCommand(SELECT COUNT([[id]]) FROM {{employee}}) -queryScalar();其底层实现在 Connection::quoteSql()通过正则表达式匹配{{...}}与[[...]]两种构造分别调用quoteTableName()与quoteColumnName()完成转义。而 Schema::quoteTableName() 与 Schema::quoteColumnName() 在不同数据库的 Schema 子类中有各自的实现从而做到同一套语法、各库正确转义。表前缀{{%tableName}} 语法如果你的多数表名共享一个公共前缀可以利用 Yii DAO 的表前缀功能。首先通过tablePrefix属性指定前缀return [ // ... components [ // ... db [ // ... tablePrefix tbl_, ], ], ];然后在代码中引用这类表时使用{{%table name}}语法百分号会自动替换为配置的前缀// 在 MySQL 上执行的是SELECT COUNT(id) FROM tbl_employee $count Yii::$app-db-createCommand(SELECT COUNT([[id]]) FROM {{%employee}}) -queryScalar();前缀替换同样发生在 Connection::quoteSql() 中当花括号内出现%时先执行str_replace(%, $this-tablePrefix, ...)再转义表名。这避免了在代码中到处硬编码前缀也让更换前缀成为一行配置的事。执行事务当你按顺序执行多个相互依赖的查询时为保证数据完整性应当把它们包裹在一个事务中。如果其中任何一条查询出错数据库会回滚到执行这些查询之前的状态。最简洁的写法是使用transaction()闭包Yii::$app-db-transaction(function($db) { $db-createCommand($sql1)-execute(); $db-createCommand($sql2)-execute(); // ... 执行其他 SQL ... });上面的写法等价于下面这段更显式的代码区别在于后者对错误处理拥有更多控制权$db Yii::$app-db; $transaction $db-beginTransaction(); try { $db-createCommand($sql1)-execute(); $db-createCommand($sql2)-execute(); // ... 执行其他 SQL ... $transaction-commit(); } catch(\Exception $e) { $transaction-rollBack(); throw $e; } catch(\Throwable $e) { $transaction-rollBack(); }调用beginTransaction()会启动一个新事务事务由保存在$transaction变量中的yii\db\Transaction对象表示随后查询在try...catch...块中执行。全部成功则调用commit()提交否则捕获异常并调用rollBack()回滚到失败查询之前的状态。注上面代码出于兼容 PHP 5.x 与 PHP 7.x 的考虑使用了两个 catch 块。\Exception自 PHP 7.0 起实现了\Throwable接口若你的应用只使用 PHP 7 及以上版本可以省略\Exception那个块。源码层面Connection::transaction() 会记录进入事务前的嵌套层级$level回调执行成功后仅在事务仍处于活跃状态且层级未变时才提交捕获到异常时按层级回滚并重新抛出。配套的commit()、rollBack()实现在 framework/db/Transaction.php两者都会递减内部层级计数只有最外层_level归零才真正向数据库发出 COMMIT / ROLLBACK。指定隔离级别Yii 支持为事务设置隔离级别。默认情况下启动事务时使用数据库系统自带的默认隔离级别可以通过以下方式覆盖$isolationLevel \yii\db\Transaction::REPEATABLE_READ; Yii::$app-db-transaction(function ($db) { .... }, $isolationLevel); // 或者 $transaction Yii::$app-db-beginTransaction($isolationLevel);Yii 为最常见的四种隔离级别提供了常量定义见 framework/db/Transaction.php#L59-L74常量级别说明READ_UNCOMMITTED最低可能发生脏读、不可重复读和幻读READ_COMMITTED—避免脏读REPEATABLE_READ—避免脏读和不可重复读SERIALIZABLE最高避免上述所有问题除了使用上述常量也可以使用你的数据库系统支持的有效字符串例如在 PostgreSQL 中可使用SERIALIZABLE READ ONLY DEFERRABLE。不同数据库对隔离级别有一些限制需要特别注意某些数据库目前主要是 MSSQL 和 SQLite只允许对整个连接设置隔离级别后续事务即使不指定也会继承同一级别此时可能需要为所有事务显式设置以避免冲突SQLite只支持两种隔离级别READ UNCOMMITTED和SERIALIZABLE使用其他级别会抛出异常PostgreSQL不允许在事务启动前设置隔离级别因此无法在启动事务时直接指定只能通过Transaction::setIsolationLevel()framework/db/Transaction.php#L232-L239内部委托给Schema::setTransactionIsolationLevel()在事务启动后设置。嵌套事务Savepoint如果你的数据库系统支持 Savepoint就可以嵌套使用事务Yii::$app-db-transaction(function ($db) { // 外层事务 $db-transaction(function ($db) { // 内层事务 }); });或者使用更显式的方式$db Yii::$app-db; $outerTransaction $db-beginTransaction(); try { $db-createCommand($sql1)-execute(); $innerTransaction $db-beginTransaction(); try { $db-createCommand($sql2)-execute(); $innerTransaction-commit(); } catch (\Exception $e) { $innerTransaction-rollBack(); } catch (\Throwable $e) { $innerTransaction-rollBack(); throw $e; } $outerTransaction-commit(); } catch (\Exception $e) { $outerTransaction-rollBack(); } catch (\Throwable $e) { $innerTransaction-rollBack(); throw $e; }嵌套事务的底层机制是Transaction::begin()framework/db/Transaction.php#L119在检测到已有活跃事务_level 0时不会真正向数据库发出 BEGIN而是创建 Savepointcommit()与rollBack()也只在最外层才真正提交/回滚内层则释放或回滚到对应 Savepoint。复制与读写分离许多数据库系统支持复制以提升可用性、降低响应延迟数据从主服务器master复制到从服务器slave所有写入必须发生在主服务器而读取可以发生在从服务器上。要利用复制实现读写分离可按如下方式配置yii\db\Connection组件[ class yii\db\Connection, // 主服务器配置 dsn dsn for master server, username master, password , // 从服务器的公共配置 slaveConfig [ username slave, password , attributes [ // 使用较小的连接超时 PDO::ATTR_TIMEOUT 10, ], ], // 从服务器列表配置 slaves [ [dsn dsn for slave server 1], [dsn dsn for slave server 2], [dsn dsn for slave server 3], [dsn dsn for slave server 4], ], ]上述配置定义了一个单主多从的架构读取查询会自动连到其中一个从服务器写入查询则使用主服务器。读写分离是自动完成的// 使用上述配置创建连接实例 Yii::$app-db Yii::createObject($config); // 读取查询连到某个从服务器 $rows Yii::$app-db-createCommand(SELECT * FROM user LIMIT 10)-queryAll(); // 写入查询连到主服务器 Yii::$app-db-createCommand(UPDATE user SET usernamedemo WHERE id1)-execute();信息通过execute()执行的查询被判定为写查询而通过queryXyz()系列query方法执行的查询被判定为读查询。可通过$db-slave获取当前活跃的从服务器连接。从服务器的负载均衡与故障切换Connection组件对从服务器支持负载均衡和故障切换执行第一次读查询时组件会随机挑选一个从服务器尝试连接若该服务器宕机则尝试下一个若所有从服务器都不可用则回退连接到主服务器。如果配置了serverStatusCache服务器状态缓存不可用的服务器会被记住在serverRetryInterval重试间隔内不再尝试。对应逻辑见 Connection::getSlave() 与其调用的openFromPool()首次调用时从slaves池中挑选并打开一个连接enableSlaves为false或回退时直接使用主连接。信息上面配置中从服务器的连接超时被设为 10 秒意味着服务器在 10 秒内无响应即被判定为宕机你可以根据实际环境调整该参数。多主多从配置你也可以配置多个主服务器和多个从服务器[ class yii\db\Connection, // 主服务器的公共配置 masterConfig [ username master, password , attributes [ // 使用较小的连接超时 PDO::ATTR_TIMEOUT 10, ], ], // 主服务器列表 masters [ [dsn dsn for master server 1], [dsn dsn for master server 2], ], // 从服务器的公共配置 slaveConfig [ username slave, password , attributes [ // 使用较小的连接超时 PDO::ATTR_TIMEOUT 10, ], ], // 从服务器列表 slaves [ [dsn dsn for slave server 1], [dsn dsn for slave server 2], [dsn dsn for slave server 3], [dsn dsn for slave server 4], ], ]上述配置定义了 2 个主服务器和 4 个从服务器。Connection组件在主服务器之间同样支持负载均衡与故障切换见 Connection::getMaster()还支持shuffleMasters决定是否随机打乱主服务器顺序。与从服务器不同的是当所有主服务器都不可用时会抛出异常。注意一旦通过masters属性配置了一个或多个主服务器Connection对象自身的其他连接配置属性如dsn、username、password将被全部忽略。事务与主从连接的配合默认情况下事务使用主服务器连接且事务内的所有数据库操作都会走主连接$db Yii::$app-db; // 事务在主服务器上启动 $transaction $db-beginTransaction(); try { // 两个查询都走主服务器 $rows $db-createCommand(SELECT * FROM user LIMIT 10)-queryAll(); $db-createCommand(UPDATE user SET usernamedemo WHERE id1)-execute(); $transaction-commit(); } catch(\Exception $e) { $transaction-rollBack(); throw $e; } catch (\Throwable $e) { $transaction-rollBack(); throw $e; }如果希望在从服务器上启动事务需要显式指定$transaction Yii::$app-db-slave-beginTransaction();强制走主连接有时你需要在主服务器上执行读查询例如刚写入的数据需要立即读到一致的最新值。可以使用useMaster()方法$rows Yii::$app-db-useMaster(function ($db) { return $db-createCommand(SELECT * FROM user LIMIT 10)-queryAll(); });对应实现见 Connection::useMaster()它临时将enableSlaves置为false执行回调结束后恢复原值。也可以直接设置Yii::$app-db-enableSlaves false让所有查询都走主服务器连接。数据库 Schema 操作Yii DAO 还提供了一整套操作数据库结构的 API例如创建新表、从表中删除列等。完整方法清单如下方法作用createTable()创建表renameTable()重命名表dropTable()删除表truncateTable()清空表内所有记录addColumn()添加列renameColumn()重命名列dropColumn()删除列alterColumn()修改列addPrimaryKey()添加主键dropPrimaryKey()删除主键addForeignKey()添加外键dropForeignKey()删除外键createIndex()创建索引dropIndex()删除索引这些方法的使用方式如下// CREATE TABLE Yii::$app-db-createCommand()-createTable(post, [ id pk, title string, text text, ]);上面的数组描述了要创建的列名与列类型。对于列类型Yii 提供了一套抽象数据类型如pk、string、text允许你定义与数据库无关的 Schema——抽象类型会在建表时根据目标数据库转换为对应的具体类型定义例如pk在 MySQL 中变成int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY在 PostgreSQL 中则对应serial primary key。更多类型与参数可查阅createTable()方法的 API 文档。从源码看Command::createTable() 与 Command::createIndex() 都委托给QueryBuilder生成 SQL并且会调用requireTableSchemaRefresh()使相关表的 Schema 缓存失效确保后续getTableSchema()拿到最新结构。读取表结构getTableSchema除了修改 Schema还可以通过连接的getTableSchema()方法获取表的定义信息$table Yii::$app-db-getTableSchema(post);该方法Connection::getTableSchema() → Schema::getTableSchema()返回一个yii\db\TableSchema对象包含表的列、主键、外键等信息。这些信息主要被查询构建器和 Active Record 使用帮助开发者写出与数据库无关的代码。测试与源码验证线索以上行为均有对应的自动化测试覆盖读者可以在仓库中进一步验证与深入学习tests/framework/db/CommandTest.php覆盖queryAll/queryOne/execute/batchInsert等命令执行行为tests/framework/db/ConnectionTest.php覆盖连接创建、事务、读写分离等连接级行为tests/framework/db/QueryBuilderTest.php 与 tests/framework/db/SchemaTest.php覆盖 SQL 生成与 Schema 元数据读取。核心实现位于 framework/db/Connection.php、framework/db/Command.php、framework/db/Transaction.php、framework/db/Schema.php各数据库专属实现见 framework/db 下的mysql/、pgsql/、sqlite/、mssql/、oci/、cubrid/子目录。小结Yii 2 DAO 以 PDO 为底座提供了从连接管理、SQL 执行、参数绑定、名称转义、事务控制到主从复制读写分离的一整套对象化数据库访问能力yii\db\Connection负责连接生命周期与读写路由yii\db\Command负责 SQL 构建与执行yii\db\Transaction负责事务与隔离级别yii\db\Schema系列负责数据库无关的转义与元数据。掌握 DAO 不仅能写出最高效的数据库访问代码也为你理解查询构建器与 Active Record 的底层原理打下坚实基础。若要继续深入学习上层机制可进一步阅读查询构建器与 Active Record 指南。赞分享后端Web框架【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址https://gitcode.com/gh_mirrors/yi/yii2点击查看免费下载相关推荐Yii 2 DAO数据库访问对象完全指南从连接管理、SQL 执行到事务与读写分离Yii 2 DAO数据库访问对象完全指南从连接管理、SQL 执行到事务与读写分离 Yii 2 的 DAODatabase Access Objects后端Web框架Yii 2 DAO 数据库访问对象完全指南连接、查询、事务与读写分离实战Yii 2 DAO 数据库访问对象完全指南连接、查询、事务与读写分离实战 Yii 2 的 DAODatabase Access Objects数据库访问对后端Web框架Yii 2 DAO 数据库访问对象实战从连接管理到读写分离的完整指南Yii 2 DAO 数据库访问对象实战从连接管理到读写分离的完整指南 Yii 2 内置的数据访问层DAODatabase Access Objects构后端Web框架创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价