为什么 FleaPHP 使用 Table Data Gateway 代替 Active Record 来提供数据库访问服务

2023华为OD面试手撕真题【最长公共前缀】 阅读详情

[原文地址:http://www.dualface.com/blog/?p=357]

  许多开发者很疑惑为什么 FleaPHP 以高效开发为目标,却没有提供 Active Record 模式。本文尝试详细阐述这个问题。

Active Record 是什么?

  Active Record 模式中文名为“活动记录”,在《企业应用架构模式》(PoEAA)一书中定义如下:

活动记录(Active Record):一个对象,它包装数据库表或视图中的某一行,封装数据库访问,并在这些数据上增加了领域逻辑。

  举个例子来说,一个图书数据表,每一条记录就是一本图书的信息。那么采用 Active Record 时,每一本图书就是一个 Active Record 对象实例。

Active Record 因 Ruby On Rails 而流行

  Active Record 之所以现在这么炙手可热,甚至许多人将 Active Record 和 ORM 划等号,完全是 Ruby On Rails 的原因。

在 Ruby On Rails 中,Active Record 除了最基本的将数据记录和一个对象互相映射外,还提供了数据(而不是对象)间关联关系的处理。例如:

  一本图书有一个或者多个作者,所以每一个图书对象都和多个作者对象关联。反过来一个作者可以写多本书,所以一个作者对象也和多个图书对象关联。

  在 RoR 中,我们获取一个图书对象时,自动就获得了该图书对象所对应的作者对象(本质上是图书数据对应的作者数据)。更进一步,通过图书对象关联的作者对象,我们 可以获取该作者所写的所有图书的对象实例。而这些工作,在 RoR 中只需要几行代码而已,以前我们需要写上一大段代码才能实现同样的效果。

  RoR 中,对 Active Record 模式的实现完全利用了 Ruby 语言的灵活性,简短几行代码就可以定义一个关联。并且通过复杂的 ActiveRecord:Base 对象,提供了 CRUD(创建、读取、更新、删除)操作的默认处理。所以使用 RoR 时,绝大部分常见的数据库操作只需要很少量的代码就可以完成,大大提高了开发效率。

  但 Active Record 模式也不是完美的,Active Record 存在不少缺点。

  • Active Record 模式需要数据表结构和对象属性一一对应(至少是大部分对应),否则将难以使用 Active Record 模式;
  • Active Record 模式并不能够真正适合完全面向对象的应用程序。因为 Active Record 模式本质上就要求一个对象必须和一个数据表对应。但在完全面向对象的应用程序中,数据和操作数据的方法很可能分布在各个不同的对象中,这些对象却并没有和 某一个数据表完全对应,而且 Active Record 无法很好的处理对象的继承、聚合等面向对象常见的对象间关系;
  • 随着逐渐向 Active Record 添加业务逻辑,Active Record 对象中会混入越来越多的 SQL 语句,这在更复杂的项目中显然是一个不利因素。

  如果在 Active Record 模式中添加了对数据关系(注意,不是对象关系)的处理,那么还要注意性能问题:
  假如一个 Active Record 对象有多个关联。那么我取出一个对象时,很可能就连带取出了其他不少对象。但这些对象可能根本就是本次操作用不上的。其次,将对象更新到数据库时,也需要对关联的对象进行处理,否则对关联对象的修改就会丢失。

  虽然可以用各种技巧来避免这些情况,但毫无疑问需要开发者对 RoR 的 Active Record 很熟悉才行。否则看上去很简单的代码,背后则会是噩梦般的数据库操作。

  其次,假设我们要将数据库中每本书的单价减半,那么采用 Active Record 模式时,就必须首先读取所有的记录并实例化为对象,然后更新对象属性,再写回数据库。可想而知这样会有多差的效率。
  当然了,实际开发中没有人会这样做。开发者会编写一个单独的方法,用一条 SQL 语句完成对批量数据的更新。但也说明 Active Record 模式不适合批量处理数据,而现实世界中,批量处理数据的需求随处可见。

  不过由于 RoR 对开发效率戏剧性的提高,所以对于追求开发效率的项目,RoR 是一个很不错的选择。而且性能上的不足可以通过更新硬件或者配合其他技术手段来改善(例如 FastCGI 通常是运行 RoR 应用的首选)。因此在现实世界中,37signals.com 公司的所有基于 RoR 开发的应用,都获得了良好的性能表现(但是同等的硬件,跑 PHP 开发的同样功能应用是更好还是更差呢?这个问题没有答案)。

Active Record 与 ORM

  许多人将 Active Record 与 ORM 划等号,这是错误的。ORM(对象关系映射)是将对象及对象间的关系(继承、聚合等)映射到关系式数据库中。由于面向对象和关系式数据库天生的不匹配,所以这种映射是相当复杂的。

  而 Active Record 原本只是将一个数据行记录包装为一个对象,只是在 RoR 中由于添加了对关系的处理,而具有了一些 ORM 的特征。所以可以简单的将 RoR 中的 Active Record 看作 ORM 的一种实现方式。但本质上,RoR 中的 Active Record 是处理数据间的关系而不是对象间的关系(但支持对象继承),因为每一个 Active Record 对象都是和数据表一一对应的。

  那为什么在 Java 世界中,没有大量采用 Active Record 模式呢?

  在 Java 世界中,绝大部分 ORM 都是作为中间件存在的。由于 Java 与 Ruby、PHP 等脚本语言截然不同的运行机制。所以即便是很复杂的中间层,只要能够在运行时提供良好的性能,那就能够被开发者接受。而 Hibernate 这样的 ORM 中间件能够提供比 Active Record 多得多的功能和灵活性,所以 Active Record 模式在 Java 世界不受欢迎就可以理解了。

  而在 .NET 世界中,大量使用的都是表数据入口(Table Data Gateway)和表模块(Table Module)。这两种模式由于有 Microsoft 出色的 IDE 支持,所以能够获得很高的开发效率,自然 .NET 开发者对 Active Record 模式也不感兴趣了。

如果将 Active Record 或者 ORM 照搬到 PHP 中呢?

  许多开发者都很羡慕 Hibernate 的强大功能和 RoR 中 Active Record 的快速开发能力,但是这些东西如果照搬到 PHP 中,会遇到一个相当大的麻烦:

  PHP 本质上是解释执行的脚本语言,所以对于每一次 HTTP 请求,PHP 执行环境都会将请求的 .php 文件编译为 opcode,然后执行 opcode,再清理所有的资源(内存、数据库连接、文件句柄等等)。在这种环境中,应用程序应该花尽可能少的时间去初始化底层框架,而是把大部分资源用 在业务逻辑的执行上。

  但 Ruby 也是解释执行,为什么就可以用 Active Record,而 PHP 就不应该呢?
简单点说就是因为 PHP 在面向对象支持上的缺陷使得要实现和 RoR 同等功能的 Active Record 模式变得非常艰难。也许你对此不以为然,那么可以实际尝试一下使用 PHP on Trax(一个 RoR 的 PHP 克隆)。看看一次简单的读取操作需要载入多少文件并调用多少对象和方法。

  所以有些 PHP 框架提供的 Active Record 模式实现非常简单,根本不考虑关联问题,但这样一来使用 Active Record 能获得的开发效率提升就太小了。

  至于更为复杂的 ORM,目前 PHP 领域还没有一个真正的成功项目。虽然 Propel 是目前 PHP 领域唯一一个具有实际工作能力的 ORM。但由于其自身的复杂性和执行效率问题,一直没有得到广泛使用。即便是 Symfony 也是对 Propel 进行裁剪后才用于处理数据库操作。

虽然在国内 PHP 社区中常看到有人说自己做的 ORM 如何如何先进,既有高级特征,又有好的效率。但自始至终没有看到过有人公布代码。至于不公布的原因不外乎:还不够成熟,成熟后再公布;我是最领先的,除非 有了同水平的,不然我不会公布;商业产品,不能泄露。而且别说是代码,就算问问实现原理通常也只能得到几句无关痛痒的回答。

所以如果你看到这篇文章后,觉得你实现了我认为很难实现的东西,请拿出实际证据。不要再搬出诸如此类的理由,没有论据的辩论是毫无意义的。

  那么 PHP 就注定和 Active Record 和 ORM 无源吗?
如果这个问题的潜在意思是问:PHP 就不能找到和 Active Record 一样好用的数据库访问方法吗?那么答案是否定的。

Table Data Gateway 是一个更合理的选择

  我仔细研究了 PoEAA 中关于表数据入口、表模块的内容后,又做了大量实际测试。最终决定在 FleaPHP 中采用 Table Data Gateway(表数据入口)模式来提供数据库服务。并在此基础上实现对关联数据的自动处理。

表数据入口(Table Data Gateway):充当数据表访问入口的对象,一个实例处理表中所有的行。

表模块(Table Module):处理某一数据库表或视图中所有行的业务逻辑的一个实例。

  表数据入口是封装一个数据表的操作,而不是一个记录行。这样一来,表数据入口可以很方便的处理针对单个记录和多个记录的操作,而操作的数据就是 PHP 中的数组。实际上我初期还写了一些对象来封装记录集(也就是多行记录),不过后来发现完全是多此一举。PHP 的数组功能非常强大,再专门用对象包装一下弊大于利。

  针对数据表提供单纯的 CRUD 操作吸引力还不够,所以我在表数据入口的基础上增加了对 HasOne、HasMany、ManyToMany 以及 BelongsTo 关联的处理。这四种关联,基本上满足了常见的数据关联操作。

  不过有了自动化的关联,类似 RoR ActiveRecord 中加载过量数据的问题依然存在,所以 FleaPHP 的表数据入口对象 FLEA_Db_TableDataGateway 也提供了针对关联的方法,让开发者可以细粒度的控制数据库操作。

  而且由于表数据入口是针对纯数据进行操作,而不是针对包装了数据的对象。所以开发者可以很容易的优化数据库操作,例如无需读取即可更新数据或者一次性处理大批量的数据。

  相对于 Active Record 模式,Table Data Gateway 模式有下列优势:

  • 表数据入口针对一个表封装数据库操作,这更接近传统 PHP 开发的思维模式;
  • 处理批量数据时,表数据入口更方便,常见操作无需额外编写处理方法;
  • 数据以数组的形式保存和传递,比将每个记录行实例化为对象具有好得多的性能;
  • 实现比 Active Record 简单,每个操作执行更少的代码;
  • 可以很好的与表模块(Table Module)模式配合来封装业务逻辑。从而避免了 Active Record 中将数据库操作和业务逻辑写在一起的问题。

  当然,表数据入口也有相对于 Active Record 不足的地方:

  • 由于表数据入口总是传递纯数据,所以无法像 Active Record 一样以属性的形式封装对数据的操作。不过这种操作即便使用 Active Record 也要多写不少处理代码,而使用表数据入口时,这部分代码只不过是转移到了表模块中;
  • 看上去更没有那么面向对象。可惜的是即便采用 Active Record,大多数应用程序从设计思想上也不是面向对象的,只不过用了一个对象来传递数据而已。

  而且 Active Record 存在的一些问题,Table Data Gateway 依然无法避免。最主要的就是表数据入口和表模块都是和数据表一一对应,因此不适用于持久化细粒度对象。不过熟悉 .NET 的开发者应该很容易找到解决办法,那就是以表模块完成大部分业务操作,而细粒度对象仅用于部分操作。这是因为 Microsoft 的开发环境一向都对表数据入口和表模块有着偏好和最好的支持。

  不过使用表数据入口,相对于 Active Record 最大的好处就是能够很容易的将业务逻辑操作从表数据入口对象分离到表模块对象中,因此对于更大更复杂的项目,表数据入口配合表模块的方式具有更高的可维护性。

表数据入口和表模块的配合

  表数据入口封装了针对数据表的操作,而表模块则封装了针对数据表的业务逻辑,两者怎么配合呢?我们就以操作图书记录为例,看看具体如何做。

  首先,从 FLEA_Db_TableDataGateway 派生一个类,作为图书表的表数据入口对象,例如 TableBooks。接下来建立一个空白的类,名为 ModuleBooks。

  1. class TableBooks extends FLEA_Db_TableDataGateway
  2. {
  3.     // 只需要指明数据表名称和主键字段名即可,CRUD 操作已经有了默认实现
  4.     var $tableName = 'books';
  5.     var $primaryKey = 'book_id';
  6. }
  7.  
  8. class ModuleBooks
  9. {
  10.     var $table;
  11. }

  现在我们要统计指定年份的出版的图书。

Step1:在 TableBooks 中增加一个方法 countBooksRange():

  1. class TableBooks extends FLEA_Db_TableDataGateway
  2. {
  3.     ......
  4.    
  5.     /**
  6.      * 统计指定时间区间的图书总数
  7.      */
  8.     function countBooksRange($begin, $end)
  9.     {
  10.         // 对参数进行转义,确保不会存在 SQL 攻击漏洞
  11.         $begin = $this->_dbo->qstr($begin);
  12.         $end = $this->_dbo->qstr($end);
  13.         return $this->findCount("publish_date >= {$begin} AND publish_date <= {$end}");
  14.     }
  15. }

  countBooksRange() 方法可以统计指定区间的图书总数,所以我们再给 ModuleBooks 增加一个 countBooksByYear() 方法来统计指定年份的图书。

  1. class ModuleBooks
  2. {
  3.     ......
  4.    
  5.     function countBooksByYear($year)
  6.     {
  7.         $begin = date("{$year}/1/1");
  8.         $end = date("{$year}/12/31");
  9.         return $this->table->countBooksRange($begin, $end);
  10.     }
  11. }

  上面的例子虽然简单,但是很清晰的描述了表数据入口如何封装具体的数据库操作,而表模块又如何利用表数据入口的方法提供更高层的接口。如果需要 可运行的示例程序,可以参考 FleaPHP 的 SHOP 示例。这个示例中,Model 目录下就是表模块,而 Table 目录下就是表数据入口。

 

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1442087


phpmyadmin爆破程序 phpmyadmin爆破程序 自己选择自己的字典文件 立即下载

相关推荐

dgw:dgw从PostgreSQL表元数据生成Golang结构和简单的TableRow Data Gateway函数

dgw 描述 dgw从PostgreSQL表元数据生成Golang结构和简单的表/行数据网关功能。 受到极大启发。 为什么创建 就个人而言,当将Go与RDBMS一起使用时,我更喜欢表/行数据网关而不是ORM /查询构建器方法。 但是,编写很多列,查询占位符和结构字段非常耗时,繁琐且容易出错,即使对于简单的select / insert语句,这些字段也必须完全正确。 dgw生成Go结构,并使用简单函数从PostgreSQL表定义中获取/创建行,从而避免手动编写简单但乏味SQL。 dgw可以正确检测自动生成的列(例如,串行,bigserial),并合成主键以创建适当SQL。 dgw能够使用to

FleaPHP 使用 Table Data Gateway 代替 Active Record提供数据库访问服务

许多开发者很疑惑为什么 FleaPHP 以高效开发为目标,却没有提供 Active Record 模式。本文尝试详细阐述这个问题。 Active Record 是什么?  Active Record 模式中文名为“活动记录”,在《企业应用架构模式》(PoEAA)一书中定义如下:    活动记录(Active Record):一个对象,它包装数据库表或视图中的某一行,封装数据库访

phphot 2057

McgsPro 3.3.2.5166.rar

国产触摸屏MCGS新版屏编程软件,McgsPro 3.3.2.5166;功能强大,使用便捷,编程简单。

fleaphp之dump()

     dump()的功能主要是实现打印数组 。没有再次看源码之前,先自己手写它的实现。   dump($var) { if(is_array($var))  {echo $var."{" foreach($var as $v){   if(!is_array($v)){  $conten=.$v;  }else{dump($v);  }    }   echo "}";   }else   {

a1031031032的专栏 352

架构模式数据源模式之:表数据入口(Table Data Gateway)、行数据入口(Row Data Gateway)、活动记录(Active Record)...

一:表数据入口(Table Data Gateway) 表数据入口提供了用于访问单个表或者视图(也包含了联表查询)的所有SQL,通常一个表一个类。其它代码通过它来实现对数据库的交互。基于这个特点,表数据入口和事务脚本代码以及表模块结合的很好。 在查询时候,表数据接口可以返回数据集 或者 DTO 或者 DTO列表。在 事务脚本 代码中已经阐述过了 DTO 以及 DTO 的列表这种形式。但是使用 ...

weixin_33938733的博客 421

Castle Active Record for .NET2.0快速入门示例

一.创建Web工程 创建一个Web站点或者Web应用程序,添加对Castle.ActiveRecord.dll的引用。 二.创建需要持久化的业务实体 在.NET2.0下,由于引入了泛型,创建业务实体比1.1下简单了许多,业务实体只需要继承于泛型的ActiveRecordBase类,其中默认已经实现了一些静态的方法,不需要我们再在业务实体中实现。 [ActiveRecord("Emplo...

weixin_34367257的博客 153

为什么FleaPHP使用Table Data Gateway代替Active Record提供数据库访问服务

许多开发者很疑惑为什么 FleaPHP 以高效开发为目标,却没有提供 Active Record 模式。本文尝试详细阐述这个问题。Active Record 是什么?  Active Record 模式中文名为“活动记录”,在《企业应用架构模式》(PoEAA)一书中定义如下:    活动记录(Active Record):一个对象,它包装数据库表或视图中的某一行,封装数据库访问,并在这些数据上增加

phphot 1999

Active Record 设计模式原理及简单实现

转载自:http://blog.csdn.net/fanhengguang_php/article/details/54964490 概述 本文简要介绍Active Record 设计模式Active Record 是一种数据访问设计模式,它可以帮助你实现数据对象Object到关系数据库的映射。 应用Active Record 时,每一个类的实例对象唯一对应一个数据库表的一行(一对一关系)...

glowd的专栏 1003

Active Record和Domain Object + Dao

Martin Fowler的 Active Record pattern实现,它是指一个既包含数据又包含行为的对象,这些数据需要持久保存到对应的数据表中。Active Record一个很明显的特征是:将数据访问逻辑也包含在这个domain对象中,通过这种办法让人们可以知道如何从数据库读写数据。如下图: Active Record,也就是将Dao中对数据库的CRUD方...

weixin_33699914的博客 132

微软放缓.NET动态语言开发计划,徐汇区网站设计

微软曾经作出承诺,开发能运行在.NET环境的动态语言实现,例如Ruby语言实现IronRuby和Python语言实现IronPython。 当前IronRuby项目有点不妙,核心开发者,项目经理Jimmy Schementi宣布离职, 在他的博客中描述到:公司对IronRuby项目严重不承诺,严重限制了团队的灵活性,限制项目无法提供具有VisualStudio IronRuby的整合,以及种种理...

weixin_34007879的博客 128

Castle ActiveRecord学习实践(9):使用ActiveRecord的一些技巧

摘要:本文将会介绍使用ActiveRecord中的一些技巧。   主要内容 1.由实体类生成数据表 2.运行存在的SQL脚本 3.使用空属类型 4.使用枚举类型的属性 5.使用NHibernate中的日志记录   一.由实体类生成数据表 在前面所用到的例子中我们都是先有数据表结构,然后才有实体类,然而这会让很多朋友认为ORM怎么变成了ROM了,其实这只是我们平时的一个开发时的习...

weixin_34259232的博客 202

Castle ActiveRecord学习实践(1):快速入门指南

摘要:最近几天有时间看了一下Castle,原来它的功能是如此的强大,从数据访问框架到IOC容器,再到WEB框架,基本包括了整个开发过程中的所有东西,看来得好好学习研究一下了,并且打算把自己学习过程的一些东西记录下来。先从ActiveRecord开始吧,ActiveRecord提供的简洁的O/R映射给我留下了很深的印象,本文将通过一个简单对象的CRUD操作来带你快速走进Castle ActiveRe...

weixin_34347651的博客 270
上一篇: 不一样的敏捷开发实践
下一篇: 获取和安装 FleaPHP
itkbase
博客等级 码龄19年 112粉丝 4647原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值