题
我正在构建一个对象来搜索我的数据库中的订单。用户可以设置许多可能的参数,并且可以设置每次搜索所需的数量。我已经创建了setter方法来收集搜索所需的所有参数。
我的问题是这个。什么是“最佳实践”
- 在调用
doSearch
方法时存储参数并构建WHERE
子句 - 在设置参数 时构建
WHERE
子句
醇>
我想了解任何推荐背后的原因。
请注意,对象会针对每次搜索进行实例化,因此我不必担心使用不同参数进行第二次搜索。
解决方案
您应该将订单搜索的代码与构建SQL的代码分开。 SQL应该构建在 OrderSearch <的衍生(或策略衍生物)中。 / code> class。完成这种分离后,构建SQL时 并不重要。
使这更加简单明了。给定一个名为 OrderSearch
的类,它有一堆用于搜索条件的setter方法,你想要一个名为 OrderSearchSQLBuilder
的子类。请注意,子类依赖于基类,并且基类独立于子类。这是非常重要的。这种独立性允许您忽略SQL是在setter方法中还是在搜索方法中构建的。请参阅依赖性倒置原则(DIP)。
一旦你有这种分离,你可以用其他策略替换衍生物。例如,如果您想在不将其连接到SQL数据库的情况下测试应用程序,则可以创建一个虚拟的in-ram数据库并创建处理该虚拟数据库的 OrderSearch
的衍生物。应用程序的其余部分将无法意识到,您的测试将独立于数据库连接,预先存在的数据等的恐怖。
其他提示
在您的方法中,只需使用动态SQL中的参数进行搜索。这样,where子句就在SQL运行之前构建。您只需将搜索参数作为参数传递给方法。
像这样......
<cffunction name="getByAttributesQuery" access="public" output="false" returntype="query">
<cfargument name="id" type="numeric" required="false" />
<cfargument name="userName" type="string" required="false" />
<cfargument name="firstName" type="string" required="false" />
<cfargument name="lastName" type="string" required="false" />
<cfargument name="createdAt" type="date" required="false" />
<cfargument name="updatedAt" type="date" required="false" />
<cfargument name="orderby" type="string" required="false" />
<cfset var qList = "" />
<cfquery name="qList" datasource="#variables.dsn#">
SELECT
id,
userName,
firstName,
lastName,
createdAt,
updatedAt
FROM users
WHERE 0=0
<cfif structKeyExists(arguments,"id") and len(arguments.id)>
AND id = <cfqueryparam value="#arguments.id#" CFSQLType="cf_sql_integer" />
</cfif>
<cfif structKeyExists(arguments,"userName") and len(arguments.userName)>
AND userName = <cfqueryparam value="#arguments.userName#" CFSQLType="cf_sql_varchar" />
</cfif>
<cfif structKeyExists(arguments,"firstName") and len(arguments.firstName)>
AND firstName = <cfqueryparam value="#arguments.firstName#" CFSQLType="cf_sql_varchar" />
</cfif>
<cfif structKeyExists(arguments,"lastName") and len(arguments.lastName)>
AND lastName = <cfqueryparam value="#arguments.lastName#" CFSQLType="cf_sql_varchar" />
</cfif>
<cfif structKeyExists(arguments,"createdAt") and len(arguments.createdAt)>
AND createdAt = <cfqueryparam value="#arguments.createdAt#" CFSQLType="cf_sql_timestamp" />
</cfif>
<cfif structKeyExists(arguments,"updatedAt") and len(arguments.updatedAt)>
AND updatedAt = <cfqueryparam value="#arguments.updatedAt#" CFSQLType="cf_sql_timestamp" />
</cfif>
<cfif structKeyExists(arguments, "orderby") and len(arguments.orderBy)>
ORDER BY #arguments.orderby#
</cfif>
</cfquery>
<cfreturn qList />
</cffunction>
在执行搜索之前,不要构建where子句。您最终可能会在迭代中提供参数的用户界面,并且您不知道何时拥有所有内容。此外,您可能永远不会执行搜索,所以为什么要担心where子句。
我认为它没有多大区别,但我认为在doSearch时构建WHERE子句似乎更好。我不认为设置器的责任在于将参数添加到某处的WHERE子句字符串。
在SQLServer数据库上,将所有参数包含在where子句中而不是动态构建它是更有效的。然后有一个数据库索引,其中包含您要搜索的所有列。这可确保在执行语句时始终使用索引。
听起来你的抽象是不对的。
搜索可能更好地作为“订单”的方法。宾语。将参数传递给搜索功能并手动构建查询,如 russ建议。然后可以在订单init方法上设置任何特定于订单而不是搜索的内容。
您可能想要构建订单搜索对象,但这应该通过订单对象完成,以保持前端代码简单。
选项1是您最好的选择。选项2听起来很危险。如果参数更新怎么办?如何在WHERE子句中替换它?
我会做这样的事情:
<cffunction name="doSearch" access="public" output="false" returntype="query">
<cfset var qList = "" />
<cfquery name="qList" datasource="#variables.dsn#">
SELECT
...
FROM
...
WHERE 0=0
<cfif len(getID())>
AND id = <cfqueryparam value="#getID()#" CFSQLType="cf_sql_integer" />
</cfif>
<cfif len(getUserName())>
AND userName = <cfqueryparam value="#getUserName()#" CFSQLType="cf_sql_varchar" />
</cfif>
</cfquery>
<cfreturn qList />
</cffunction>