Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

[BCB 6 : TSQLQuery] Simplification de l'utilisation de cette classe ?

4 réponses
Avatar
Stephane Wirtel
Bonjour,

J'ai posté ce message sur www.developpez.com, mais je n'ai pas eu de
réponses intéressantes.

En gros, pour ceux qui developpe avec Borland C++ Builder 6, TSQLQuery
est une classe permettant de lancer des queries SQL sur une DB ( suffit
de lire le nom de la classe :d ).

Je me suis demandé si quelqu'un avait déjà essayé d'écrire un wrapper au
dessus de TSQLQuery afin de simplifier son utilisation.

Alors, est-ce que quelqu'un a déjà tenté l'aventure ?

Bonne journée,

Stéphane

4 réponses

Avatar
alex
bonjour,

"Stephane Wirtel" a écrit dans le message de
news: fcam32$enp$
Bonjour,

J'ai posté ce message sur www.developpez.com, mais je n'ai pas eu de
réponses intéressantes.

En gros, pour ceux qui developpe avec Borland C++ Builder 6, TSQLQuery est
une classe permettant de lancer des queries SQL sur une DB ( suffit de
lire le nom de la classe :d ).

Je me suis demandé si quelqu'un avait déjà essayé d'écrire un wrapper au
dessus de TSQLQuery afin de simplifier son utilisation.

Alors, est-ce que quelqu'un a déjà tenté l'aventure ?

la classe est déjà très simple d'utilisation il me semble.

Perso dans mes applis j'utilise une classe autour pour fixer le "database"
mais rien de plus.
Tu peux préciser un peu qu'est-ce que tu veux simplifier dans l'utilisation
de TQuery ?

Avatar
Stephane Wirtel
la classe est déjà très simple d'utilisation il me semble.
Perso dans mes applis j'utilise une classe autour pour fixer le "database"
mais rien de plus.
Tu peux préciser un peu qu'est-ce que tu veux simplifier dans l'utilisation
de TQuery ?
Un bête exemple d'utilisation que j'utilise maintenant au quotidien,

pour me simplifier l'écriture.

typedef unsigned long int ulint;

ulint id_user = 200;

SqlConnector sqlConnector; //< Contient les infos pour se connecter
SqlQuery query( sqlConnector, "SELECT * FROM USER WHERE ID = :ID" );
query["ID"] = id_user;
query.activate();

par après, j'ai surchargé l'opérateur () pour qu'il prennent deux
arguments ou aucuns,

pour l'opérateur () vide , Activation de la query
pour l'opérateur () surchargé de deux arguments

template< typename T > operator() ( std::string const & field, T const &
value ) {
// Assignation vers le bon type, soit AsString, AsInteger, etc...
}

un exemple
SqlQuery query( sqlConnector, "SELECT * FROM USER WHERE ID = :ID" );
query( "ID", id_user )();


Autre chose, j'ai modifié aussi ma classe pour que lorsque la query ne
trouve pas une valeur, elle puisse retourner une valeur par défaut.

Ce qui donne cet exemple-ci

Désolé, je n'ai pas trouvé un exemple qui pourrait être généralisé pour
d'autre bout de code, mais dans le code que je maintiens, cela m'aide.

SqlQuery query( sqlConnector, "SELECT XXX FROM USER WHERE ID = :ID" );
return ( query( "ID", id_user()()["XXX"] | "Inconnu" );

Donc, dans le cas ou la query ne trouverait pas l'utilisateur via son
ID, le code retournera "Inconnu".



En gros, j'ai réalisé une classe qui encapsule TSQLQuery dans un
auto_ptr. J'ai utilisé l'idiom RAII, et ajouter des opérateurs
d'assignations et de cast pour std::string, int, ulint, bool et TDateTime.

Ce qui me permet d'écrire, ceci ( version très simplifiée ).

std::string firstname, lastname;
TDateTime birthdate;
int sex;

SqlQuery query( sqlConnector, "SELECT FIRSTNAME, LASTNAME, BIRTHDATE,
SEX FROM USER WHERE ID = :ID" );
query( "ID", id_user )();
if( !query.empty() ) {
firstname = query["FIRSTNAME"];
lastname = query["LASTNAME"];
birthdate = query["BIRTHDATE"];
sex = query["SEX"];
}

Voilà,

Alors si quelqu'un a quelque chose de similaire et qu'il désire le faire
partager, je suis preneur.

Bonne journée

Avatar
Stephane Wirtel
J'ai posté ce message sur le forum Borland C++ Builder de developpez.com
, et il ne semble pas que cela intéresse grand monde.

Serais-le seul à développer avec TSQLQuery ?

Stéphane
Avatar
Sylvain
Stephane Wirtel wrote on 14/09/2007 08:53:
Je me suis demandé si quelqu'un avait déjà essayé d'écrire un wrapper
au dessus de TSQLQuery afin de simplifier son utilisation.




si la question est limitée au surcharge possible de TSQLQuery, les
commentaires si après seront peut être non pertinent car je ne connais
pas cette classe (je n'utilise plus le FWK Borland).

si la question porte sur la façon de représenter une requête SQL
générique, j'ai rencontré ce même désir de simplification mais cette
simplification a (assez vite) des limites, par exemple pour une requête
multi-tables avec jointures naturelles et externes.

Un bête exemple d'utilisation que j'utilise maintenant au quotidien,
pour me simplifier l'écriture.

unsigned long int id_user = 200;

SqlConnector sqlConnector; //< Contient les infos pour se connecter
SqlQuery query( sqlConnector, "SELECT * FROM USER WHERE ID = :ID" );
query["ID"] = id_user;
query.activate();


cette écriture résume plusieurs points qui m'ont fait abandonné le Fwk
Borland à l'époque de BC & Database tools (dont SQLQuery doit surement
être la lignée).

Une requête n'a, IMHO, pas d'existence autre que la construction de la
chaîne SQL formant cette requête, or ici SQLQuery ignore la structure de
la ou des tables sur lesquelles portent cette requête (mais peut-être
peut-il y accéder via le SQLConnector).

je préfère donc un modèle où la requête est rédigée par une classe
représentant une table car on dispose alors d'informations sur la clé
primaire - souvent utilisée pour des requêtes simples - comme sur les
colonnes existantes afin de valider les champs demandés par la requête.

j'ai surchargé l'opérateur () pour qu'il prennent deux
arguments ou aucuns,
un exemple
SqlQuery query( sqlConnector, "SELECT * FROM USER WHERE ID = :ID" );
query( "ID", id_user )();


ainsi une telle requête peut devenir:

SqlTable table(sqlServer, nameOfTable);
RecordSet set = table.select("*", id_user);

la classe table - qui connait son nom et sa clé primaire - formatte
alors le "select * from tableName where keyName=<param>" qui est traité
par l'instance sqlServer.

pour une recherche mono-table multi-critères, j'utilise parfois une méthode
select(const char* fields, const char* criteria, ...);
"criteria" jouant le role d'un template et les paramètres variables les
arguments d'un formattage à la sprintf.

plus généralement je préfère une simple méthode
select(const char* fields, const char* whereClause);
où la mise en forme de la requête (or vérification des champs listés vis
à vis de la définition de la table) se résume à un
"select <fields> from tableName where <whereClause>"

Autre chose, j'ai modifié aussi ma classe pour que lorsque la query ne
trouve pas une valeur, elle puisse retourner une valeur par défaut.


je ne comprends pas la motivation d'un tel traitement.

les valeurs par défaut sont définies au niveau de la table, si une
colonne accepte une valeur non assignée (null), l'applicatif voudra
surement recevoir cette valeur, ceci afin de distinguer les traitements
sur la base des traitements liés à l'interface utilisateur.

En gros, j'ai réalisé une classe qui encapsule TSQLQuery dans un
auto_ptr. J'ai utilisé l'idiom RAII, et ajouter des opérateurs
d'assignations et de cast pour std::string, int, ulint, bool et TDateTime.

Ce qui me permet d'écrire, ceci ( version très simplifiée ).

std::string firstname, lastname;
TDateTime birthdate;
int sex;

SqlQuery query( sqlConnector, "SELECT FIRSTNAME, LASTNAME, BIRTHDATE,
SEX FROM USER WHERE ID = :ID" );
query( "ID", id_user )();
if( !query.empty() ) {
firstname = query["FIRSTNAME"];
lastname = query["LASTNAME"];
birthdate = query["BIRTHDATE"];
sex = query["SEX"];
}


la librairie MySQL++ utilise le même paradigme ... et je n'aime pas trop.

pour une écriture (applicative) la plus concise, c'est une bonne
solution (puisque la définition d'opérateurs mulitples () ou [] avec un
argument entier (numéro de colonne) ou texte (nom de la colonne)
différencié par le type de retour est hélas/bien sur impossible.

pour autant cette écriture passe par une conversion de tous les champs
lus en chaine (std::string ou autre) qui seront reconvertis, selon un
cast implicite en leur type d'origine.

cette double conversion est très pénalisante (en temps de traitement)
pour des grosses requêtes et je préfère une lecture des champs au format
natif tels qu'ils sont transmis par le moteur SQL, ceci imposant certes
des methodes dédiées telles:
string str(int i) const;
long num(int i) const;
double dec(int i) const;
Date date(int i) const;
et/ou
string str(const char*) const;
...

Alors si quelqu'un a quelque chose de similaire et qu'il désire le faire
partager, je suis preneur.


my 2 cents.

Sylvain.