yii2的数据库层设计

Yii2在M层的抽象做的很经典,值得分析理解。

QueryBuilder

不同的数据库引擎(mysql,oracle…)执行相同的SQL,可能语法有一些细微的差异,但是SQL整体语法差别不大,所以会定义一个QueryBuilder基类,它提供一般通用的SQL生成方法,具体每个数据库引擎继承QueryBuilder,如果存在细微差异只需覆盖特定的方法既可。

简单说,QueryBuilder用来构造SQL语句(读SQL和写SQL都可以),但是并不执行它,通过调用它的where,groupBy,orderBy,select,insert等方法传入结构化的语句组成部分,最终由QueryBuilder返回一个字符串的SQL语句。

Query

仅提供查询能力,不支持修改,它也提供where,groupBy,select等方法(注意不支持insert,update这种修改操作),但是它本身没有生成SQL的能力,仅仅是在QueryBulder之上提供一个通用层,最终在调用它的one或者all方法时会将SQL的各个组成部分一次性交给QueryBuilder构造出SQL语句,那么Query对象怎么知道用哪个QueryBuilder呢?

Query对象会通过application->get(“db”)拿到配置的数据库连接connection对象,这个connection对象自然是知道自己连接的是哪种数据库引擎,因此可以通过它拿到对应的queryBuilder对象。具体来说,connection对象创建时会从配置中取到这样的配置:

Connection对象根据dsn知道对应的数据库引擎,从而创建对应的schema对象:

而这个Schema对象是每个数据库引擎各自实现的,它们有一个方法可以返回对应的QueryBuilder对象:

但是需要注意,虽然经过QueryBuilder生成了SQL,但是Query并不是自己直接执行这个SQL,而是将这个任务交给了下面这个类。

Command

这个类需要由connection对象创建的,内部保存了从connection对象取来的pdo数据库连接。command类的定位是直接执行SQL,也提供bind参数的能力,它不再限制SQL是读还是写,这也是为什么Query类不直接使用connection的pdo连接执行读SQL而是把构造来的SQL交给command,因为command类才是负责执行所有SQL的合适角色,职责集中在Command对象是一个合理的设计。

根据这个认识,我们知道Command类可以直接执行裸SQL,读和写都是可以的。

ActiveQuery

之前的Query算一个调度者,它从用户收集查询的组成要素,交给QueryBuilder生成SQL,最后交给Command完成SQL的执行,拿回来的结果都是数组形式的一行一行的数据。

ActiveQuery则是在Query基础上(继承自Query),额外提供了ORM能力,主要包含2点:数据库行映射为对象(就是指ActiveRecord,也是一个model),以及表的关联关系。

我们知道Query的one和all都是返回关联数组形式的结果集,而ActiveRecord则覆盖了这两个方法,为每一行数据生成对应的activeRecord对象并把列值填充到对象里,这样就实现了数据库行和程序对象的自动映射,大概就是这种关系了。

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~