IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

QxOrm Discussion :

Conseil sur l'implémentation des fonctions spécifiques aux différents SGBD


Sujet :

QxOrm

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    151
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 151
    Points : 49
    Points
    49
    Par défaut Conseil sur l'implémentation des fonctions spécifiques aux différents SGBD
    Hello !

    J'aimerais avoir votre avis sur ce point:
    postgresql propose une fonction spécifique qui s'appelle "ilike" qui est semblable à un like dans une requête mais en insensible à la casse.
    Ma question est comment puis-je faire pour l'utiliser au sein de QxOrm, je comprends bien que l'objectif de QxOrm n'est pas d'implémenter ces fonctions spécifiques, mais du coup, c'est un peu délicat et je préfère demander des avis avant de me décider....

    Plus généralement et idéalement, j'aimerais faire un LIKE insensible à la casse qui soit indépendant du SGBD, mais ma question s'étend également à d'autres fonctions spécifiques au langage comme "UPPER" etc...

    Je pourrais utiliser une requête brute par le biais de QxQuery, mais ça me semble pas terrible car ça m'empêche d'utiliser ce que tu as fait avec la syntaxe des requêtes du type query.where("toto").like("tata%");

    Merci par avance !

  2. #2
    Expert confirmé

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    481
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 481
    Points : 4 238
    Points
    4 238
    Par défaut


    C'est une excellente question
    Et je pense que je vais l'ajouter dans la FAQ si on arrive à faire quelque chose de sympa.

    Je pense que tu peux essayer de surcharger la classe qx::QxSqlQuery pour ajouter tes propres fonctionnalités (spécifiques à un SGBD par exemple).

    Chaque élément SQL doit implémenter l'interface qx::dao::detail::IxSqlElement. Tu peux donc te créer tes propres éléments SQL.
    Par exemple, si on veut implémenter ta fonction ILIKE, on pourrait procéder comme ceci :

    * fichier MySqlElementILIKE.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class MySqlElementILIKE : public qx::dao::detail::IxSqlElement
    {
     
    public:
     
       MySqlElementILIKE(int index) : qx::dao::detail::IxSqlElement(index) { ; }
       virtual ~MySqlElementILIKE() { ; }
     
       virtual QString toString() const;
       virtual void resolve(QSqlQuery & query) const;
       virtual void postProcess(QString & sql) const;
     
    };
     
    typedef boost::shared_ptr<MySqlElementILIKE> MySqlElementILIKE_ptr;
    * fichier MySqlElementILIKE.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    QString MySqlElementILIKE::toString() const
    {
       qAssert((m_lstColumns.count() == 1) && (m_lstKeys.count() == 1));
       QString sColumn(m_lstColumns.at(0)), sKey(m_lstKeys.at(0));
       qAssert(! sColumn.isEmpty() && ! sKey.isEmpty());
       return sColumn + " ILIKE " + sKey;
    }
     
    void MySqlElementILIKE::resolve(QSqlQuery & query) const
    {
       qAssert((m_lstKeys.count() == 1) && (m_lstValues.count() == 1));
       QString sKey(m_lstKeys.at(0));
       QVariant vValue(m_lstValues.at(0));
     
       bool bQuestionMark = (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark);
       if (bQuestionMark) { query.addBindValue(vValue); }
       else { query.bindValue(sKey, vValue); }
    }
     
    void MySqlElementILIKE::postProcess(QString & sql) const
    {
       Q_UNUSED(sql);
    }
    * fichier MySqlQuery.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class MySqlQuery : public qx::QxSqlQuery
    {
     
    public:
     
       MySqlQuery() : qx::QxSqlQuery() { ; }
       MySqlQuery(const QString & sQuery) : qx::QxSqlQuery(sQuery) { ; }
       MySqlQuery(const char * sQuery) : qx::QxSqlQuery(sQuery) { ; }
       virtual ~MySqlQuery() { ; }
     
       MySqlQuery & ilike(const QString & val);
       // ...
       // Ici on peut ajouter toutes les fonctions spécifiques à un SGBD
     
    };
    * fichier MySqlQuery.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    MySqlQuery & MySqlQuery::ilike(const QString & val)
    {
       if (! m_pSqlElementTemp)
       { qDebug("[QxOrm] MySqlQuery : '%s'", "invalid SQL query, need a column name"); qAssert(false); return (* this); }
     
       MySqlElementILIKE_ptr p;
       p.reset(new MySqlElementILIKE(m_iSqlElementIndex++));
       p->clone(m_pSqlElementTemp.get());
       QVariant var = QVariant(val);
       p->setValue(var);
     
       m_lstSqlElement.append(p);
       m_pSqlElementTemp.reset();
       return (* this);
    }
    A présent tu peux utiliser la classe MySqlQuery de la même façon que qx::QxSqlQuery :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    MySqlQuery query;
    query.where("toto").ilike("tata%");
    // ...
    Je n'ai pas testé et j'écris ça un peu vite, tu me diras si ça fonctionne ou non...

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    151
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 151
    Points : 49
    Points
    49
    Par défaut
    Réponse très intéressante !!

    Je vais regarder ça, après réflexion, ce que tu proposes me semble tout à fait bien

    Reste à savoir où je fais ma spécialisation car l'idée est quand même que mon logiciel reste autant compatible que possible avec les autres SGBD.

    Peut être faudrait-il un concept supplémentaire: un genre de propriété fallbackTo(&This::like) qui retombe dans une fonctionnalité plus compatible quand "ilike" n'est pas géré (dans le cas présent: like).

    Qu'en penses-tu ?

  4. #4
    Expert confirmé

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    481
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 481
    Points : 4 238
    Points
    4 238
    Par défaut
    Peut être faudrait-il un concept supplémentaire: un genre de propriété fallbackTo(&This::like) qui retombe dans une fonctionnalité plus compatible quand "ilike" n'est pas géré (dans le cas présent: like).
    Qu'en penses-tu ?
    Attention à ça : tu risques d'avoir des comportements étranges quand un SGBD ne gère pas la fonction spécifique en question, et ça risque d'être compliqué à déboguer.
    Mais bon, tu peux facilement le faire je pense : à toi d'organiser ton code pour le rendre +/- compatible avec les autres SGBD.
    Au lieu de la classe MySqlQuery telle que je te l'ai écrite, tu fais une interface (IMySqlQuery), et les fonctions spécifiques que tu ajoutes, tu les mets en virtual, par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class IMySqlQuery : public qx::QxSqlQuery
    {
     
    public:
     
       IMySqlQuery() : qx::QxSqlQuery() { ; }
       IMySqlQuery(const QString & sQuery) : qx::QxSqlQuery(sQuery) { ; }
       IMySqlQuery(const char * sQuery) : qx::QxSqlQuery(sQuery) { ; }
       virtual ~IMySqlQuery() { ; }
     
       virtual IMySqlQuery & ilike(const QString & val);
       // ...
       // Ici on peut ajouter toutes les fonctions spécifiques à un SGBD
     
    };
    Dans cette interface, par défaut, la méthode ilike() appelle like() de la classe de base (qx::QxSqlQuery).
    Ensuite, la classe MySqlQuery n'hérite plus de qx::QxSqlQuery mais de ton interface IMySqlQuery : tu peux en faire une pour postgres, une pour mysql, une pour oracle, etc...

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    151
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 151
    Points : 49
    Points
    49
    Par défaut
    Ok, ça me semble très bien, je vais analyser ça et le tester.

    Par contre, peux tu m'expliquer un peu ce que fait cette fonction resolve ?
    Et en particulier:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
       bool bQuestionMark = (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark);
       if (bQuestionMark) { query.addBindValue(vValue); }
       else { query.bindValue(sKey, vValue); }
    Merci !!

  6. #6
    Expert confirmé

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    481
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 481
    Points : 4 238
    Points
    4 238
    Par défaut
    peux-tu m'expliquer un peu ce que fait cette fonction resolve ?
    Avant d'envoyer la requête SQL au SGBD, il a 2 phases :
    1- la génération de la chaîne de caractères à envoyer : c'est la méthode toString() de l'interface qui gère ça ;
    2- le binding des valeurs associées aux paramètres de la requête SQL (placeholder) : c'est la méthode resolve() de l'interface qui gère ça.

    Pour cette 2ème phase, QxOrm propose plusieurs syntaxes pour l'écriture des requêtes, c'est détaillé dans la FAQ (http://www.qxorm.com/qxorm_fr/faq.html#faq_210), voici un extrait :
    La bibliothèque QxOrm supporte trois syntaxes pour l'écriture des paramètres SQL.
    Le type de syntaxe peut être modifié de façon globale à un projet en utilisant la méthode suivante : qx::QxSqlDatabase::getSingleton()->setSqlPlaceHolderStyle().
    Les trois paramètres possibles pour cette méthode sont :
    - ph_style_2_point_name : "WHERE author.sex = :sex" (syntaxe par défaut) ;
    - ph_style_at_name : "WHERE author.sex = @sex" ;
    - ph_style_question_mark : "WHERE author.sex = ?".
    Concernant le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
       bool bQuestionMark = (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark);
       if (bQuestionMark) { query.addBindValue(vValue); }
       else { query.bindValue(sKey, vValue); }
    Suivant la syntaxe utilisée, pour binder les valeurs des requêtes SQL, tu as soit une notion d'ordre (si tu utilises "?"), soit une notion clé-valeur (si tu utilises ":zzz", ou "@xxx").

  7. #7
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    151
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 151
    Points : 49
    Points
    49
    Par défaut
    Pour info, je vais également essayer de concevoir l'opérateur UPPER, afin de pouvoir écrire ce type de requete:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT * FROM Patient WHERE UPPER(Patient.name) = :patientLastName
    Ça ne doit pas être trop différent de ce que tu proposes.

  8. #8
    Expert confirmé

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    481
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 481
    Points : 4 238
    Points
    4 238
    Par défaut
    Ça ne doit pas être trop différent de ce que tu proposes.
    En effet, juste la fonction toString() à modifier, quelque chose comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return "UPPER(" + sColumn + ") = " + sKey;

  9. #9
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    151
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 151
    Points : 49
    Points
    49
    Par défaut
    Pour information, ce que tu m'as donné fonctionne très bien, mais il y a quand même un souci.

    Vu que les mots clés or_(...) and_, etc... renvoient une référence vers l'objet qx::SqlQery et non pas ma classe, je suis obligé de faire ma requête en plusieurs temps...

  10. #10
    Expert confirmé

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    481
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 481
    Points : 4 238
    Points
    4 238
    Par défaut
    Vu que les mots clés or_(...) and_, etc... renvoient une référence vers l'objet qx::QxSqlQery et non pas ma classe, je suis obligé de faire ma requête en plusieurs temps...
    Tu peux essayer de télécharger cette version BETA :
    http://www.qxorm.com/version/QxOrm_1.2.5_BETA_06.zip

    Normalement ton compilateur C++ devrait supporter les types de retour covariant (tous les compilateurs "récents" le supportent), donc ça devrait fonctionner.
    Avec cette version BETA, tu changes rien à ton code, et tu ajoutes juste à ta classe :
    * dans le .h :
    QX_SQL_QUERY_DERIVED_IMPL_COVARIANT_RETURN_TYPE_HPP(MyClassName)
    * dans le .cpp :
    QX_SQL_QUERY_DERIVED_IMPL_COVARIANT_RETURN_TYPE_CPP(MyClassName)

    Avec ça, tu pourras écrire ta requête normalement
    Tiens moi au courant stp pour me dire si ça fonctionne bien ou pas...

  11. #11
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    151
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 151
    Points : 49
    Points
    49
    Par défaut
    Je vais tester ça très bientôt ! Merci beaucoup !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 29/10/2006, 18h52
  2. Réponses: 3
    Dernier message: 21/10/2006, 16h03
  3. [1.5][dbutils]Conseil sur mon implémentation ?
    Par elitost dans le forum Langage
    Réponses: 2
    Dernier message: 14/09/2006, 12h43
  4. pointeurs sur les arguments des fonctions?
    Par brunoP dans le forum C
    Réponses: 3
    Dernier message: 14/05/2006, 18h11
  5. Implémentation des fonctions mathématiques
    Par mat.M dans le forum Mathématiques
    Réponses: 9
    Dernier message: 17/06/2002, 16h19

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo