【问题标题】:How to put records from a query into DAOs?如何将查询中的记录放入 DAO?
【发布时间】:2024-01-14 15:46:01
【问题描述】:

我已经编写了一个网关来从我的数据库中获取结果集。我如何将每一行存储在一个单独的 dao 中,以便我可以进一步操作每条记录?或者我可以直接访问结果集来获取记录吗?

这是我的网关(顺便说一句,我应该将 cfquery 中的条件逻辑写入扩展此网关的单独 cfc 吗?)

<cfcomponent name="MaterialDao" hint="data access object" output="false">
 <cffunction name="init" hint="constructor" access="public" output="false" returntype="MaterialDao">
  <cfargument name="dsn" type="String" required="true" hint="datasource" />
  <cfset variables.instance.dsn = arguments.dsn />
  <cfreturn this />
 </cffunction>

 <cffunction name="readMaterial" hint="read" access="public" output="false" returntype="Query">
  <cfargument name="district" type="String" />
  <cfset var qReadMaterial = "" />
  <cfquery name="qReadMaterial" datasource="#variables.instance.dsn#">
   <cfif StructKeyExists(arguments,"district")>
   SELECT A.NR, A.BEZ, D.BES, D.STA
   <cfelse>
   SELECT A.NR, A.BEZ
   </cfif>
   FROM  DEK AS D INNER JOIN ART AS A
   ON D.NR = A.NR
   WHERE 0=0
   <cfif StructKeyExists(arguments,"district")>
    AND D.BEZ = #arguments.district#
   </cfif>
   ORDER BY A.BEZ
 </cfquery>
 <cfreturn qReadMaterial />
 </cffunction>
</cfcomponent>

我已经阅读了很多文章,似乎对这个问题有不同的看法(DAO vs. Gateway,DAO & Gateway 等)。最佳做法是什么,专业人士会做什么?

【问题讨论】:

  • 我注意到您在 cfif 语句中使用了 # 符号。没有必要使用 # 符号,除非将变量用作某些输出的一部分,例如用于查询、视图或标记的参数,就像您对 datasource="#variables.instance.dsn#" 所做的那样它在引号之间的位置。只是一个小提示,可以为您节省一些打字按键。 :)
  • 我采纳了 Aaron 的提示并将我的网关更改为 dao,以便更符合专业人士:)
  • 您在 StructKeyExist 检查中缺少右括号。
  • 另外,您应该将 cfqueryparam 用于#arguments.district#

标签: coldfusion dao cfc gateway


【解决方案1】:

专业人士只对数据库访问层使用一种模式。 DAO 和网关的使用是一个误称,我不确定它从哪里开始,但似乎只存在于 ColdFusion 人群中。 DAO 和网关模式几乎可以提供相同的功能,但我认为 DAO 更适合谈论数据库交互。

DAO 应包括 CRUD 方法的功能以及返回记录集。由于 CRUD 和基本记录集是高度重复的,因此我使用代码生成器来创建此交互的代码,然后自定义我需要的内容。这是选择所需记录的条件逻辑的好地方。

正如 Aaron 所提到的,由于创建对象的性能开销,在 ColdFusion 中为数据库中的一组记录返回对象数组是不可行的。在我的视图中,我通常只使用从 DAO 返回的基本查询。但是,如果我正在建模的事物需要视图中的某些行为,那么我将使用类似于 Peter Bell 所做的事情将查询放入对象中。

【讨论】:

  • 使用 DAO 与网关 + DAO 纯属品味,但我也只是使用 DAO,喜欢在一个地方对一个对象进行所有数据库交互。
  • 我在dao中添加了一个readAll方法来获得网关的效果(搜索记录)。当我单击(链接)结果(选择记录)时,我创建一个默认 bean 并通过 dao 将来自多个源的数据写入 bean。我关注了 www.inknowkungfoo.com 上的文章
【解决方案2】:

几个月前,Peter Bell 做了一个关于迭代业务对象 CFC 的精彩演示,它允许您使用这个简单的框架获取多条记录并一次迭代一条记录:http://ibo.riaforge.org/。在 CF 生成对象的速度稍微快一点之前,回收对象的单个实例并重新填充属性可能是您最好的选择。也许这可以帮助您一次将一条记录加载到您的 DAO 中。

条件逻辑可以进入网关或管理器 CFC。通常,我会在 CFC 中包含简单的逻辑,就像您在帖子中概述的逻辑一样。

一点建议,您可能希望不再需要 arguments.distinct 并使用 if (structKeyExists(arguments, "distinct") ) { do something } 做一个简单的检查。

问候,

-亚伦·格林利

【讨论】:

  • 默认值(而不是null,CF中不存在)和structKeyExists(arg,"name")有什么区别?
【解决方案3】:

在我们公司,我们花了几个月的时间认真思考这个问题,通过 RDS 和其他一些旧的尝试 Adob​​e CF DAO 创建器(有人记得 CFPowerTools 吗?)。

我们最终决定编写自己的 DAO 代码生成器,我想我会在这里分享我们的想法。我们决定的原因是因为我们需要在 SQL 中添加锁定提示,我们想让它更高效、更安全和更干净。

我们决定的设置是创建一个预定义的基础 DAO 对象(称为DAO.cfc),所有生成的“表”DAO 都扩展了该对象。它只有一些实用方法,但关键是我们可以在其中添加任何其他我们需要所有生成的 DAO 都可以访问的函数。

因此,我们通过从数据库中选择一个表(使用 CF 管理 API)自动生成代码,并使用通常的 init、setter 和 getter 创建 [TableName].cfc DAO,这是基本的 CRUD 内容。

除此之外,我们还生成[TableName]GatewayBase.cfc[TableName]Gateway.cfc[TableName]Gateway.cfc 扩展 [TableName]GatewayBase.cfc

因此,对于在名为“客户”的表上运行的示例 DAO,创建的文件是:

Customers.cfc /* extends DAO.cfc [not created, already exists] */
CustomersGateway.cfc 
CustomersGatewayBase.cfc /* extends CustomersGateway */

因此,这个想法是网关提供了一种处理许多“客户”记录的方法——DAO 用于处理一个且只有一个。网关中的所有方法一般都会返回一个 CF 查询对象。 CF 效率太低,无法创建大量 DAO 对象数组,而且在我们看来,CF 中的查询对象非常灵活,因此我们很乐意使用它。

编码时,子类CustomerGateway.cfc是唯一实例化和使用的。但是,它扩展的基类有一些非常有用的泛型函数是免费提供的,例如getFieldListByProperty(),它基于传递的参数将通过某个属性(即列值)返回某些字段(即表列),所以对于示例:

myGateway.getFieldListByProperty(property="status", value="1", fieldList="customerName,customerID", orderBy="createdOn") />

该调用将返回状态为 1 的所有客户的“customerName”和“customerID”值,按创建日期排序。该代码还针对 SQL 注入进行了强化并经过验证,因此会引发合理的异常。

此功能将提供您对表执行的多记录查询的 99%(我们希望!)。如果您需要更复杂的查询,那么CustomerGateway.cfc 可以为您添加功能。

最后,我们允许您向CustomerGateway CFC 添加函数,因为如果您更改客户表(比如添加一列),您将需要重新创建该表,并且您将覆盖Customers.cfcCustomersGatewayBase.cfc。但是,您的自定义代码(如果有)在子类中是安全的。

无论如何,这可能有点离题,但我认为有人可能会发现我们的经验很有用。

【讨论】: