2009-03-06 7 views
3

Je construis un objet pour rechercher des ordres dans ma base de données. Il y a un tas de paramètres possibles que l'utilisateur peut définir, et ils peuvent en définir autant que souhaité pour chaque recherche. J'ai créé des méthodes setter pour collecter tous les paramètres nécessaires à la recherche.Paramètres individuels ou construire où Clause

Ma question est la suivante. Quelle serait la « meilleure pratique »

  1. Stockage des paramètres et la construction de la clause WHERE lorsque la méthode est appelée doSearch
  2. Construire la clause WHERE que sont définies

paramètres Je voudrais comprendre la raison derrière toute recommandation.

Notez que l'objet est instancié pour chaque recherche, donc je n'ai pas à m'inquiéter d'une seconde recherche avec des paramètres différents.

Répondre

13

Vous devez séparer le code de votre recherche d'ordre à partir du code qui construit le SQL. Le SQL doit être construit dans une dérivée (ou dérivée Strategy) de la classe OrderSearch.Une fois que vous avez fait cette séparation, il n'a pas vraiment d'importance lorsque vous construisez le SQL.

Pour rendre cela un peu plus simple. Étant donné une classe nommée OrderSearch qui a un tas de méthodes de définition pour les critères de recherche, vous souhaitez avoir une sous-classe nommée OrderSearchSQLBuilder. Notez que la sous-classe dépend de la classe de base et que la classe de base est indépendante de la sous-classe. C'est très important. Cette indépendance vous permet de ne pas tenir compte du fait que le SQL est intégré dans les méthodes setter ou dans la méthode de recherche. Voir The Dependency Inversion Principle (DIP). Une fois que vous avez ce genre de séparation, vous pouvez remplacer la dérivée par d'autres stratégies. Par exemple, si vous souhaitez tester votre application sans la connecter à la base de données SQL, vous pouvez créer une base de données in-ram factice et créer une dérivée de OrderSearch qui traite cette base de données factice. Le reste de l'application serait parfaitement inconscient, et vos tests seraient alors indépendants des horreurs des connexions à la base de données, des données préexistantes, etc.

1

Ne construisez pas la clause where avant d'avoir besoin d'exécuter la recherche. Vous pouvez vous retrouver avec une interface utilisateur qui alimente les paramètres en itérations, et vous ne savez pas quand vous avez tout. En outre, vous ne pouvez jamais exécuter la recherche, alors pourquoi s'inquiéter de la clause where.

0

Sur une base de données SQLServer, il est plus efficace d'inclure tous les paramètres dans la clause where plutôt que de les construire à la volée. Ensuite, ayez un index de base de données qui inclut toutes les colonnes que vous chercherez. Cela garantit que l'index est toujours utilisé lors de l'exécution de l'instruction.

3

Dans votre méthode, utilisez simplement les paramètres de votre SQL dynamique qui effectue la recherche. De cette façon, la clause where est construite juste avant l'exécution du SQL. Vous passerez simplement les paramètres de recherche dans votre méthode en tant qu'arguments.

Quelque chose comme ça ...

<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> 
+0

Supprimez les contrôles len (...) - ils ne devraient pas être présents. –

+0

Je dois définir les paramètres de la recherche avant de faire la recherche proprement dite, donc je ne peux pas simplement passer tous les paramètres à la méthode doSearch. Je pense aussi qu'en fournissant des méthodes setter en dehors de la méthode doSearch me donne un ordre plus flexible objectSearch – Yisroel

+0

>> Je dois définir les paramètres de la recherche avant de faire la recherche réelle, donc je ne peux pas simplement passer tous les paramètres dans le doSearch méthode << Huh? Pas même avec quelque chose comme doSearch (ArgumentCollection = Variables.SearchData)? –

1

Je ne pense pas que cela fasse beaucoup de différence, mais je pense qu'il semble une meilleure pratique de construire la clause WHERE lorsque vous doSearch. Je ne pense pas qu'il devrait être la responsabilité d'un setter pour un paramètre à ajouter à une chaîne de clause WHERE quelque part.

0

Vous n'avez pas l'impression que votre abstraction est juste.

La recherche peut être meilleure comme méthode d'un objet "orders". passez les paramètres à la fonction de recherche et créez la requête manuellement en tant que russ suggested. Tout ce qui est spécifique aux commandes plutôt qu'à la recherche peut ensuite être défini sur la méthode orders orders.

Il est possible que vous souhaitiez créer un objet de recherche de commandes, mais cela devrait être fait via un objet de commande, pour garder votre code frontal simple.

+0

Quelle fonctionnalité fournit l'objet Orders? – Yisroel

0

L'option 1 est votre meilleur pari. L'option 2 semble dangereuse. Que faire si un paramètre est mis à jour? Comment le remplacez-vous dans votre clause WHERE?

je ferais quelque chose comme ceci:

<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> 
+0

Je n'ai pas à m'inquiéter de la mise à jour d'un paramètre, car une nouvelle commande est créée pour chaque recherche. En substance, je fais exactement ce que vous faites ci-dessus. Je voulais juste une compréhension claire de ce qui ne va pas avec l'autre option. btw, vous appelez getUserName(), mais référencez ensuite arguments.userName – Yisroel