![]() |
|
|
![]() |
| Accueil | T�l�chargement | Exemple rapide |
Tutoriel (4)
|
Manuel (2)
|
Forum | Nos clients |
| QxOrm >> Manuel d'utilisation de la biblioth�que QxOrm |
|
L'objectif de ce manuel utilisateur est de pr�senter de mani�re structur�e l'ensemble des
fonctionnalit�s propos�es par la biblioth�que QxOrm.
Ce manuel est destin� aux d�veloppeurs et architectes logiciel qui souhaitent g�rer une couche de
donn�es persistante en C++/Qt.
Des comp�tences techniques en C++ et base de donn�es sont requises pour la bonne compr�hension de
ce document.
Remarque : la plupart des fonctionnalit�s pr�sent�es dans ce manuel peuvent �tre d�finies rapidement et facilement avec l'application QxEntityEditor (l'�diteur graphique de la biblioth�que QxOrm). Une documentation d�di�e � l'application QxEntityEditor est �galement disponible. Autre remarque : ce manuel est bas� en grande partie sur l'ancienne FAQ du site QxOrm, toujours accessible en cliquant ici.
QxOrm est une biblioth�que C++ open source de gestion de donn�es (Object Relational Mapping,
ORM).
QxOrm est d�velopp� XDL Teamarty, Ing�nieur en d�veloppement logiciel depuis 2003. � partir d'une simple fonction de param�trage (que l'on peut comparer avec un fichier de mapping XML Hibernate), vous aurez acc�s aux fonctionnalit�s suivantes :
La biblioth�que QxOrm a �t� retenue pour faire partie du programme Qt Ambassador. Si vous trouvez un bug ou si vous avez une question concernant le fonctionnement de la biblioth�que QxOrm, vous pouvez envoyer un mail � : support@qxorm.com. Un forum (en anglais) d�di� � QxOrm est disponible en cliquant ici. Vous pouvez �galement retrouver la communaut� fran�aise de QxOrm sur le forum de Developpez.com. Aper�u rapide de l'application QxEntityEditor
QxEntityEditor est un �diteur graphique pour la biblioth�que QxOrm :
QxEntityEditor permet de g�rer graphiquement le mod�le d'entit�s.
QxEntityEditor est multi-plateforme (disponible pour Windows, Linux et Mac OS X) et g�n�re du code natif pour tous les environnements : bureau (Windows, Linux, Mac OS X), embarqu� et mobile (Android, iOS, Windows Phone, Raspberry Pi, etc.). Une vid�o de pr�sentation de l'application QxEntityEditor est disponible. QxEntityEditor est bas� sur un syst�me de plugins et propose diverses fonctionnalit�s pour importer/exporter le mod�le de donn�es :
QxEntityEditor est d�velopp� XDL Teamarty, Ing�nieur en d�veloppement logiciel depuis 2003. Un manuel utilisateur d�di� � l'application QxEntityEditor est disponible. Convention d'�criture C++ utilis�e par la biblioth�que QxOrm
La biblioth�que QxOrm utilise les conventions d'�criture de code C++ suivantes :
La biblioth�que QxOrm est multi-plateforme et peut �tre install�e sur tous types d'environnement :
Windows, Linux (Unix), Mac OS X, Android, iOS, Windows Phone, etc...
Un tutoriel complet (avec captures d'�cran) pour installer un environnement de d�veloppement avec QxOrm sous Windows est disponible en cliquant ici. L'objectif de ce chapitre est de pr�senter rapidement les diff�rentes �tapes � suivre pour installer QxOrm sur tous types d'environnement :
Remarque : par d�faut, la biblioth�que QxOrm d�pend uniquement des modules QtCore et QtSql. Il est possible d'activer des fonctionnalit�s suppl�mentaires gr�ce au fichier de configuration QxOrm.pri (ou QxOrm.cmake) : ces nouvelles fonctionnalit�s peuvent alors ajouter des d�pendances � QxOrm. D�pendance � boost (optionnel)
Par d�faut, la biblioth�que QxOrm d�pend uniquement de Qt (QtCore et QtSql).
L'installation de boost est optionnelle et non requise avec la configuration par d�faut.
Remarque : QxOrm propose 2 niveaux de d�pendance � boost en option :
Remarque importante : avec l'option de compilation _QX_ENABLE_BOOST, la biblioth�que QxOrm d�pend uniquement des fichiers d'en-t�te *.hpp de boost (utilisation des biblioth�ques header only uniquement). L'installation de boost est donc tr�s simple puisqu'il suffit de d�zipper le package boost (pour disposer des fichiers d'en-t�te *.hpp). Fichier de configuration QxOrm.pri (ou QxOrm.cmake)
Le fichier de configuration QxOrm.pri (ou QxOrm.cmake) est divis� en plusieurs
sections (chacune �tant comment�e) et regroupe les diff�rents param�trages et options de
compilation disponibles.
Il est fortement recommand� de lire attentivement le fichier de configuration QxOrm.pri
avant de compiler la biblioth�que QxOrm.
Il est possible de conserver le param�trage par d�faut, seule la variable
QX_BOOST_INCLUDE_PATH est n�cessaire si votre projet utilise le framework boost : cette
variable indique o� trouver les fichiers d'en-t�te *.hpp de la biblioth�que boost :
Si vous ne souhaitez pas modifier le fichier de configuration QxOrm.pri, il est possible de d�finir une variable d'environnement nomm�e BOOST_INCLUDE : cette variable d'environnement sera alors utilis�e automatiquement pour valoriser QX_BOOST_INCLUDE_PATH (lire le fichier QxOrm.pri pour plus d'informations). Voici une liste non exhaustive des diff�rentes options de compilation disponibles (lire le fichier de configuration QxOrm.pri pour plus de d�tails), aucune n'�tant activ�e par d�faut :
Remarque : le fichier de configuration QxOrm.pri (ou QxOrm.cmake) devra �tre inclus dans tous les projets d�pendants de la biblioth�que QxOrm en ajoutant la ligne suivante dans le fichier *.pro du projet :
Autre remarque : � la place de qmake, il est possible d'utiliser l'outil de compilation CMake pour configurer et construire la biblioth�que QxOrm. CMake propose un outil graphique afin de visualiser et param�trer les diff�rentes options disponibles :
Compiler la biblioth�que QxOrm (avec qmake ou CMake)
QxOrm utilise le processus qmake de la biblioth�que Qt pour g�n�rer les
makefile et compiler le projet (il est �galement possible d'utiliser l'outil de compilation CMake, un fichier
CMakeLists.txt �tant fourni avec la biblioth�que QxOrm).
qmake est multi-plateforme et fonctionne parfaitement sous Windows, Linux (Unix) et Mac OS X. Pour compiler QxOrm, il suffit d'ex�cuter les commandes suivantes :
Sous Windows, des fichiers *.vcproj et *.sln sont disponibles pour les �diteurs Microsoft Visual C++. Les fichiers *.pro sont lisibles par l'�diteur Qt Creator, et des plugins existent permettant de s'interfacer avec de nombreux �diteurs C++. Les fichiers mingw_build_all_debug.bat et mingw_build_all_release.bat pr�sents dans le dossier ./tools/ permettent de compiler rapidement QxOrm ainsi que tous les tests avec le compilateur MinGW sous Windows. Les fichiers gcc_build_all_debug.sh et gcc_build_all_release.sh pr�sents dans le dossier ./tools/ permettent de compiler rapidement QxOrm ainsi que tous les tests avec GCC sous Linux. Enfin, les fichiers osx_build_all_debug.sh et osx_build_all_release.sh pr�sents dans le dossier ./tools/ permettent de compiler rapidement QxOrm ainsi que tous les tests sous Mac OS X (merci � Dominique Billet pour l'�criture des scripts). Pilotes SQL fournis par Qt (drivers)
QxOrm utilise le moteur QtSql de Qt bas� sur un syst�me de plugin.
Une liste d�taill�e des bases de donn�es support�es est disponible sur le site de Qt. Le plugin ODBC (QODBC) assure une compatibilit� avec de nombreuses bases de donn�es. Pour des performances optimales, il est conseill� d'utiliser un plugin sp�cifique � une base de donn�es :
Autre remarque : la biblioth�que QxOrm supporte �galement la base de donn�es MongoDB (C++ ODM Object Document Mapper). Persistance - Object Relational Mapping (ORM)
La biblioth�que QxOrm fournit un moteur de persistance des donn�es bas� sur le module QtSql de Qt.
Ce moteur de persistance utilise la technique de programmation : Object Relational Mapping
(ORM).
D�finition du site Wikipedia : un mapping objet-relationnel (en anglais object-relational mapping ou ORM) est une technique de programmation informatique qui cr�e l'illusion d'une base de donn�es orient�e objet � partir d'une base de donn�es relationnelle en d�finissant des correspondances entre cette base de donn�es et les objets du langage utilis�. On pourrait le d�signer par � correspondance entre monde objet et monde relationnel �. Le mapping objet-relationnel consiste � associer une ou plusieurs classes avec une table, et chaque attribut de la classe avec un champ de la table. Les frameworks de mapping objet-relationnel permettent d'�liminer la duplication de code dans les op�rations CRUD. Pour effectuer cette correspondance entre le monde objet et le monde relationnel, ainsi pour que proposer l'ensemble de ses fonctionnalit�s, la biblioth�que QxOrm impose l'enregistrement de classes C++ dans le contexte QxOrm. Nous allons donc d�buter ce chapitre de la fa�on suivante : comment enregistrer une classe C++ dans le contexte QxOrm ? Remarque : la biblioth�que QxOrm supporte �galement la base de donn�es MongoDB (C++ ODM Object Document Mapper). D�finir une classe dans le contexte QxOrm (mapping)
Toutes les classes C++ peuvent �tre enregistr�es dans le contexte QxOrm : il n'y a pas besoin de
d�river d'un super objet, et vous pouvez �crire vos m�thodes de classes et accesseurs sans
aucune contrainte.
Enregistrer une classe C++ dans le contexte QxOrm signifie :
* Fichier person.h :
* Fichier person.cpp :
Remarque : les m�thodes qx::QxClass<T>::id() et qx::QxClass<T>::data() retournent une instance de type : qx::IxDataMember (classe de base pour l'enregistrement des donn�es membre). Gr�ce � cette instance, il est possible de personnaliser le comportement par d�faut propos� par la classe qx::IxDataMember, comme par exemple dans le chapitre : D�finir une donn�e membre transient. Autre remarque : il est �galement possible d'enregistrer des m�thodes de classe dans le contexte QxOrm (gestion des m�thodes static et non static) avec les m�thodes qx::QxClass<T>::fct_0(), qx::QxClass<T>::fct_1(), etc... Cette fonctionnalit� fait partie du moteur d'introspection de la biblioth�que QxOrm, plus de d�tails dans le chapitre : Appeler dynamiquement une fonction. Cl� primaire autre que le type par d�faut "long"
Par d�faut, lorsqu'un mapping d'une classe C++ est �crit avec la m�thode void
qx::register_class<T>, l'identifiant associ� � la classe est de type long
(cl� primaire avec auto-incr�mentation dans la base de donn�es).
Il est possible de d�finir un identifiant d'un autre type en utilisant la macro QX_REGISTER_PRIMARY_KEY. Cette macro sp�cialise le template qx::trait::get_primary_key<T> pour associer un type d'identifiant � une classe C++. Par exemple, pour d�finir un identifiant unique de type QString pour la classe C++ myClass (mapp�e vers une table de la BDD avec une colonne de type VARCHAR pour cl� primaire), il suffit d'�crire : QX_REGISTER_PRIMARY_KEY(myClass, QString) Voici un exemple d'utilisation de la macro QX_REGISTER_PRIMARY_KEY avec une classe author poss�dant un identifiant de type QString :
Cl� primaire sur plusieurs colonnes (composite key)
QxOrm supporte la notion de 'multi-columns primary key'.
L'identifiant de la classe doit �tre du type suivant :
La liste des noms des colonnes doit �tre de la forme suivante : 'column1|column2|column3|etc.'. Exemple d'utilisation avec la classe 'author' du projet 'qxBlogCompositeKey', cette classe poss�de un identifiant sur trois colonnes :
Donn�es membres public/protected/private
Pour enregistrer des membres private ou protected dans le contexte QxOrm
(fonction qx::register_class<T>), il faut d�clarer les friend class
n�cessaires.
Pour simplifier l'�criture avec les template C++, la biblioth�que QxOrm fournit la macro suivante : QX_REGISTER_FRIEND_CLASS(myClass). Un exemple d'utilisation se trouve dans le dossier ./test/qxDllSample/dll1/ du package QxOrm avec la classe CPerson :
Si une classe est d�finie dans un espace de nom (namespace), alors une erreur de
compilation se produit avec l'utilisation des macros : QX_REGISTER_HPP et
QX_REGISTER_CPP.
Pour �viter ces erreurs de compilation, il est n�cessaire d'utiliser les macros suivantes :
QX_REGISTER_COMPLEX_CLASS_NAME_HPP et QX_REGISTER_COMPLEX_CLASS_NAME_CPP.
Vous trouverez un exemple d'utilisation dans le dossier ./test/qxDllSample/dll1/ de la distribution de QxOrm avec la classe CPerson d�finie dans l'espace de nom qx::test :
Les macros QX_REGISTER_COMPLEX_CLASS_NAME... n�cessitent un param�tre suppl�mentaire (dans l'exemple ci-dessus il s'agit du param�tre qx_test_CPerson) afin de cr�er une variable globale. Celle-ci est appel�e d�s le lancement de l'application. La construction de cette instance globale d�clare la classe dans le module QxFactory (mod�le de conception fabrique ou design pattern factory). Un objet C++ ne pouvant pas se nommer avec des caract�res "::", le param�tre suppl�mentaire de la macro permet de remplacer tous les "::" par des "_".
La biblioth�que QxOrm supporte la plupart des types primitifs du standard C++ et du framework
Qt (num�riques, bool�ens, chaines de caract�res, date/heure, collections, pointeurs et
pointeurs intelligents, etc...).
Voici un exemple pr�sentant une liste (non exhaustive) de types C++ support�s ainsi que
l'association par d�faut du type de base de donn�es (format SQLite) :
Remarque : il est �galement possible de persister un type non g�r� par d�faut par la biblioth�que QxOrm. Rendez-vous au chapitre Persister des types personnalis�s pour plus de d�tails sur cette fonctionnalit�. Autre remarque : concernant l'association d'un type C++ avec le type de base de donn�es associ�, rendez-vous au chapitre Associer un type SQL � une classe C++ pour plus de d�tails. D�finir une donn�e membre transient
Une donn�e membre transient n'est pas associ�e � une colonne d'une table de la base de
donn�es.
Le module QxDao ignore donc
cette propri�t� pour toutes les requ�tes � la base de donn�es.
A quoi sert l'enregistrement d'une donn�e membre transient dans le contexte QxOrm ? Enregistrer une donn�e membre transient dans le contexte QxOrm permet de disposer des autres fonctionnalit�s de la biblioth�que QxOrm sur cette propri�t�, comme par exemple : s�rialisation, introspection, etc... La m�thode qx::QxClass<T>::data() dispose d'un param�tre optionnel nomm� : bool bDao (par d�faut, valeur � true). Par exemple, ajoutons une propri�t� transient nomm�e age � la classe person (cette propri�t� n'a pas besoin d'�tre stock�e en base de donn�es puisque nous disposons d�j� de la propri�t� birthDate) :
Voici une autre fa�on de d�finir une propri�t� transient en r�cup�rant l'instance de type qx::IxDataMember :
Connexion � la base de donn�es
La connexion � la base de donn�es peut �tre param�tr�e avec la classe singleton : qx::QxSqlDatabase.
Voici un exemple de param�trage � une base de donn�es SQLite nomm�e test_qxorm.db :
Une fois les param�tres de connexion renseign�s dans la classe singleton qx::QxSqlDatabase, toutes les op�rations avec la base de donn�es effectu�es par la biblioth�que QxOrm utiliserons ces param�tres. Pour plus d'informations sur les param�tres de connexion � renseigner, il est recommand� de lire la documentation de la classe QSqlDatabase du framework Qt. Remarque : la classe qx::QxSqlDatabase g�re automatiquement les appels � la base de donn�es dans diff�rents threads (multi-threading). Autre remarque : il est possible de g�rer son propre pool de connexions � la base de donn�es, et de travailler �galement avec plusieurs bases de donn�es distinctes : rendez-vous dans le chapitre Travailler avec plusieurs bases de donn�es pour plus d'informations sur cette fonctionnalit�. Autre remarque : suivant le pilote SQL renseign� dans les param�tres de connexion, la biblioth�que QxOrm associe automatiquement un g�n�rateur SQL. Ce g�n�rateur SQL permet de g�rer les sp�cificit�s propres � chaque type de base de donn�es. Tous les g�n�rateurs SQL h�ritent de la classe de base : qx::dao::detail::IxSqlGenerator :
Sauvegarder une instance C++ en base de donn�es (insert/update)
Toutes les fonctions li�es � la base de donn�es sont disponibles dans l'espace de nom
qx::dao.
Pour sauvegarder une instance C++ (ou une liste d'instances C++) en base de donn�es, la biblioth�que QxOrm fournit les fonctions suivantes :
Par exemple :
Remarque : toutes les fonctions de l'espace de nom qx::dao sont flexibles au niveau des param�tres, elles peuvent accepter : une instance, une liste d'instances, un pointeur, un pointeur intelligent, une liste de pointeurs, une liste de pointeurs intelligents, etc... Par exemple :
Pour connaitre la liste des pointeurs intelligents support�s, rendez-vous dans le chapitre : Pointeurs intelligents support�s par QxOrm (smart-pointers). Supprimer une instance C++ de la base de donn�es (delete)
Toutes les fonctions li�es � la base de donn�es sont disponibles dans l'espace de nom
qx::dao.
Pour supprimer une instance C++ (ou une liste d'instances C++) en base de donn�es, la biblioth�que QxOrm fournit les fonctions suivantes :
Par exemple :
Suppression logique (soft delete)
Une suppression logique permet de ne pas effacer de ligne dans une table d'une base de
donn�es (contrairement � une suppression physique) : une colonne suppl�mentaire est ajout�e �
la d�finition de la table pour indiquer que la ligne est supprim�e ou non.
Cette colonne peut contenir soit un bool�en (1 signifie ligne supprim�e, 0 ou vide signifie ligne non supprim�e), soit la date-heure de suppression de la ligne (si vide, la ligne est consid�r�e comme non supprim�e). Il est donc � tout moment possible de r�activer une ligne supprim�e en r�initialisant la valeur � vide dans la table de la base de donn�es. Pour activer le m�canisme de suppression logique avec la biblioth�que QxOrm, il faut utiliser la classe qx::QxSoftDelete dans la fonction de mapping qx::register_class<T>. Voici un exemple d'utilisation avec une classe Bar contenant deux propri�t�s m_id et m_desc :
Les requ�tes SQL g�n�r�es automatiquement par la biblioth�que QxOrm vont prendre en compte ce param�tre de suppression logique pour ajouter les conditions n�cessaires (ne pas r�cup�rer les �l�ments supprim�s, ne pas supprimer physiquement une ligne, etc.). Par exemple, si vous ex�cutez les lignes suivantes avec la classe Bar :
Vous obtiendrez les traces suivantes :
Remarque : pour supprimer physiquement une ligne de la base de donn�es, il faut utiliser les fonctions : qx::dao::destroy_by_id() et qx::dao::destroy_all(). Autre remarque : il peut �tre int�ressant de d�finir au niveau du SGBD un index sur la colonne deleted_at (ou peu importe le nom que vous donnez) afin d'acc�l�rer l'ex�cution des requ�tes SQL. R�cup�rer une instance C++ de la base de donn�es (fetch)
Toutes les fonctions li�es � la base de donn�es sont disponibles dans l'espace de nom
qx::dao.
Pour valoriser automatiquement les propri�t�s d'une instance C++ (ou d'une liste d'instances C++) en fonction des donn�es d'une table (ou plusieurs tables si des relations sont d�finies) de la base de donn�es, la biblioth�que QxOrm fournit les fonctions suivantes :
Par exemple :
La biblioth�que QxOrm fournit plusieurs outils pour effectuer des requ�tes � la base de donn�es
:
Utilisation de la classe qx::QxSqlQuery (ou son alias qx_query)
La classe qx::QxSqlQuery (ou bien son alias qx_query) permet
d'interroger la base de donn�es (trier, filtrer, etc.) de deux mani�res diff�rentes :
La deuxi�me m�thode (utilisation du code C++ pour g�n�rer la requ�te SQL) permet de mapper automatiquement les param�tres SQL sans utiliser la fonction qx::QxSqlQuery::bind(). Voici un exemple d'utilisation de la classe qx::QxSqlQuery avec �criture manuelle d'une requ�te SQL :
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 :
Cette utilisation de la classe qx::QxSqlQuery pr�sente l'avantage de ne pas avoir � mapper les param�tres de la requ�te, tout en restant tr�s proche de l'�criture manuelle d'une requ�te SQL. Les param�tres seront automatiquement inject�s en utilisant la syntaxe d�finie de mani�re globale par la m�thode : qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle(). Voici un exemple pr�sentant diff�rentes m�thodes disponibles avec la classe qx::QxSqlQuery (ou bien son alias qx_query) :
Ce qui produira le code SQL suivant pour les bases de donn�es MySQL, PostgreSQL et SQLite (pour Oracle et SQLServer, le traitement de la m�thode limit() est diff�rent) :
Voici la liste des fonctions et m�thodes disponibles pour utiliser la classe qx::QxSqlQuery (ou bien son alias qx_query) :
Remarque : certaines de ces fonctions ont �galement deux autres param�tres optionnels :
Appel de proc�dure stock�e ou requ�te SQL personnalis�e
La biblioth�que QxOrm fournit deux fonctions pour appeler une proc�dure stock�e ou une
requ�te SQL personnalis�e :
Le premier param�tre de ces deux fonctions, de type qx::QxSqlQuery (ou son alias qx_query), correspond � la
proc�dure stock�e ou � la requ�te SQL personnalis�e.
Pour plus d'informations sur la classe qx::QxSqlQuery, rendez-vous sur ce chapitre du manuel utilisateur : Utilisation de la classe qx::QxSqlQuery (ou son alias qx_query). La fonction qx::dao::execute_query<T>() est une fonction template : le type T doit �tre enregistr� dans le contexte QxOrm (fonction qx::register_class<T>). Toutes les donn�es renvoy�es par la proc�dure stock�e ou la requ�te SQL personnalis�e qui pourront �tre associ�es aux membres des classes C++ (de type T) seront valoris�es automatiquement. Une recherche automatique est effectu�e sur le nom des champs associ�s aux donn�es. Voici un exemple d'utilisation (disponible dans le projet qxBlog du package QxOrm) :
La fonction qx::dao::call_query() n'est pas une fonction template : les r�sultats de la requ�te doivent �tre parcourus manuellement sur la classe qx::QxSqlQuery (ou qx_query). Pour r�cup�rer un param�tre de sortie (qui doit �tre pass� � la requ�te en tant que QSql::Out ou QSql::InOut), il suffit d'utiliser la m�thode : QVariant qx::QxSqlQuery::boundValue(const QString & sKey) const;. Pour parcourir la liste des r�sultats de la requ�te, il faut utiliser les m�thodes suivantes :
Transactions (commit, rollback, session)
Une transaction est une suite d'op�rations effectu�es comme une seule
unit� logique de travail.
Une fois termin�e, la transaction est :
Remarque : une session peut d�clencher une exception de type qx::dao::sql_error lorsqu'une erreur se produit (par d�faut, aucune exception n'est d�clench�e). Il est possible de param�trer ce comportement en utilisant :
De plus, il est possible d'initialiser une session avec sa propre connexion (provenant d'un pool de connexions par exemple) en utilisant le constructeur de la classe qx::QxSession. La classe qx::QxSession propose �galement des m�thodes de persistance (CRUD), ce qui peut simplifier l'�criture du code C++ suivant les habitudes de programmation. Voici le m�me exemple en utilisant les m�thodes de la classe qx::QxSession � la place des fonctions du namespace qx::dao :
La biblioth�que QxOrm fournit un puissant moteur de relations permettant de d�finir facilement :
Une relation one-to-many (1-n) est d�finie par la m�thode : qx::QxClass<T>::relationOneToMany().
Cette m�thode renvoie une instance de la classe qx::IxSqlRelation (classe de base pour toutes les relations) et
n�cessite 3 param�tres :
Par exemple : prenons l'exemple d'un author (une personne) qui peut r�diger plusieurs blog : nous allons ainsi montrer comment d�finir une relation de type one-to-many. Au niveau base de donn�es, voici les deux tables qui correspondent : ![]() Fichier author.h :
Fichier author.cpp :
Une relation many-to-one (n-1) est d�finie par la m�thode : qx::QxClass<T>::relationManyToOne().
Cette m�thode renvoie une instance de la classe qx::IxSqlRelation (classe de base pour toutes les relations) et
n�cessite 2 param�tres :
Par exemple : un comment est associ� � un blog et un blog peut contenir plusieurs comment : nous allons ainsi montrer comment d�finir une relation de type many-to-one. Au niveau base de donn�es, voici les deux tables qui correspondent : ![]() Fichier comment.h :
Fichier comment.cpp :
Une relation many-to-many (n-n) est d�finie par la m�thode : qx::QxClass<T>::relationManyToMany().
Cette m�thode renvoie une instance de la classe qx::IxSqlRelation (classe de base pour toutes les relations) et
n�cessite 5 param�tres :
Par exemple : une category r�f�rence plusieurs blog et un blog peut appartenir � plusieurs category : nous allons ainsi montrer comment d�finir une relation de type many-to-many. Ce type de relation implique une table suppl�mentaire dans la base de donn�es pour stocker la liste des id de chaque c�t� des relations. Au niveau base de donn�es, voici les trois tables qui correspondent : ![]() Fichier category.h :
Fichier category.cpp :
Une relation one-to-one (1-1) permet de repr�senter 2 entit�s distinctes qui partagent
le m�me identifiant en base de donn�es.
Une relation one-to-one (1-1) est d�finie par la m�thode : qx::QxClass<T>::relationOneToOne().
Cette m�thode renvoie une instance de la classe qx::IxSqlRelation (classe de base pour toutes les relations) et
n�cessite 2 param�tres :
Par exemple : prenons l'exemple d'une table person et d'une autre table author : un author est �galement une person, les 2 tables pourraient partager le m�me identifiant en base de donn�es. Au niveau base de donn�es, voici les 2 tables qui correspondent (person_id == author_id) :
La biblioth�que QxOrm supporte quatre types de relations pour lier les classes C++
enregistr�es dans le contexte QxOrm : one-to-one, one-to-many,
many-to-one et many-to-many.
Pour plus de d�tails sur la d�finition de ces relations, il est conseill� de lire le tutoriel qxBlog. Nous allons d�tailler dans cette Q&R les diff�rentes m�thodes de r�cup�ration des donn�es (module QxDao, fonctions du namespace qx::dao) :
Cette liste de relations peut contenir les �l�ments suivants :
Exemple : � partir du tutoriel qxBlog, il est possible de r�cup�rer les donn�es suivantes avec une seule requ�te : 1- r�cup�rer un blog et son author ; 2- pour l'author valoris�, r�cup�rer tous les blog qu'il a �crit ; 3- pour chaque blog que l'author a �crit, r�cup�rer tous les comment associ�s.
Ce qui g�n�re la requ�te SQL suivante : SELECT blog.blog_id AS blog_blog_id_0, blog.blog_text AS blog_blog_text_0, blog.date_creation AS blog_date_creation_0, blog.author_id AS blog_author_id_0,
author_1.author_id AS author_1_author_id_0, author_1.name AS author_1_name_0, author_1.birthdate AS author_1_birthdate_0, author_1.sex AS author_1_sex_0,
blog_2.blog_id AS blog_2_blog_id_0, blog_2.author_id AS blog_2_author_id_0, blog_2.blog_text AS blog_2_blog_text_0, blog_2.date_creation AS blog_2_date_creation_0,
comment_4.comment_id AS comment_4_comment_id_0, comment_4.blog_id AS comment_4_blog_id_0, comment_4.comment_text AS comment_4_comment_text_0, comment_4.date_creation AS comment_4_date_creation_0
FROM blog
LEFT OUTER JOIN author author_1 ON author_1.author_id = blog.author_id
LEFT OUTER JOIN blog blog_2 ON blog_2.author_id = author_1.author_id
LEFT OUTER JOIN comment comment_4 ON comment_4.blog_id = blog_2.blog_id
WHERE blog.blog_id = :blog_id
Autre exemple : il est �galement possible de cr�er une liste de relations � r�cup�rer, comme ceci par exemple :
Ce qui g�n�re la requ�te SQL suivante : SELECT blog.blog_id AS blog_blog_id_0, blog.blog_text AS blog_blog_text_0, blog.date_creation AS blog_date_creation_0, blog.author_id AS blog_author_id_0,
author_1.author_id AS author_1_author_id_0, author_1.name AS author_1_name_0, author_1.birthdate AS author_1_birthdate_0, author_1.sex AS author_1_sex_0,
blog_2.blog_id AS blog_2_blog_id_0, blog_2.author_id AS blog_2_author_id_0, blog_2.blog_text AS blog_2_blog_text_0, blog_2.date_creation AS blog_2_date_creation_0,
category_5.category_id AS category_5_category_id_0, category_5.name AS category_5_name_0, category_5.description AS category_5_description_0,
comment_6.comment_id AS comment_6_comment_id_0, comment_6.blog_id AS comment_6_blog_id_0, comment_6.comment_text AS comment_6_comment_text_0, comment_6.date_creation AS comment_6_date_creation_0,
category_7.category_id AS category_7_category_id_0, category_7.name AS category_7_name_0, category_7.description AS category_7_description_0
FROM blog
LEFT OUTER JOIN author author_1 ON author_1.author_id = blog.author_id
LEFT OUTER JOIN blog blog_2 ON blog_2.author_id = author_1.author_id
LEFT OUTER JOIN category_blog category_blog_5 ON blog_2.blog_id = category_blog_5.blog_id
LEFT OUTER JOIN category category_5 ON category_blog_5.category_id = category_5.category_id
LEFT OUTER JOIN comment comment_6 ON comment_6.blog_id = blog.blog_id
LEFT OUTER JOIN category_blog category_blog_7 ON blog.blog_id = category_blog_7.blog_id
LEFT OUTER JOIN category category_7 ON category_blog_7.category_id = category_7.category_id
WHERE blog.blog_id = :blog_id
Autre exemple : pour r�cup�rer toutes les relations pour un niveau donn�, il faut utiliser le mot-cl� "*". Pour r�cup�rer toutes les donn�es de toutes les relations sur trois niveaux, il faut �crire :
Ce qui g�n�re la requ�te SQL suivante : SELECT blog.blog_id AS blog_blog_id_0, blog.blog_text AS blog_blog_text_0, blog.date_creation AS blog_date_creation_0, blog.author_id AS blog_author_id_0,
author_1.author_id AS author_1_author_id_0, author_1.name AS author_1_name_0, author_1.birthdate AS author_1_birthdate_0, author_1.sex AS author_1_sex_0,
blog_2.blog_id AS blog_2_blog_id_0, blog_2.author_id AS blog_2_author_id_0, blog_2.blog_text AS blog_2_blog_text_0, blog_2.date_creation AS blog_2_date_creation_0, blog_2.author_id AS blog_2_author_id_0_2,
author_3.author_id AS author_3_author_id_0, author_3.name AS author_3_name_0, author_3.birthdate AS author_3_birthdate_0, author_3.sex AS author_3_sex_0,
comment_4.comment_id AS comment_4_comment_id_0, comment_4.blog_id AS comment_4_blog_id_0, comment_4.comment_text AS comment_4_comment_text_0, comment_4.date_creation AS comment_4_date_creation_0,
category_5.category_id AS category_5_category_id_0, category_5.name AS category_5_name_0, category_5.description AS category_5_description_0,
comment_6.comment_id AS comment_6_comment_id_0, comment_6.blog_id AS comment_6_blog_id_0, comment_6.comment_text AS comment_6_comment_text_0, comment_6.date_creation AS comment_6_date_creation_0, comment_6.blog_id AS comment_6_blog_id_0_6,
blog_7.blog_id AS blog_7_blog_id_0, blog_7.blog_text AS blog_7_blog_text_0, blog_7.date_creation AS blog_7_date_creation_0, blog_7.author_id AS blog_7_author_id_0_7,
author_8.author_id AS author_8_author_id_0, author_8.name AS author_8_name_0, author_8.birthdate AS author_8_birthdate_0, author_8.sex AS author_8_sex_0,
comment_9.comment_id AS comment_9_comment_id_0, comment_9.blog_id AS comment_9_blog_id_0, comment_9.comment_text AS comment_9_comment_text_0, comment_9.date_creation AS comment_9_date_creation_0,
category_10.category_id AS category_10_category_id_0, category_10.name AS category_10_name_0, category_10.description AS category_10_description_0,
category_11.category_id AS category_11_category_id_0, category_11.name AS category_11_name_0, category_11.description AS category_11_description_0,
blog_12.blog_id AS blog_12_blog_id_0, blog_12.blog_text AS blog_12_blog_text_0, blog_12.date_creation AS blog_12_date_creation_0, blog_12.author_id AS blog_12_author_id_0_12,
author_13.author_id AS author_13_author_id_0, author_13.name AS author_13_name_0, author_13.birthdate AS author_13_birthdate_0, author_13.sex AS author_13_sex_0,
comment_14.comment_id AS comment_14_comment_id_0, comment_14.blog_id AS comment_14_blog_id_0, comment_14.comment_text AS comment_14_comment_text_0, comment_14.date_creation AS comment_14_date_creation_0,
category_15.category_id AS category_15_category_id_0, category_15.name AS category_15_name_0, category_15.description AS category_15_description_0
FROM blog
LEFT OUTER JOIN author author_1 ON author_1.author_id = blog.author_id
LEFT OUTER JOIN blog blog_2 ON blog_2.author_id = author_1.author_id
LEFT OUTER JOIN author author_3 ON author_3.author_id = blog_2.author_id
LEFT OUTER JOIN comment comment_4 ON comment_4.blog_id = blog_2.blog_id
LEFT OUTER JOIN category_blog category_blog_5 ON blog_2.blog_id = category_blog_5.blog_id
LEFT OUTER JOIN category category_5 ON category_blog_5.category_id = category_5.category_id
LEFT OUTER JOIN comment comment_6 ON comment_6.blog_id = blog.blog_id
LEFT OUTER JOIN blog blog_7 ON blog_7.blog_id = comment_6.blog_id
LEFT OUTER JOIN author author_8 ON author_8.author_id = blog_7.author_id
LEFT OUTER JOIN comment comment_9 ON comment_9.blog_id = blog_7.blog_id
LEFT OUTER JOIN category_blog category_blog_10 ON blog_7.blog_id = category_blog_10.blog_id
LEFT OUTER JOIN category category_10 ON category_blog_10.category_id = category_10.category_id
LEFT OUTER JOIN category_blog category_blog_11 ON blog.blog_id = category_blog_11.blog_id
LEFT OUTER JOIN category category_11 ON category_blog_11.category_id = category_11.category_id
LEFT OUTER JOIN category_blog category_blog_12 ON category_11.category_id = category_blog_12.category_id
LEFT OUTER JOIN blog blog_12 ON category_blog_12.blog_id = blog_12.blog_id
LEFT OUTER JOIN author author_13 ON author_13.author_id = blog_12.author_id
LEFT OUTER JOIN comment comment_14 ON comment_14.blog_id = blog_12.blog_id
LEFT OUTER JOIN category_blog category_blog_15 ON blog_12.blog_id = category_blog_15.blog_id
LEFT OUTER JOIN category category_15 ON category_blog_15.category_id = category_15.category_id
WHERE blog.blog_id = :blog_id
S�lectionner les colonnes des relations � r�cup�rer et d�finition des alias SQL
Il est parfois n�cessaire de ne pas requ�ter toutes les colonnes d'une table par soucis
d'optimisation : en effet, s�lectionner les colonnes r�ellement utilis�es par un traitement
permet de limiter les flux r�seau entre la base de donn�es et l'application C++, ce qui
am�liore les performances.
Concernant les relations, la biblioth�que QxOrm fournit une syntaxe sp�cifique pour s�lectionner les colonnes � r�cup�rer, sous la forme : my_relation { col_1, col_2, etc... }. Si cette syntaxe n'est pas utilis�e, par d�faut, QxOrm r�cup�re toutes les colonnes. Par exemple : imaginons la requ�te suivante qui permet de r�cup�rer :
Remarque : une autre syntaxe est disponible afin de renseigner les colonnes � ne pas r�cup�rer : my_relation -{ col_1, col_2, etc... }. Autre remarque : il est �galement possible de d�finir un alias par relation � utiliser dans la requ�te SQL. Ceci est utile pour l'�criture des conditions dans la clause WHERE. Un alias SQL peut �tre d�fini entre les caract�res < >. Exemple : voici un exemple de fetch avec relations en d�finissant des alias SQL par relation :
Ce qui g�n�re la requ�te SQL suivante : SELECT blog_alias.blog_id AS blog_alias_blog_id_0, blog_alias.blog_text AS blog_alias_blog_text_0, blog_alias.author_id AS blog_alias_author_id_0, author_alias.author_id AS author_alias_author_id_0, author_alias.name AS author_alias_name_0, author_alias.birthdate AS author_alias_birthdate_0, list_comment_alias.comment_id AS list_comment_alias_comment_id_0, list_comment_alias.blog_id AS list_comment_alias_blog_id_0, list_comment_alias.comment_text AS list_comment_alias_comment_text_0, list_comment_alias.blog_id AS list_comment_alias_blog_id_0_2, blog_alias_2.blog_id AS blog_alias_2_blog_id_0, blog_alias_2.blog_text AS blog_alias_2_blog_text_0, blog_alias_2.date_creation AS blog_alias_2_date_creation_0, blog_alias_2.author_id AS blog_alias_2_author_id_0_3, author_my_alias_suffix.author_id AS author_my_alias_suffix_author_id_0, author_my_alias_suffix.name AS author_my_alias_suffix_name_0, author_my_alias_suffix.birthdate AS author_my_alias_suffix_birthdate_0, author_my_alias_suffix.sex AS author_my_alias_suffix_sex_0, comment_my_alias_suffix.comment_id AS comment_my_alias_suffix_comment_id_0, comment_my_alias_suffix.blog_id AS comment_my_alias_suffix_blog_id_0, comment_my_alias_suffix.comment_text AS comment_my_alias_suffix_comment_text_0, comment_my_alias_suffix.date_creation AS comment_my_alias_suffix_date_creation_0, comment_my_alias_suffix.blog_id AS comment_my_alias_suffix_blog_id_0_5, category_my_alias_suffix.category_id AS category_my_alias_suffix_category_id_0, category_my_alias_suffix.name AS category_my_alias_suffix_name_0, category_my_alias_suffix.description AS category_my_alias_suffix_description_0
FROM blog AS blog_alias
LEFT OUTER JOIN author author_alias ON author_alias.author_id = blog_alias.author_id
LEFT OUTER JOIN comment list_comment_alias ON list_comment_alias.blog_id = blog_alias.blog_id
LEFT OUTER JOIN blog blog_alias_2 ON blog_alias_2.blog_id = list_comment_alias.blog_id
LEFT OUTER JOIN author author_my_alias_suffix ON author_my_alias_suffix.author_id = blog_alias_2.author_id
LEFT OUTER JOIN comment comment_my_alias_suffix ON comment_my_alias_suffix.blog_id = blog_alias_2.blog_id
LEFT OUTER JOIN category_blog category_blog_6 ON blog_alias_2.blog_id = category_blog_6.blog_id
LEFT OUTER JOIN category category_my_alias_suffix ON category_blog_6.category_id = category_my_alias_suffix.category_id
Ajout SQL dans les clauses LEFT OUTER JOIN / INNER JOIN
La classe qx::QxSqlQuery (ou son alias qx_query) dispose
de la m�thode suivante :
La m�thode qx::QxSqlQuery::addJoinQuery() permet d'ins�rer des sous-requ�tes SQL dans les clauses LEFT OUTER JOIN / INNER JOIN. Par exemple :
Le code C++ ci-dessus va construire la requ�te SQL suivante : SELECT blog_alias.blog_id AS blog_alias_blog_id_0, blog_alias.blog_text AS blog_alias_blog_text_0, blog_alias.author_id AS blog_alias_author_id_0, author_alias.author_id AS author_alias_author_id_0, author_alias.name AS author_alias_name_0, author_alias.birthdate AS author_alias_birthdate_0, author_alias.sex AS author_alias_sex_0, list_comment_alias.comment_id AS list_comment_alias_comment_id_0, list_comment_alias.blog_id AS list_comment_alias_blog_id_0, list_comment_alias.comment_text AS list_comment_alias_comment_text_0
FROM blog AS blog_alias
LEFT OUTER JOIN author author_alias ON (author_alias.author_id = blog_alias.author_id
AND author_alias.sex = :sex)
LEFT OUTER JOIN comment list_comment_alias ON (list_comment_alias.blog_id = blog_alias.blog_id
AND list_comment_alias.comment_text IS NOT NULL)
WHERE blog_alias.blog_text = :blog_alias_blog_text_1_0
Collections support�es par QxOrm
QxOrm supporte de nombreux conteneurs livr�s avec Qt, boost ou la biblioth�que standard std.
La biblioth�que QxOrm fournit �galement son propre conteneur, nomm� qx::QxCollection, particuli�rement adapt� pour stocker les donn�es
issues d'une base de donn�es.
Le d�veloppeur a donc � sa disposition un large choix : QxOrm n'impose aucune contrainte sur
l'utilisation des collections.
Collections fournies par l'espace de nom standard std
Il existe de nombreux container dans les biblioth�ques stl, boost et
Qt.
Il est donc l�gitime de se poser cette question : � quoi sert qx::QxCollection<Key, Value> ? qx::QxCollection<Key, Value> est un nouveau container (bas� sur l'excellente biblioth�que boost::multi_index_container) qui poss�de les fonctionnalit�s suivantes :
Cependant, chaque �l�ment renvoy� par ces deux macros correspond � un objet de type std::pair<Key, Value>. Pour obtenir un r�sultat 'plus naturel' et plus lisible, il est conseill� d'utiliser la macro _foreach : cette macro utilise BOOST_FOREACH pour tous les container sauf pour qx::QxCollection<Key, Value>. Dans ce cas, l'�l�ment renvoy� correspond au type Value (voir par la suite l'exemple d'utilisation). La macro _foreach est donc compatible avec tous les container (stl, Qt, boost, etc.) puisqu'elle utilise la macro BOOST_FOREACH. Autre Remarque : qx::QxCollection<Key, Value> est particuli�rement adapt� pour recevoir des donn�es issues d'une base de donn�es. En effet, ces donn�es peuvent �tre tri�es (en utilisant ORDER BY dans une requ�te SQL par exemple), il est donc important de conserver l'ordre d'insertion des �l�ments dans la liste. De plus, chaque donn�e issue d'une base de donn�es poss�de un identifiant unique. Il est donc int�ressant de pouvoir acc�der � un �l�ment en fonction de cet identifiant unique de mani�re extr�mement rapide (hash-map). Exemple d'utilisation de la collection qx::QxCollection<Key, Value> :
Pointeurs intelligents support�s par QxOrm (smart-pointers)
QxOrm supporte de nombreux pointeurs intelligents livr�s avec Qt, boost ou la biblioth�que standard std.
La biblioth�que QxOrm fournit �galement son propre pointeur intelligent, nomm� qx::dao::ptr, apportant de nouvelles fonctionnalit�s lorsqu'il est
utilis� avec les fonctions de l'espace de nom qx::dao.
Le d�veloppeur a donc � sa disposition un large choix : QxOrm n'impose aucune contrainte sur
l'utilisation des pointeurs intelligents.
Pointeurs intelligents de boost
Pointeurs intelligents fournis par l'espace de nom standard std
QxOrm est compatible avec les pointeurs intelligents des biblioth�ques boost et
Qt.
Le pointeur intelligent d�velopp� par QxOrm est bas� sur QSharedPointer et apporte de nouvelles fonctionnalit�s s'il est utilis� avec les fonctions 'qx::dao::...'. qx::dao::ptr<T> conserve automatiquement les valeurs issues de la base de donn�es. Il est ainsi possible de v�rifier � tout moment si une instance d'objet a subi des modifications gr�ce � la m�thode 'isDirty()' : cette m�thode peut renvoyer la liste de toutes les propri�t�s ayant �t� modifi�es. qx::dao::ptr<T> peut �galement �tre utilis� par la fonction 'qx::dao::update_optimized()' pour mettre � jour en base de donn�es uniquement les champs modifi�s. qx::dao::ptr<T> peut �tre utilis� avec un objet simple ou bien avec la plupart des containers : stl, boost, Qt et qx::QxCollection<Key, Value>. Exemple d'utilisation du pointeur intelligent qx::dao::ptr<T> :
Les Trigger de QxOrm permettent d'effectuer divers traitements avant et/ou apr�s
une insertion, une mise � jour ou bien une suppression dans la base de donn�es.
Un exemple d'utilisation se trouve dans le dossier ./test/qxDllSample/dll2/ avec la classe BaseClassTrigger. Cette classe contient cinq propri�t�s : m_id, m_dateCreation, m_dateModification, m_userCreation et m_userModification. Ces propri�t�s se mettront � jour automatiquement pour chaque classe h�ritant de BaseClassTrigger (cf. les classes Foo et Bar du m�me projet). Il est n�cessaire de sp�cialiser le template 'qx::dao::detail::QxDao_Trigger<T>' pour profiter de cette fonctionnalit�.
Validation d'une instance C++ (validators)
Le module QxValidator de la biblioth�que QxOrm permet d'ajouter des
contraintes sur les propri�t�s enregistr�es dans le contexte QxOrm.
Ces contraintes sont d�finies dans la m�thode de mapping : void qx::register_class<T>. Si pour une instance de classe donn�e, au moins une contrainte n'est pas respect�e, alors l'instance est consid�r�e comme invalide : l'objet ne peut alors pas �tre sauvegard� en base de donn�es (INSERT ou UPDATE). Il est �galement possible d'utiliser le module QxValidator pour valider les donn�es au niveau de la couche pr�sentation de l'application : si les donn�es saisies par un utilisateur ne sont pas valides, un message d'erreur peut �tre signal�, il n'est alors pas n�cessaire d'essayer d'enregistrer l'instance courante en base de donn�es. Les r�gles de validation n'ont pas besoin d'�tre dupliqu�es : elles peuvent �tre utilis�es aussi bien par la couche pr�sentation que par la couche d'acc�s aux donn�es de l'application. Voici la description de quelques classes du module QxValidator :
Voici un exemple d'utilisation du module QxValidator avec une classe 'person' : * fichier 'person.h' :
* fichier 'person.cpp' :
* fichier 'global_validator.h' :
* fichier 'main.cpp' :
A l'ex�cution de ce bout de code, l'instance 'personValidate' est non valide : la collection 'invalidValues' contient quatre �l�ments : - "la valeur de la propri�t� 'firstName' ne peut pas �tre vide" ; - "le sexe de la personne doit �tre d�fini : masculin ou f�minin" ; - "la valeur ne peut pas �tre �gale � 'admin'" ; - "la date-heure doit �tre renseign�e et doit �tre valide". Le module QxValidator fournit plusieurs validateurs pour effectuer des v�rifications basiques :
Il existe trois types de validateurs personnalis�s :
Il est ainsi possible de cr�er des groupes de validation suivant le contexte d'ex�cution : par exemple, valider la saisie d'une personne sur une IHM A ne n�cessite peut-�tre pas les m�mes v�rifications que valider une personne sur une IHM B. Pour ex�cuter la validation d'une instance pour un groupe donn� (par exemple "myGroup"), il faut appeler la fonction suivante : "qx::QxInvalidValueX invalidValues = qx::validate(personValidate, "myGroup");". Autre remarque : le module QxValidator d�finit des messages par d�faut lorsqu'une contrainte n'est pas v�rifi�e. Il est possible de red�finir ces messages par d�faut en modifiant la collection suivante : "QHash Par exemple : "lstMessage->insert("min_value", "la valeur '%NAME%' doit �tre inf�rieure ou �gale � '%CONSTRAINT%'");". Les champs %NAME% et %CONSTRAINT% seront automatiquement remplac�s par les valeurs correspondantes. Pour modifier le message pour un validateur donn� (et non de mani�re globale), il faut utiliser le param�tre optionnel disponible pour les m�thodes add_XXX() de la classe qx::IxValidatorX. G�rer la valeur NULL de la base de donn�es
Les bases de donn�es poss�dent la notion de valeur NULL : pour plus de d�tails sur la valeur
NULL, rendez-vous sur la
page Wikipedia.
La biblioth�que QxOrm permet de g�rer la valeur NULL de plusieurs fa�ons diff�rentes :
La classe boost::optional<T> fournie par boost est particuli�rement
adapt�e pour g�rer la notion de valeur NULL en base de donn�es.
Pour utiliser boost::optional<T> avec la biblioth�que QxOrm, il est n�cessaire de d�finir l'option de compilation _QX_ENABLE_BOOST, ou bien d'inclure l'en-t�te <QxExtras/QxBoostOptionalOnly.h>. Voici un exemple de classe dont toutes les propri�t�s (sauf la cl� primaire) peuvent �tre NULL en utilisant boost::optional :
La classe boost::optional<T> se manipule facilement : rendez-vous sur la documentation fournie par boost pour plus de d�tails.
La classe QVariant fournie
par Qt permet �galement de g�rer la notion de valeur NULL en base de donn�es.
Voici un exemple de classe dont toutes les propri�t�s (sauf la cl� primaire) peuvent �tre NULL en utilisant QVariant :
Cette solution a pour d�savantage de perdre le type de donn�e compar� � boost::optional<T>. Il est donc recommand� d'utiliser boost::optional<T> pour g�rer la valeur NULL avec la biblioth�que QxOrm.
On retrouve g�n�ralement dans les diff�rents outils de type ORM trois diff�rentes
strat�gies pour g�rer la notion d'h�ritage avec la base de donn�es :
De nombreux tutoriaux et forums sont disponibles sur internet pour plus de d�tails sur cette notion d'h�ritage. Un exemple d'utilisation avec une classe de base se trouve dans le dossier ./test/qxDllSample/dll2/ avec la classe BaseClassTrigger. Interface qx::IxPersistable (classe abstraite)
L'interface qx::IxPersistable (ou classe abstraite) dispose uniquement de
m�thodes virtuelles pures.
Elle permet d'avoir une classe de base commune pour appeler les fonctions de persistance sans conna�tre le type r�el de l'instance courante (notion de polymorphisme). La biblioth�que QxOrm n'impose pas de travailler avec une classe de base pour enregistrer un type persistant dans le contexte QxOrm, cependant il est parfois utile de disposer d'une interface afin d'�crire des algorithmes g�n�riques. La classe qx::IxPersistable met � disposition les m�thodes virtuelles suivantes (pour plus d'informations sur ces m�thodes, rendez-vous sur la documentation en ligne de la biblioth�que QxOrm) : virtual long qxCount(const qx::QxSqlQuery & query = qx::QxSqlQuery(), QSqlDatabase * pDatabase = NULL); virtual QSqlError qxFetchById(const QVariant & id = QVariant(), const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL); virtual QSqlError qxFetchAll(qx::IxCollection & list, const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL); virtual QSqlError qxFetchByQuery(const qx::QxSqlQuery & query, qx::IxCollection & list, const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL); virtual QSqlError qxInsert(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL); virtual QSqlError qxUpdate(const qx::QxSqlQuery & query = qx::QxSqlQuery(), const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL); virtual QSqlError qxSave(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL); virtual QSqlError qxDeleteById(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL); virtual QSqlError qxDeleteAll(QSqlDatabase * pDatabase = NULL); virtual QSqlError qxDeleteByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL); virtual QSqlError qxDestroyById(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL); virtual QSqlError qxDestroyAll(QSqlDatabase * pDatabase = NULL); virtual QSqlError qxDestroyByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL); virtual qx_bool qxExist(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL); virtual qx::QxInvalidValueX qxValidate(const QStringList & groups = QStringList()); virtual qx::IxPersistableCollection_ptr qxNewPersistableCollection() const; virtual qx::IxClass * qxClass() const; Par exemple, � partir d'une liste de pointeurs de type qx::IxPersistable, il est possible d'enregistrer les �l�ments dans plusieurs tables diff�rentes de la base de donn�es de la fa�on suivante :
Pour impl�menter l'interface qx::IxPersistable, il faut :
Remarque : le projet de test ./test/qxDllSample/dll1/ met � disposition une sorte de 'super classe de base' : la classe qx::QxPersistable impl�mente l'interface qx::IxPersistable et h�rite de QObject. Le m�canisme SIGNAL-SLOT de Qt peut donc �tre utilis� avec cette classe, ce qui peut �tre int�ressant par exemple pour la notion de d�clencheurs (ou trigger). La classe qx::QxPersistable met �galement � disposition des m�thodes virtuelles qu'il est possible de surcharger pour g�rer notamment la notion de validation des donn�es avec le module QxValidator. La classe qx::QxPersistable ne fait pas partie de la distribution de QxOrm, mais il est possible de la copier-coller dans un projet afin de profiter de ses fonctionnalit�s :
Utiliser le pattern C++ PIMPL (Private Implementation idiom ou d-pointer)
D�finition du site
cppreference : "Pointer to implementation" or "pImpl" is a C++ programming technique that
removes implementation details of a class from its object representation by placing them in a
separate class, accessed through an opaque pointer.
This technique is used to construct C++ library interfaces with stable ABI and to reduce
compile-time dependencies.
Les avantages � utiliser le pattern PIMPL pour d�finir une classe persistente enregistr�e dans le contexte QxOrm :
La biblioth�que QxOrm fournit un projet de test o� toutes les classes persistantes sont cod�es en utilisant le pattern PIMPL : qxBlogPImpl (avec gestion des relations). Il est �galement possible d'utiliser l'application QxEntityEditor pour g�n�rer facilement et automatiquement toutes les classes persistantes d'un projet C++ avec l'option PIMPL. Exemple de classe persistante C++ enregistr�e dans le contexte QxOrm en utilisant le pattern PIMPL (avec relations 1-n, n-1 et n-n) :
Persister des types personnalis�s
La biblioth�que QxOrm permet de persister n'importe quel type, m�me si ce dernier n'est pas
enregistr� dans le contexte QxOrm par la m�thode qx::register_class<T>().
Il est n�cessaire d'�crire les fonctions de s�rialisation de la biblioth�que boost, en utilisant la m�thode non intrusive (puisque le code source n'est pas disponible ou ne peut pas �tre modifi�). Pour plus d'informations sur la s�rialisation des donn�es avec la biblioth�que boost, rendez-vous sur le tutoriel de developpez.com. Par exemple, imaginons une classe 'ExtObject3D' provenant d'une biblioth�que tierce et dont le code source n'est pas disponible ou ne peut pas �tre modifi�. Voici le code n�cessaire pour pouvoir persister une instance de type 'ExtObject3D' en base de donn�es :
Le code ci-dessus est suffisant pour persister une instance de type 'ExtObject3D' en base de donn�es : il est ainsi possible d'utiliser une propri�t� de type 'ExtObject3D' dans une classe persistante enregistr�e dans le contexte QxOrm. Cette propri�t� peut �tre mapp�e sur une colonne de type TEXT ou VARCHAR en base de donn�es. Le comportement par d�faut de la biblioth�que QxOrm est le suivant : l'instance est s�rialis�e au format XML avant d'�tre ins�r�e ou mise � jour en base de donn�es. Ce comportement par d�faut peut �tre utile par exemple si l'on souhaite enregistrer une collection d'objets sans vouloir faire de relation (et donc g�rer une autre table dans la base de donn�es). Par exemple, si l'on utilise une propri�t� de type std::vector<mon_objet> dans une classe persistante sans relation associ�e, la liste d'�l�ments sera automatiquement enregistr�e au format XML en base de donn�es. Remarque : ce comportement par d�faut peut �tre facilement modifi� pour un type donn�. Le moteur QtSql utilise le type QVariant pour faire le lien entre le code C++ et la base de donn�es. Le type QVariant peut contenir du texte, des valeurs num�riques, du binaire, etc. Il peut donc �tre int�ressant de sp�cialiser le comportement par d�faut (s�rialisation XML) si l'on souhaite stocker des donn�es au format binaire ou bien optimiser les performances (la s�rialisation XML peut �tre couteuse en temps d'ex�cution). Il suffit de proposer (en plus des fonctions de s�rialisation boost) les conversions n�cessaires en QVariant, par exemple avec la classe 'ExtObject3D' :
Remarque : Voici un template pour cr�er un type personnalis� persistable :
G�n�rer le sch�ma DDL SQL de la base de donn�es
!!! Il est fortement recommand� d'utiliser
l'application QxEntityEditor pour g�rer cette probl�matique !!!
La biblioth�que QxOrm ne fournit pas de m�canisme pour g�rer automatiquement la cr�ation et mise � jour des tables dans la base de donn�es. En effet, la fonction qx::dao::create_table<T> doit �tre utilis�e uniquement pour cr�er des prototypes. Il est fortement recommand� d'utiliser un outil sp�cifique � chaque SGBD pour cr�er et maintenir les tables de la base de donn�es (par exemple Navicat pour MySql, pgAdmin pour PostgreSQL, SQLite Manager pour SQLite, etc.). De plus, un outil sp�cifique � chaque SGBD permet d'appliquer certaines optimisations (ajout d'index par exemple). Cependant, il peut �tre int�ressant pour certaines applications de ne pas avoir � g�rer manuellement les tables de la base de donn�es. Dans ce cas, il est possible de cr�er une fonction C++ pour parcourir la liste des classes persistantes enregistr�es dans le contexte QxOrm (en utilisant le moteur d'introspection de la biblioth�que) et ainsi cr�er un script SQL de g�n�ration et mise � jour des tables de la base de donn�es. La biblioth�que QxOrm fournit une fonction C++ cr��e uniquement � titre d'exemple : elle peut donc servir de base de travail pour cr�er sa propre fonction de g�n�ration de script SQL. Cette fonction se trouve dans le fichier ./src/QxRegister/QxClassX.cpp et se nomme QString qx::QxClassX::dumpSqlSchema(). Elle g�n�re un script SQL et le renvoie sous forme de QString : il est possible d'adapter cette fonction pour g�n�rer un fichier contenant le script SQL ou bien appliquer chaque instruction SQL directement au SGBD. Voici un exemple d'impl�mentation propos� par dodobibi pour g�rer une base PostgreSQL : cet exemple g�re les �volutions futures de son application (ajout de colonnes dans une table existante, ajout d'index sur une colonne existante, etc.). Au lancement de l'application, le num�ro de version est indiqu� de la fa�on suivante :
Une table de la base de donn�es permet de stocker le num�ro de version courant. Une classe persistante C++ est mapp�e sur cette table de la fa�on suivante :
Avec la classe DatabaseVersion, il est possible de v�rifier si la version de la base de donn�es est � jour. C'est le r�le de la fonction isDatabaseVersionOld() :
Si au lancement de l'application, la fonction isDatabaseVersionOld() renvoie true, alors la mise � jour de la base de donn�es est effectu�e de la fa�on suivante :
Remarque : le code pr�c�dent (tout comme la fonction qx::QxClassX::dumpSqlSchema()) peut �tre modifi� pour s'adapter aux besoins sp�cifiques d'une application. Par exemple, il pourrait �tre int�ressant de cr�er par d�faut une seconde table (en plus de la table DatabaseVersion) pour enregistrer la liste des classes persistantes enregistr�es dans le contexte QxOrm : ainsi, au lieu d'utiliser la fonction propos�e par Qt "db.tables()", il serait possible de r�cup�rer toutes les tables mapp�es sur des classes persistantes avec des informations suppl�mentaires (num�ro de version pour chaque table, nombre de colonnes enregistr�es dans le contexte QxOrm, description de chaque table, etc.). Associer un type SQL � une classe C++
Chaque base de donn�es propose des types SQL diff�rents pour stocker l'information.
La biblioth�que QxOrm propose une association par d�faut pour les classes C++ les plus fr�quemment utilis�es dans un programme. Voici cette association par d�faut :
Si le type SQL propos� par d�faut par la biblioth�que QxOrm ne correspond pas � la base de donn�es utilis�e, il peut facilement �tre modifi� (de mani�re globale � toute l'application) en utilisant la collection suivante :
Pour modifier le type SQL de mani�re sp�cifique pour une colonne d'une table de la base de donn�es, il faut d�finir le type SQL dans la fonction de mapping de QxOrm :
Pour les classes non support�es par d�faut par la biblioth�que QxOrm (voir ce chapitre du manuel utilisateur : Persister des types personnalis�s), il est possible d'associer un type SQL par d�faut en utilisant la macro suivante (en dehors de tout namespace) :
Effectuer des requ�tes asynchrones � la base de donn�es
Il peut �tre parfois int�ressant d'ex�cuter certaines requ�tes � la base de donn�es de mani�re
asynchrone (multi-thread), par exemple pour �viter de bloquer une IHM si une requ�te est trop
longue � s'ex�cuter.
Pour simplifier les requ�tes asynchrones, la biblioth�que QxOrm fournit la classe qx::QxDaoAsync. Cette classe ex�cute une requ�te dans un thread d�di� et renvoie un SIGNAL queryFinished() lorsque la requ�te est termin�e. Pour utiliser la classe qx::QxDaoAsync, il suffit de :
Et voici l'impl�mentation de la classe MyWidget :
Gestion du cache pour sauvegarder des instances C++ (module QxCache)
Le cache propos� par la biblioth�que QxOrm (module QxCache) est thread-safe et permet de stocker
facilement n'importe quel type de donn�es.
Les fonctions pour acc�der au cache se trouvent dans le namespace qx::cache. Le cache permet une optimisation du programme : il est possible par exemple de stocker des �l�ments issus d'une requ�te effectu�e en base de donn�es. Chaque �l�ment stock� dans le cache est associ� � une cl� de type QString : cette cl� permet de retrouver rapidement un �l�ment du cache. Si un nouvel �l�ment est stock� dans le cache avec une cl� qui existe d�j�, alors l'ancien �l�ment associ� � cette cl� est effac� automatiquement du cache. Le cache de la biblioth�que QxOrm ne g�re pas la dur�e de vie des objets : il n'y a aucun delete effectu� par le cache. C'est pourquoi il est fortement recommand� (mais ce n'est pas une obligation) de privil�gier le stockage de pointeurs intelligents : par exemple, boost::shared_ptr<T> pour la biblioth�que boost ou bien QSharedPointer<T> pour la biblioth�que Qt. Le cache peut avoir un co�t relatif maximum pour �viter une utilisation de la m�moire trop importante : chaque �l�ment ins�r� dans le cache peut indiquer un co�t repr�sentant une estimation de sa taille m�moire (par exemple, le nombre d'�l�ments d'une collection). Lorsque le co�t maximum du cache est atteint, les premiers �l�ments ins�r�s dans le cache sont supprim�s (en respectant l'ordre d'insertion dans le cache) jusqu'� ce que la limite du cache ne soit plus d�pass�e. Il est possible d'associer � chaque �l�ment du cache une date-heure d'insertion. Si aucune date-heure n'est renseign�e, alors la date-heure courante est prise en compte. Ce m�canisme permet de v�rifier si un �l�ment stock� dans le cache n�cessite une mise � jour ou non. Voici un exemple d'utilisation du cache de la biblioth�que QxOrm (fonctions du namespace qx::cache) :
Travailler avec plusieurs bases de donn�es
Dans le chapitre Connexion � la base de donn�es, nous avons vu comment
param�trer la connexion par d�faut avec la classe singleton : qx::QxSqlDatabase.
La biblioth�que QxOrm �tant bas�e sur le moteur QtSql de Qt, elle
utilise en interne la classe QSqlDatabase de Qt.
Toutes les fonctions d'acc�s � la base de donn�es (namespace qx::dao, classe
qx::QxSession, etc...)
ont un param�tre optionnel : QSqlDatabase * pDatabase = NULL :
D�clarer une classe abstraite dans le contexte QxOrm
Une classe abstraite C++ (contenant au moins une m�thode virtuelle pure) ne peut pas �tre mapp�e
avec une table d'une base de donn�es (puisqu'elle ne peut pas �tre instanci�e).
Cependant, il peut �tre int�ressant de d�finir une classe abstraite contenant une liste de propri�t�s utilis�es par plusieurs objets persistants. Un exemple de classe abstraite se trouve dans le dossier ./test/qxDllSample/dll2/ de la distribution de QxOrm avec la classe BaseClassTrigger. QxOrm propose le m�canisme suivant pour d�finir une classe abstraite dans le contexte QxOrm :
D�clarer automatiquement les m�ta-propri�t�s de Qt (macro Q_PROPERTY)
Toute classe h�ritant du type QObject peut d�clarer ses propri�t�s avec la macro Q_PROPERTY :
les propri�t�s deviennent alors des m�ta-propri�t�s.
Ce m�canisme permet au framework Qt de proposer un moteur d'introspection gr�ce au
pr�-compilateur moc.
Les m�ta-propri�t�s peuvent alors �tre utilis�es par exemple par le moteur QML,
QtScript, etc.
La biblioth�que QxOrm n�cessite une d�claration de chacune des propri�t�s d'une classe dans la fonction de mapping void qx::register_class<T>() afin de proposer l'ensemble de ses fonctionnalit�s (persistance des donn�es, s�rialisation XML, JSON et binaire, etc.). Il est possible de d�clarer automatiquement dans le contexte QxOrm l'ensemble des m�ta-propri�t�s sans maintenir une fonction de mapping void qx::register_class<T>() : la macro QX_REGISTER_ALL_QT_PROPERTIES() utilise le moteur d'introspection de Qt pour parcourir la liste des m�ta-propri�t�s. Voici un exemple d'utilisation avec la classe TestQtProperty se trouvant dans le dossier ./test/qxDllSample/dll1/include/ de la distribution QxOrm :
Pour ceux qui ne souhaitent pas utiliser la macro QX_REGISTER_ALL_QT_PROPERTIES, il est possible d'�crire � la place les quatre lignes de code suivantes :
Remarque : le deuxi�me param�tre de la macro QX_REGISTER_ALL_QT_PROPERTIES permet d'indiquer la propri�t� qui servira de cl� primaire dans la base de donn�es. Si ce param�tre est vide, cela signifie que la classe ne poss�de pas de cl� primaire ou bien que celle-ci est d�finie dans une classe de base. Toute propri�t� d�finie avec la macro Q_PROPERTY peut s'enregistrer dans le contexte QxOrm de deux mani�res diff�rentes : 1- par la m�thode classique : t.data(& MyQObject::my_property, "my_property", 0); 2- ou bien sans mentionner le pointeur vers la donn�e membre de la classe : t.data("my_property", 0); Peu importe la m�thode d'enregistrement des propri�t�s dans le contexte QxOrm, elles seront accessibles par la m�me interface qx::IxDataMember et proposent donc les m�mes fonctionnalit�s. Il est possible d'utiliser les deux m�thodes dans une m�me fonction de mapping void qx::register_class<T>(). Chaque m�thode d'enregistrement pr�sente des avantages et inconv�nients. Voici la liste des avantages de la deuxi�me m�thode d'enregistrement des propri�t�s dans le contexte QxOrm :
La s�rialisation est un m�canisme permettant de sauvegarder l'�tat d'une instance d'objet
dans un flux (fichier, r�seau, etc...) sous un certain format (binaire, XML, JSON, texte, etc...).
La d�s�rialisation est le processus inverse permettant de restaurer l'�tat d'un objet �
partir d'un flux.
Pour plus d'informations sur la notion de s�rialisation : rendez-vous sur la page
Wikipedia.
Toute classe C++ enregistr�e dans le contexte QxOrm peut �tre s�rialis�e dans diff�rents formats :
Autre remarque : par d�faut, toutes les propri�t�s enregistr�es dans le contexte QxOrm sont s�rialisables. Pour supprimer une propri�t� du moteur de s�rialisation, il est possible d'�crire :
N� version pour assurer une compatibilit� ascendante
La compatibilit� ascendante permet � une application de pouvoir d�s�rialiser un flux provenant
d'une version ant�rieure.
La biblioth�que QxOrm impose un num�ro de version par classe ainsi qu'un num�ro de version pour
chaque propri�t� enregistr�e dans le contexte QxOrm afin de pouvoir assurer une compatibilit�
ascendante automatiquement.
Par exemple, imaginons une classe person cr��e dans une application en version A : nous renseignons dans la macro QX_REGISTER_HPP une n� de version � 0 (correspond � la 1�re version de notre classe person), ainsi qu'un n� de version � 0 pour chacune des propri�t�s de la classe (si param�tre non renseign�, 0 est la valeur par d�faut). Ce qui donne le r�sultat suivant : * Fichier person.h :
* Fichier person.cpp :
Dans la version B de notre application, nous modifions la classe person pour ajouter 2 nouvelles propri�t�s : sex et address. Notre classe ayant �volu�e, il faut donc incr�menter son n� de version, et les nouvelles propri�t�s doivent avoir un n� de version � 1, ce qui donne : * Fichier person.h :
* Fichier person.cpp :
Remarque : en proc�dant ainsi, la biblioth�que QxOrm peut s�rialiser une instance de la classe person dans une application en version A, puis d�s�rialiser � partir de ce flux issu de la version A afin de recr�er une instance de la classe person dans une version B de l'application. Autre remarque : la suppression d'une propri�t� casse la compatibilit� ascendante. Il est donc recommand� de ne jamais supprimer de propri�t� pour utiliser le moteur de s�rialisation : il est possible par exemple de mettre une visibilit� � private et de supprimer les accesseurs get/set, la propri�t� devenant ainsi inaccessible � l'ext�rieur de la classe, elle peut alors �tre consid�r�e comme �tant obsol�te.
Toute classe C++ enregistr�e dans le contexte QxOrm peut �tre s�rialis�e en utilisant le moteur QDataStream de
Qt.
Les fonctions pour utiliser ce type de s�rialisation sont disponibles dans l'espace de nom : namespace
qx::serialization::qt.
Par exemple :
Remarque : dans l'exemple ci-dessus, nous s�rialisons une instance C++. Toutes les fonctions du namespace qx::serialization peuvent �galement s�rialiser des listes d'instances C++. Pour connaitre la liste des collections support�es, rendez-vous dans le chapitre : Collections support�es par QxOrm.
Toute classe C++ enregistr�e dans le contexte QxOrm peut �tre s�rialis�e en utilisant le moteur QJson de Qt (n�cessite
Qt5).
Les fonctions pour utiliser ce type de s�rialisation sont disponibles dans l'espace de nom : namespace
qx::serialization::json.
Autre remarque : la s�rialisation JSON est bas�e sur le moteur d'introspection de la biblioth�que QxOrm, elle est moins performante que les s�rialisations bas�es sur le moteur boost::serialization. Par exemple :
L'exemple ci-dessus g�n�re le flux JSON suivant : {
"author_id_2": {
"author_id": "author_id_2",
"birthdate": "2016-03-24",
"list_blog": [
],
"name": "author_2",
"sex": 1
},
"author_id_3": {
"author_id": "author_id_3",
"birthdate": "2016-03-24",
"list_blog": [
],
"name": "author_3",
"sex": 1
}
}
Remarque : le module QxRestApi de la biblioth�que QxOrm est bas� sur le moteur de s�rialisation JSON. Autre remarque : il est possible de personnaliser le format de sortie JSON (filtrer les propri�t�s du flux JSON g�n�r� par la s�rialisation). Les fonctions de s�rialisation JSON dispose d'un param�tre optionnel de type QString nomm� format. Les pr�-requis pour utiliser le param�tre format sont :
Exemple : voici un exemple de s�rialisation JSON en d�finissant un format de sortie pour filtrer certaines propri�t�s :
Moteur XML de boost::serialization
Le moteur XML de boost::serialization n'est pas activ� par d�faut : pour activer cette
fonctionnalit�, il est n�cessaire de d�finir les options de compilation
_QX_ENABLE_BOOST_SERIALIZATION et _QX_ENABLE_BOOST_SERIALIZATION_XML dans le fichier de configuration QxOrm.pri (ou QxOrm.cmake).
Il est �galement n�cessaire de compiler le binaire boost::serialization (ce module de boost n'�tant pas header only),
et de renseigner le chemin d'acc�s � ce binaire dans les variables QX_BOOST_LIB_PATH,
QX_BOOST_LIB_SERIALIZATION_DEBUG et QX_BOOST_LIB_SERIALIZATION_RELEASE du fichier de configuration QxOrm.pri (ou QxOrm.cmake).
Toute classe C++ enregistr�e dans le contexte QxOrm peut �tre s�rialis�e en utilisant le moteur XML de boost::serialization. Les fonctions pour utiliser ce type de s�rialisation sont disponibles dans l'espace de nom : namespace qx::serialization::xml (m�mes fonctions que dans l'espace de nom qx::serialization::qt). Ce type de s�rialisation poss�de les caract�ristiques suivantes :
Moteur binaire de boost::serialization
Le moteur binaire de boost::serialization n'est pas activ� par d�faut : pour activer cette
fonctionnalit�, il est n�cessaire de d�finir les options de compilation
_QX_ENABLE_BOOST_SERIALIZATION et _QX_ENABLE_BOOST_SERIALIZATION_BINARY dans le fichier de configuration QxOrm.pri (ou QxOrm.cmake).
Il est �galement n�cessaire de compiler le binaire boost::serialization (ce module de boost n'�tant pas header only),
et de renseigner le chemin d'acc�s � ce binaire dans les variables QX_BOOST_LIB_PATH,
QX_BOOST_LIB_SERIALIZATION_DEBUG et QX_BOOST_LIB_SERIALIZATION_RELEASE du fichier de configuration QxOrm.pri (ou QxOrm.cmake).
Toute classe C++ enregistr�e dans le contexte QxOrm peut �tre s�rialis�e en utilisant le moteur binaire de boost::serialization. Les fonctions pour utiliser ce type de s�rialisation sont disponibles dans l'espace de nom : namespace qx::serialization::binary (m�mes fonctions que dans l'espace de nom qx::serialization::qt). Ce type de s�rialisation poss�de les caract�ristiques suivantes :
Autres types de s�rialisation propos�s par boost
Le moteur boost::serialization propose d'autres types de s�rialisation.
Ces diff�rents types ne sont pas activ�s par d�faut, pour utiliser ces fonctionnalit�s (m�mes
fonctions que dans l'espace de nom qx::serialization::qt), il est
n�cessaire de d�finir les options de compilation suivantes dans le fichier
de configuration QxOrm.pri (ou QxOrm.cmake) :
Toute classe C++ enregistr�e dans le contexte QxOrm peut �tre clon�e en utilisant une des
fonctions suivantes :
Remarque importante : il faut faire attention lorsqu'on clone un pointeur intelligent (boost::shared_ptr ou QSharedPointer par exemple) dont l'�l�ment parent (root) peut �tre r�f�renc� plusieurs fois dans sa hi�rarchie (cas d'une structure en arbre par exemple). Dans ce cas, afin de prot�ger le pointeur parent d'une double suppression (2 pointeurs intelligents qui pointent sur le m�me pointeur brut), il est conseill� de cloner de cette fa�on :
Afficher le d�tail d'une instance C++ (dump au format XML ou JSON)
Toute instance C++ enregistr�e dans le contexte QxOrm peut �tre affich�e au format JSON.
Si le moteur XML de boost::serialization est activ�, alors il est
�galement possible d'afficher un dump sous format XML (param�tre d'entr�e de la fonction
qx::dump).
Cette fonctionnalit� peut �tre utile pour faire du d�bogage par exemple, ou bien pour g�n�rer
des logs.
Ce qui g�n�re le flux XML suivant : [QxOrm] start dump 'boost::shared_ptr<blog>' <boost.shared_ptr-blog- class_id="0" tracking_level="0" version="1"> <px class_id="1" tracking_level="1" version="0" object_id="_0"> <blog_id>113</blog_id> <blog_text class_id="2" tracking_level="0" version="0">update blog_text_1</blog_text> <date_creation class_id="3" tracking_level="0" version="0">20100409162612000</date_creation> <author_id class_id="4" tracking_level="0" version="1"> <px class_id="5" tracking_level="1" version="0" object_id="_1"> <author_id>author_id_2</author_id> <name>author_2</name> <birthdate class_id="6" tracking_level="0" version="0">20100409</birthdate> <sex>1</sex> <list_blog class_id="7" tracking_level="0" version="0"> <count>0</count> <item_version>1</item_version> </list_blog> </px> </author_id> <list_comment class_id="8" tracking_level="0" version="0"> <count>2</count> <item class_id="9" tracking_level="0" version="1"> <px class_id="10" tracking_level="1" version="0" object_id="_2"> <comment_id>209</comment_id> <comment_text>comment_1 text</comment_text> <date_creation>20100409162612000</date_creation> <blog_id> <px class_id_reference="1" object_id="_3"> <blog_id>113</blog_id> <blog_text></blog_text> <date_creation></date_creation> <author_id> <px class_id="-1"></px> </author_id> <list_comment> <count>0</count> </list_comment> <list_category class_id="11" tracking_level="0" version="0"> <count>0</count> </list_category> </px> </blog_id> </px> </item> <item> <px class_id_reference="10" object_id="_4"> <comment_id>210</comment_id> <comment_text>comment_2 text</comment_text> <date_creation>20100409162612000</date_creation> <blog_id> <px class_id_reference="1" object_id="_5"> <blog_id>113</blog_id> <blog_text></blog_text> <date_creation></date_creation> <author_id> <px class_id="-1"></px> </author_id> <list_comment> <count>0</count> </list_comment> <list_category> <count>0</count> </list_category> </px> </blog_id> </px> </item> </list_comment> <list_category> <count>2</count> <item class_id="12" tracking_level="0" version="0"> <first>355</first> <second class_id="13" tracking_level="0" version="0"> <qt_shared_ptr class_id="14" tracking_level="1" version="0" object_id="_6"> <category_id>355</category_id> <name>category_1</name> <description>desc_1</description> <list_blog class_id="15" tracking_level="0" version="0"> <count>0</count> </list_blog> </qt_shared_ptr> </second> </item> <item> <first>357</first> <second> <qt_shared_ptr class_id_reference="14" object_id="_7"> <category_id>357</category_id> <name>category_3</name> <description>desc_3</description> <list_blog> <count>0</count> </list_blog> </qt_shared_ptr> </second> </item> </list_category> </px> </boost.shared_ptr-blog-> [QxOrm] end dump 'boost::shared_ptr<blog>'
Toute classe enregistr�e dans le contexte QxOrm par la m�thode qx::register_class<T>()
peut �tre utilis�e par le moteur d'introspection (ou r�flexion) de la biblioth�que QxOrm.
Le moteur d'introspection permet d'obtenir de fa�on dynamique (donc pendant l'ex�cution du
programme) des informations propres � un type.
Ces informations correspondent � des m�ta-donn�es et d�crivent de fa�on exhaustive les
caract�ristiques d'une classe (propri�t�s, m�thodes, etc.).
De nombreux langages de programmation (par exemple Java ou C#) int�grent nativement ce m�canisme,
ce n'est pas le cas du C++, c'est pourquoi la biblioth�que QxOrm �mule un moteur d'introspection.
Pour plus de d�tails sur l'introspection (ou r�flexion), rendez-vous sur la page Wikipedia.
Voici la liste des classes disponibles pour acc�der aux m�ta-donn�es :
Le moteur d'introspection de la biblioth�que QxOrm permet par exemple de :
Remarque : le module QxService de la biblioth�que QxOrm (cliquez ici pour acc�der au tutoriel) permettant de cr�er un serveur d'applications C++ est bas� sur le moteur d'introspection pour appeler dynamiquement les m�thodes de type service (demande du client) sur le serveur, ainsi que pour cr�er dynamiquement les instances des classes de param�tre (entr�e/sortie). Autre remarque : il est possible d'ajouter de nouvelles informations au moteur d'introspection en utilisant la notion de property bag. En effet, les classes qx::IxClass, qx::IxDataMember et qx::IxFunction poss�dent chacune une liste d'�l�ments de type QVariant accessibles par cl� de type QString (voir la classe qx::QxPropertyBag pour plus de d�tails sur cette notion). Autre remarque : afin d'initialiser le moteur d'introspection QxOrm, il est recommand� d'appeler la fonction suivante en d�but de programme (main par exemple) :
Obtenir dynamiquement la valeur d'une donn�e membre
Pour obtenir dynamiquement la valeur d'une donn�e membre en utilisant le moteur d'introspection
de la biblioth�que QxOrm, il est n�cessaire de passer par la classe : qx::IxDataMember.
La classe qx::IxDataMember fournit plusieurs m�thodes pour obtenir la valeur d'une
donn�e membre (chacune prenant en param�tre un pointeur g�n�rique de type void *
correspondant � l'adresse de l'instance courante) :
Valoriser dynamiquement une donn�e membre
De la m�me fa�on que pour obtenir la valeur d'une donn�e membre, la classe qx::IxDataMember
permet de valoriser une donn�e membre (modifier sa valeur).
La classe qx::IxDataMember fournit les 2 m�thodes suivantes (chacune prend en
param�tre un pointeur de type void * correspondant � l'adresse de l'instance courante,
ainsi que la nouvelle valeur � positionner) :
Appeler dynamiquement une fonction
Tout comme les donn�es membre (propri�t�s), il est possible d'enregistrer des m�thodes membre
(fonctions) dans le contexte QxOrm (support des m�thodes static et non static).
Le moteur d'introspection de la biblioth�que QxOrm permet d'invoquer dynamiquement des m�thodes
de classe.
Toutes les fonctions enregistr�es dans le contexte QxOrm sont associ�es � une instance de la
classe : qx::IxFunction.
Pour enregistrer des m�thodes dans le contexte QxOrm, il faut utiliser :
* Fichier person.h :
* Fichier person.cpp :
Une fois enregistr�es dans le contexte QxOrm, il est possible d'appeler dynamiquement ces fonctions avec les m�thodes qx::QxClassX::invoke() et qx::QxClassX::invokeStatic() :
Cr�er une instance C++ dynamiquement
Le moteur d'introspection de la biblioth�que QxOrm permet de cr�er dynamiquement des instances
de classe (module
QxFactory, mod�le de conception fabrique ou design pattern factory) avec les m�thodes
suivantes :
Parcourir la liste des classes/propri�t�s enregistr�es dans le contexte QxOrm
Voici un exemple d'utilisation du moteur d'introspection de la biblioth�que QxOrm : comment
lister toutes les classes, propri�t�s et m�thodes enregistr�es dans le contexte QxOrm ?
Si on utilise la m�thode qx::QxClassX::dumpAllClasses() avec le tutoriel qxBlog, voici le r�sultat obtenu :
Services : transf�rer la couche de donn�es persistante sur le r�seau (module QxService)
Le module QxService de
la biblioth�que QxOrm permet de cr�er rapidement un serveur d'applications C++
performant (notion de services avec demande du client et r�ponse du serveur).
Un tutoriel est disponible sur
le site QxOrm afin de pr�senter un exemple d'utilisation du module QxService.
Le module QxService est bas�
sur le moteur d'introspection ainsi que le moteur de
s�rialisation de la biblioth�que QxOrm afin de transf�rer la couche de donn�es persistante
sur le r�seau et ex�cuter automatiquement les routines c�t� serveur.
Remarque : pour activer le module QxService, il faut d�finir l'option de compilation _QX_ENABLE_QT_NETWORK dans le fichier de configuration QxOrm.pri (ou QxOrm.cmake). Cette option de compilation ajoute une d�pendance au binaire QtNetwork fourni avec la biblioth�que Qt. Autre remarque : l'application QxEntityEditor est livr�e avec le plugin QxEECppServicesExport : ce plugin g�n�re automatiquement le code source n�cessaire pour transf�rer l'ensemble des entit�s d'un projet sur le r�seau. Une liste de m�thodes client/serveur est g�n�r�e automatiquement :
L'objectif de ce chapite est de pr�senter les concepts � mettre en oeuvre pour utiliser le module QxService :
Param�tres d'entr�e/sortie d'un service (requ�te/r�ponse)
Chaque fonction publi�e par un service dispose de param�tres d'entr�e (demande du client) et de
param�tres de sortie (r�ponse du serveur).
Ces param�tres d'entr�e/sortie doivent h�riter de l'interface qx::service::IxParameter et doivent �tre enregistr�es dans le contexte
QxOrm (par la fonction void qx::register_class<T>).
Par exemple : voici un exemple de param�tres d'entr�e/sortie g�n�r�s automatiquement par l'application QxEntityEditor bas� sur la classe blog du tutoriel qxBlog : * Fichier blog.services.gen.h :
* Fichier blog.services.gen.cpp :
Remarque : comme on peut le constater sur l'exemple ci-dessus, les param�tres d'entr�e/sortie peuvent contenir des types complexes (des collections, des pointeurs, etc...). Il est donc possible et tr�s simple de transf�rer des structures complexes sur le r�seau avec le module QxService. D�finir les fonctions publi�es par un service
Chaque service enregistr� dans le module QxService publie une liste de fonctions accessibles c�t� client (requ�tes
client/serveur).
Les services doivent h�riter de la classe de base qx::service::QxService<INPUT, OUTPUT> (les param�tres template
INPUT et OUTPUT correspondant aux param�tres
d'entr�e/sortie) et doivent �tre enregistr�s dans le contexte QxOrm (par la fonction
void qx::register_class<T>).
Par exemple : voici un exemple de service g�n�r� automatiquement par l'application QxEntityEditor bas� sur la classe blog du tutoriel qxBlog : * Fichier blog.services.gen.h :
* Fichier blog.services.gen.cpp :
Remarque : une fois d�finies dans le contexte QxOrm, le client peut appeler les fonctions publi�es par le service : les routines c�t� serveur sont alors ex�cut�es automatiquement. La s�rialisation des donn�es ainsi que la gestion de la couche r�seau pour le transfert des donn�es sont g�r�es de mani�re transparente par le module QxService. Liste des options disponibles c�t� serveur
Le serveur d'application C++ bas� sur le module QxService dispose de
plusieurs param�tres accessibles par la classe singleton qx::service::QxConnect :
Param�trage de la connexion c�t� client
La couche cliente bas�e sur le module QxService dispose de plusieurs param�tres accessibles par la classe
singleton qx::service::QxConnect :
Gestion de l'authentification dans un service
Il est classique d'impl�menter un contr�le au niveau du serveur pour v�rifier l'utilisateur
connect� � la couche cliente.
L'interface qx::service::IxService (classe de base de tous les services enregistr�s
par le module QxService)
fournit des m�thodes virtuelles qui peuvent �tre surcharg�es pour g�rer cette probl�matique :
Par exemple : voici une classe de base nomm�e ParameterAuthentication qui peut �tre utilis�e par tous les param�tres d'entr�e/sortie, cette classe fournit 3 propri�t�s login, password et token : * Fichier ParameterAuthentication.h :
* Fichier ParameterAuthentication.cpp :
Maintenant que l'on dispose d'une classe de base pour nos param�tres (ParameterAuthentication), nous allons cr�er une classe de base utilis�e par tous nos services nomm�e ServiceAuthentication<INPUT, OUTPUT>. Cette classe de base des services va surcharger la m�thode virtuelle onBeforeProcess() afin de g�rer l'authentification avant ex�cution de la routine serveur : * Fichier ServiceAuthentication.h :
A pr�sent, nous disposons des classes de base ParameterAuthentication et ServiceAuthentication<INPUT, OUTPUT> : toutes les classes de param�tres et toutes les classes de services doivent h�riter de ces classes de base pour g�rer automatiquement l'authentification, et retourner une erreur au client si les param�tres de l'utilisateur ne sont pas valides. Remarque : de la m�me fa�on que pour g�rer l'authentification, il est possible de mettre en place des logs automatiques en surchargeant les m�thodes virtuelles onBeforeProcess() et onAfterProcess(). Requ�tes client/serveur asynchrones
Par d�faut, les requ�tes client/serveur sont synchrones : ce qui signifie que la couche cliente
attend la r�ponse du serveur pour continuer son ex�cution.
Dans une interface graphique utilisateur (GUI), une requ�te client/serveur bloque
l'application (freeze) si elle est ex�cut�e dans le thread principal : si le serveur met
du temps pour renvoyer sa r�ponse, l'utilisateur peut alors penser qu'il s'agit d'un crash de
l'application.
La module QxService
propose une solution simple pour effectuer des requ�tes asynchrones (qui ne bloquent donc pas
l'interface graphique de l'utilisateur) gr�ce � la classe qx::service::QxClientAsync.
La classe qx::service::QxClientAsync utilise le moteur d'introspection de la biblioth�que QxOrm ainsi que le m�canisme SIGNAL-SLOT de Qt. Elle prend en param�tre :
Voici l'exemple issu du tutoriel qxClientServer qui ex�cute une routine serveur de mani�re asynchrone :
Remarque : l'exemple ci-dessus montre comment effectuer une requ�te asynchrone avec les actions suivantes :
Moteur mod�le/vue (module QxModelView)
Le module QxModelView permet d'utiliser le moteur model/view de Qt avec
toutes les classes enregistr�es dans le contexte QxOrm :
Remarque : le projet de test qxBlogModelView pr�sent dans le dossier ./test/ du package QxOrm montre comment cr�er rapidement un mod�le et l'associer au moteur model/view de Qt (d'abord dans un widget Qt, puis dans une vue QML). D�finir un mod�le "simple" (sans relation)
Toute classe enregistr�e dans le contexte QxOrm peut �tre utilis�e en tant que mod�le afin
d'alimenter des vues.
La classe de base qx::IxModel des mod�les QxOrm h�rite de la classe Qt QAbstractItemModel :
les mod�les QxOrm sont donc enti�rement compatibles avec le moteur model/view de Qt.
Une seule ligne de code est suffisante pour instancier un mod�le QxOrm :
Remarque : le mod�le cr�� avec cette ligne de code expose automatiquement toutes les propri�t�s enregistr�es dans le contexte QxOrm au moteur model/view. Mod�les avec relations (notion de mod�les imbriqu�s)
Adapter les relations entre classe (1-n, n-1 ou n-n) au moteur model/view
de Qt est complexe : la solution propos�e par la biblioth�que QxOrm est l'utilisation de mod�les imbriqu�s.
Pour plus de d�tails sur la notion de mod�les imbriqu�s, un tutoriel est disponible sur le site developpez.com.
Pour utiliser les relations (1-n, n-1 ou n-n) avec le module QxModelView, il est important de comprendre qu'il peut y avoir une hi�rarchie entre mod�les (un mod�le parent peut avoir plusieurs mod�les enfants associ�s, c'est la notion de mod�les imbriqu�s). Afin de pouvoir travailler avec des relations (mod�les imbriqu�s), il est n�cessaire de cr�er des classes mod�les qui h�ritent de : qx::QxModel<T>. Ainsi, toutes les propri�t�s simples (non relation) sont automatiquement expos�es aux vues (gr�ce � la classe de base), il reste � �crire uniquement les accesseurs pour acc�der aux relations. L'application QxEntityEditor est livr�e avec le plugin QxEECppModelViewExport : ce plugin g�n�re automatiquement le code source pour pouvoir travailler avec des mod�les imbriqu�s. Voici un exemple de code g�n�r� par l'application QxEntityEditor afin de cr�er un mod�le bas� sur la classe blog (voir le tutoriel qxBlog pour plus de d�tails). La classe blog dispose de 3 relations : author (n-1), list_of_comment (1-n) et list_of_category (n-n) : * Fichier blog.model_view.gen.h :
* Fichier blog.model_view.gen.cpp : namespace model_view { blog_model::blog_model(QObject * parent /* = 0 */) : blog_model_base_class(parent) { ; } blog_model::blog_model(qx::IxModel * other, QObject * parent) : blog_model_base_class(other, parent) { ; } blog_model::~blog_model() { ; } QObject * blog_model::author(int row, bool bLoadFromDatabase /* = false */, const QString & sAppendRelations /* = QString() */) { QString sRelation = "author"; qx::IxModel * pChild = (bLoadFromDatabase ? NULL : this->getChild(row, sRelation)); if (pChild) { return static_cast<QObject *>(pChild); } if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; } blog_model_base_class::type_ptr ptr = this->m_model.getByIndex(row); if (! ptr) { qAssert(false); return NULL; } long id = ptr->getblog_id(); blog::type_author value = ptr->getauthor(); if (bLoadFromDatabase) { if (! sAppendRelations.isEmpty() && ! sAppendRelations.startsWith("->") && ! sAppendRelations.startsWith(">>")) { sRelation += "->" + sAppendRelations; } else if (! sAppendRelations.isEmpty()) { sRelation += sAppendRelations; } blog tmp; tmp.setblog_id(id); this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp); if (this->m_lastError.isValid()) { return NULL; } value = tmp.getauthor(); ptr->setauthor(value); } model_view::author_model * pNewChild = NULL; pChild = qx::model_view::create_nested_model_with_type(this, QModelIndex(), value, pNewChild); if (pChild) { this->insertChild(row, "author", pChild); } return static_cast<QObject *>(pChild); } QObject * blog_model::list_of_comment(int row, bool bLoadFromDatabase /* = false */, const QString & sAppendRelations /* = QString() */) { QString sRelation = "list_of_comment"; qx::IxModel * pChild = (bLoadFromDatabase ? NULL : this->getChild(row, sRelation)); if (pChild) { return static_cast<QObject *>(pChild); } if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; } blog_model_base_class::type_ptr ptr = this->m_model.getByIndex(row); if (! ptr) { qAssert(false); return NULL; } long id = ptr->getblog_id(); blog::type_list_of_comment value = ptr->getlist_of_comment(); if (bLoadFromDatabase) { if (! sAppendRelations.isEmpty() && ! sAppendRelations.startsWith("->") && ! sAppendRelations.startsWith(">>")) { sRelation += "->" + sAppendRelations; } else if (! sAppendRelations.isEmpty()) { sRelation += sAppendRelations; } blog tmp; tmp.setblog_id(id); this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp); if (this->m_lastError.isValid()) { return NULL; } value = tmp.getlist_of_comment(); ptr->setlist_of_comment(value); } model_view::comment_model * pNewChild = NULL; pChild = qx::model_view::create_nested_model_with_type(this, QModelIndex(), value, pNewChild); if (pChild) { this->insertChild(row, "list_of_comment", pChild); } return static_cast<QObject *>(pChild); } QObject * blog_model::list_of_category(int row, bool bLoadFromDatabase /* = false */, const QString & sAppendRelations /* = QString() */) { QString sRelation = "list_of_category"; qx::IxModel * pChild = (bLoadFromDatabase ? NULL : this->getChild(row, sRelation)); if (pChild) { return static_cast<QObject *>(pChild); } if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; } blog_model_base_class::type_ptr ptr = this->m_model.getByIndex(row); if (! ptr) { qAssert(false); return NULL; } long id = ptr->getblog_id(); blog::type_list_of_category value = ptr->getlist_of_category(); if (bLoadFromDatabase) { if (! sAppendRelations.isEmpty() && ! sAppendRelations.startsWith("->") && ! sAppendRelations.startsWith(">>")) { sRelation += "->" + sAppendRelations; } else if (! sAppendRelations.isEmpty()) { sRelation += sAppendRelations; } blog tmp; tmp.setblog_id(id); this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp); if (this->m_lastError.isValid()) { return NULL; } value = tmp.getlist_of_category(); ptr->setlist_of_category(value); } model_view::category_model * pNewChild = NULL; pChild = qx::model_view::create_nested_model_with_type(this, QModelIndex(), value, pNewChild); if (pChild) { this->insertChild(row, "list_of_category", pChild); } return static_cast<QObject *>(pChild); } void blog_model::syncNestedModel(int row, const QStringList & relation) { Q_UNUSED(relation); qx::IxModel * pNestedModel = NULL; if ((row < 0) || (row >= this->m_model.count())) { return; } blog_model_base_class::type_ptr ptr = this->m_model.getByIndex(row); if (! ptr) { return; } pNestedModel = this->getChild(row, "author"); if (pNestedModel) { this->syncNestedModelRecursive(pNestedModel, relation); blog::type_author value; qx::model_view::sync_nested_model(pNestedModel, value); ptr->setauthor(value); } pNestedModel = this->getChild(row, "list_of_comment"); if (pNestedModel) { this->syncNestedModelRecursive(pNestedModel, relation); blog::type_list_of_comment value; qx::model_view::sync_nested_model(pNestedModel, value); ptr->setlist_of_comment(value); } pNestedModel = this->getChild(row, "list_of_category"); if (pNestedModel) { this->syncNestedModelRecursive(pNestedModel, relation); blog::type_list_of_category value; qx::model_view::sync_nested_model(pNestedModel, value); ptr->setlist_of_category(value); } } void blog_model::syncAllNestedModel(const QStringList & relation) { if (this->m_lstChild.count() <= 0) { return; } for (long l = 0; l < this->m_model.count(); l++) { this->syncNestedModel(static_cast<int>(l), relation); } } } // namespace model_view Remarque : comme on peut le constater sur l'exemple ci-dessus, le code source � �crire pour travailler avec des mod�les imbriqu�s est verbeux. Afin de travailler avec les relations, il est donc fortement recommand� d'utiliser l'application QxEntityEditor afin de g�n�rer le code source automatiquement.
Voici un exemple en QML (en Qt5, le module QxModelView
�tant �galement compatible avec Qt4) qui utilise la table 'author' d�finie dans le tutoriel qxBlog (le code source de cet
exemple QML est disponible dans le projet de test qxBlogModelView pr�sent dans le package
QxOrm) :
Et voici le contenu du fichier 'main.qml' :
Ce qui donne le r�sultat suivant � l'ex�cution : ![]() Remarque : comme on peut le constater dans le fichier 'main.qml', les propri�t�s 'author_id' et 'name' du mod�le 'author' (variable myModel) sont accessibles automatiquement en lecture/�criture (car elles ont �t� enregistr�es dans le contexte QxOrm). De plus, l'interface qx::IxModel propose une liste de m�thodes accessibles en QML (utilisation de Q_INVOKABLE) pour communiquer directement avec la base de donn�es : ainsi, le bouton 'Save' de l'�cran ci-dessus enregistre le mod�le en base de donn�es depuis QML. Autre remarque : un plugin de l'application QxEntityEditor permet de g�n�rer automatiquement le code des mod�les pour la gestion des relations. Il est ainsi possible de travailler avec des mod�les imbriqu�s (pour plus de d�tails sur la notion de mod�les imbriqu�s, rendez-vous sur ce tutoriel du site developpez.com). Int�raction avec les vues QtWidget
Voici un exemple de cr�ation d'un mod�le pour afficher/modifier les donn�es de la table
'author' (voir le tutoriel qxBlog
pour la d�finition de la classe 'author') dans un QTableView (le code source
de cet exemple est disponible dans le projet de test qxBlogModelView pr�sent dans le
package QxOrm) :
Ce qui donne le r�sultat suivant � l'ex�cution : ![]() Remarque : Qt propose par d�faut plusieurs vues QtWidget qui peuvent �tre mapp�es sur un mod�le, par exemple : QListView, QTableView, QTreeView. Il est �galement possible d'utiliser la classe QDataWidgetMapper pour cr�er ses propres formulaires bas�s sur des mod�les (un tutoriel est disponible sur le site developpez.com). Connexion d'un mod�le au module QxService
Le module QxModelView
fournit la classe template : qx::QxModelService<T, S> (qui h�rite de : qx::QxModel<T>
>> qx::IxModel
>> QAbstractItemModel).
Cette classe dispose de 2 param�tres template :
Remarque : l'application QxEntityEditor est livr�e avec les plugins QxEECppServicesExport et QxEECppModelViewExport : ces plugins g�n�rent automatiquement tout le code source n�cessaire pour travailler avec des mod�les qui utilisent le module QxService. Afin de travailler avec la classe qx::QxModelService<T, S>, il est donc fortement recommand� d'utiliser l'application QxEntityEditor afin de g�n�rer le code source automatiquement. QxOrm et MongoDB (C++ ODM Object Document Mapper)
En plus des bases de donn�es relationnelles classiques (MySQL, PostgreSQL, SQLite, Oracle,
Microsoft SQLServer, MariaDB, etc...), la biblioth�que QxOrm supporte �galement la base de donn�es MongoDB.
D�finition du site Wikipedia : MongoDB est un syst�me de gestion de base de donn�es orient�e documents, r�partissable sur un nombre quelconque d'ordinateurs et ne n�cessitant pas de sch�ma pr�d�fini des donn�es. Il fait partie de la mouvance NoSQL. La base de donn�es MongoDB pr�sente de nombreux avantages par rapport � une base de donn�es relationnelle classique (liste non exhaustive) :
L'utilisation de la biblioth�que QxOrm avec MongoDB est tr�s proche des bases de donn�es relationnelles classiques. Toutes les fonctionnalit�s de la biblioth�que QxOrm sont support�es avec MongoDB : donc tout ce qui se trouve dans ce manuel utilisateur est disponible avec une base de donn�es MongoDB. Les principales diff�rences � prendre en compte sont :
Remarque : le package QxOrm fournit un projet de test nomm� qxBlogMongoDB (dans le dossier ./test/). Ce projet montre comment se connecter � une base de donn�es MongoDB avec la biblioth�que QxOrm. Pr�-requis : driver libmongoc et libbson
Le module QtSql fourni par
le framework Qt sur lequel est bas� la biblioth�que QxOrm ne propose pas de connecteur � une
base de donn�es MongoDB.
La biblioth�que QxOrm a donc besoin de 2 d�pendances suppl�mentaires pour se connecter � une
base MongoDB :
Un guide d'installation est disponible pour installer ces 2 biblioth�ques sur votre environnement de d�veloppement. Param�trage du fichier QxOrm.pri (ou QxOrm.cmake)
Une fois que les biblioth�ques libmongoc et libbson sont correctement install�es
sur votre environnement de d�veloppement, il est n�cessaire de param�trer le fichier de
configuration QxOrm.pri (ou QxOrm.cmake) en activant l'option de compilation
_QX_ENABLE_MONGODB.
Remarque : une fois l'option de compilation _QX_ENABLE_MONGODB activ�e, il est possible de compiler et ex�cuter le projet de test qxBlogMongoDB du dossier ./test/ afin de valider l'environnement de d�veloppement. Connexion � la base de donn�es MongoDB
Voici un exemple de param�trage pour se connecter � une base de donn�es MongoDB :
D�finition d'une classe persistante MongoDB (Collection) dans le contexte QxOrm (mapping)
D�clarer une classe persistante MongoDB dans le contexte QxOrm est �quivalent � d�clarer une classe persistante pour une base de donn�es relationnelle
classique.
Voici un exemple du projet de test qxBlogMongoDB :
Fichier blog.h :
Fichier blog.cpp :
Remarque : l'exemple ci-dessus montre comment d�finir : Gestion des cl�s primaires ObjectId
Comme indiqu� en jaune dans l'exemple pr�c�dent, il est conseill� de d�finir en C++ une cl�
primaire de type QString.
Il n'y a pas de notion de cl� num�rique auto-incr�ment�e : MongoDB
utilise un type ObjectId qui peut �tre mapp� en C++ avec QString et g�n�r�
automatiquement (il est �galement possible de d�finir son propre type C++ pour mapper un
ObjectId MongoDB).
Ins�rer une instance C++ (Document) dans la base de donn�es MongoDB (INSERT)
Voici un exemple d'insertion de document avec g�n�ration automatique de la cl� primaire (de type
MongoDB
ObjectId) :
Voici un exemple d'insertion de document avec une cl� primaire personnalis�e :
Ins�rer une liste d'instances C++ (plusieurs Documents) dans la base de donn�es MongoDB (INSERT)
Voici un exemple d'insertion de plusieurs documents dans la base de donn�es MongoDB :
Remarque : QxOrm supporte plusieurs types C++ de listes / collections. Mettre � jour une instance C++ (Document) dans la base de donn�es MongoDB (UPDATE)
Voici un exemple de mise � jour d'un document dans la base MongoDB :
Voici un exemple de mise � jour de plusieurs documents dans la base MongoDB :
Remarque : QxOrm supporte plusieurs types C++ de listes / collections. Supprimer une instance C++ (Document) de la base de donn�es MongoDB (DELETE)
Voici un exemple de suppression d'un document de la base MongoDB :
Supprimer une liste d'instances C++ (plusieurs Documents) de la base de donn�es MongoDB (DELETE)
Voici un exemple de suppression de plusieurs documents de la base MongoDB par identifiant
(cl� primaire) :
Voici un exemple de suppression de plusieurs documents de la base MongoDB par requ�te JSON :
Pour supprimer tous les documents de la Collection author :
Remarque : QxOrm supporte plusieurs types C++ de listes / collections. R�cup�rer une instance C++ (Document) de la base de donn�es MongoDB (FETCH)
Voici un exemple pour r�cup�rer (FETCH) un document de la base MongoDB par identifiant (cl�
primaire) :
R�cup�rer une liste d'instances C++ (plusieurs Documents) de la base de donn�es MongoDB (FETCH)
Voici un exemple pour r�cup�rer (FETCH) plusieurs documents de la base MongoDB par
identifiant (cl� primaire) :
Voici un exemple pour r�cup�rer (FETCH) plusieurs documents de la base MongoDB par requ�te JSON :
Voici un exemple pour r�cup�rer (FETCH) tous les documents de la Collection author de la base MongoDB :
Voici un exemple pour r�cup�rer (FETCH) tous les documents de la Collection author de la base MongoDB (en s�lectionnant les propri�t�s/colonnes � r�cup�rer) :
Remarque : QxOrm supporte plusieurs types C++ de listes / collections.
La principale diff�rence entre une base de donn�es relationnelle classique et MongoDB est la
fa�on de requ�ter les donn�es : � la place du SQL, MongoDB propose un moteur de
requ�te JSON.
Utilisation de la classe qx::QxSqlQuery (ou son alias qx_query)
La classe qx::QxSqlQuery (ou son alias qx_query) utilis�e pour
construire du SQL classique est �galement compatible pour construire des requ�tes JSON
MongoDB.
Cette classe utilise la fonctionnalit� C++11 std::initializer_list afin d'�crire les
requ�tes en C++ avec une syntaxe proche du JSON (il est �galement possible d'�crire la
requ�te sous forme de cha�ne de caract�res).
Par exemple :
Utiliser le moteur d'aggregation MongoDB
La base de donn�es MongoDB fournit �galement un puissant moteur
d'aggregation qui �tend encore plus les possibilit�s pour requ�ter les donn�es.
Voici comment utiliser ce moteur d'aggregation avec la classe qx::QxSqlQuery (ou son alias qx_query), le 1er param�tre du
constructeur doit �tre �gal � aggregate :
Ajouter des propri�t�s � la requ�te de type : 'sort', 'limit', 'skip', etc...
Il est souvent n�cessaire de limiter les donn�es, ou bien de les trier par exemple.
Pour effectuer ces op�rations, la base de donn�es MongoDB utilise la notion de projection.
Voici un exemple d'utilisation avec la classe qx::QxSqlQuery (ou son
alias qx_query), avec une QStringList en constructeur (ou bien 2�me param�tre std::initializer_list) :
Ex�cuter une requ�te personnalis�e
Il est possible d'ex�cuter une requ�te personnalis�e avec la fonction
qx::dao::call_query().
Le r�sultat de la requ�te peut �tre facilement convertie en QVariantMap ou bien
QList<QVariantMap> (si la requ�te retourne un curseur) afin de faciliter la
lecture des r�sultats de la requ�te personnalis�e.
Voici quelques exemples d'appels de requ�tes personnalis�es :
Moteur de relations (n�cessite une version MongoDB 3.6 ou +)
Le moteur de relations de la biblioth�que QxOrm est compatible avec la
base de donn�es MongoDB (version 3.6 minimale).
QxOrm est donc capable de r�cup�rer les donn�es d'un Document sur plusieurs Collections en une
seule requ�te.
Voici un exemple pour r�cup�rer un Document et toutes ses relations sur 1 niveau de profondeur (parent > enfants) :
Voici un exemple pour r�cup�rer un Document et toutes ses relations sur 4 niveaux de profondeur (utilisation de la syntaxe *->*->*->*) :
Voici un exemple pour r�cup�rer un Document en s�lectionnant les relations et propri�t�s � alimenter (utilisation de la syntaxe { <col_1>, <col_2>, etc... }) :
Voici un exemple pour r�cup�rer un Document en s�lectionnant les relations et les propri�t�s � ne pas alimenter (utilisation de la syntaxe -{ <col_1>, <col_2>, etc... }) :
Relations : Embedded vs Referenced
Un des points forts de la base de donn�es MongoDB est de pouvoir stocker des structures
complexes de donn�es (on n'est pas limit� � une structure en tableau table/colonne des bases
de donn�es relationnelles classiques).
Un Document MongoDB peut donc contenir un objet et plusieurs sous-objets (notion de
hi�rarchie dans la structure du Document).
Inclure un sous-objet dans un m�me Document pr�sente des avantages (aucune jointure par
exemple, donc plus rapide � r�cup�rer) et inconv�nients (un m�me objet peut �tre dupliqu�
plusieurs fois dans la base).
Il est donc important de r�fl�chir sur la strat�gie � adopter pour stocker les donn�es.
La biblioth�que QxOrm supporte les 2 fa�ons de proc�der :
Cr�ation automatique des index
La biblioth�que QxOrm fournit une m�thode pour g�n�rer automatiquement les index (cette fonction
peut �tre appel�e en d�but de programme, par exemple dans le main) :
Serveur web HTTP/HTTPS (module QxHttpServer)
La biblioth�que QxOrm fournit un serveur web compatible HTTP 1.1 autonome (aucune n�cessit�
d'installer une application tierce comme Apache ou Nginx),
performant (multi-thread) et simple d'utilisation : il s'agit du module QxHttpServer (bas�
sur le module QxService).
Le module QxHttpServer supporte de nombreuses fonctionnalit�s :
Remarque : pour activer le module QxHttpServer, il faut d�finir l'option de compilation _QX_ENABLE_QT_NETWORK dans le fichier de configuration QxOrm.pri (ou QxOrm.cmake). Cette option de compilation ajoute une d�pendance au binaire QtNetwork fourni avec la biblioth�que Qt. Autre remarque : le package QxOrm est livr� avec le projet de test qxBlogRestApi. Ce projet de test est une application web avec de nombreux exemples pour requ�ter les donn�es persistantes depuis une page web (HTML et Javascript).
Voici le code source d'une application web bas�e sur le module QxHttpServer (cette
application renvoie Hello World ! au navigateur web client) :
R�sultat : ouvrir un navigateur web (Chrome, Firefox, Safari, Internet Explorer, Opera, etc...) et aller � l'adresse http://localhost:9642/, l'�cran suivant doit apparaitre :
Param�trage du serveur web HTTP
Les param�tres du serveur web HTTP sont accessibles avec la classe singleton qx::service::QxConnect :
La classe singleton qx::service::QxConnect fournit �galement des param�tres pour g�rer les
connexions s�curis�es HTTPS (SSL et/ou TLS).
Voici un exemple de param�trage de connexions s�curis�es avec certificat serveur et autorit� de certification (voir le projet de test qxBlogRestApi pour tester ce code) :
Remarque : par d�faut, toutes les erreurs SSL sont ignor�es (erreurs souvent li�es � des probl�mes de certificats). Pour adapter votre serveur web � vos normes de s�curit�, vous pouvez utiliser les fonctions suivantes (classe singleton qx::service::QxConnect) :
Routage des URL (d�finir les endpoints / dispatcher)
Le module QxHttpServer fournit un moteur de routage des URL (dispatcher) pour
d�finir les fonctions (ou lambda) � ex�cuter en fonction des param�tres de la requ�te HTTP
(m�thode HTTP GET, POST, DELETE, etc... + URL demand�e).
Les fonctions (ou lambda) doivent avoir cette signature : void myRequestHandler(qx::QxHttpRequest & request, qx::QxHttpResponse & response); La classe qx::QxHttpServer (ou son alias qx_http_server) dispose des m�thodes suivantes :
Autre remarque : chaque fonction (ou lambda) est ex�cut�e dans son propre thread . Le serveur web HTTP fourni par la biblioth�que QxOrm peut donc g�rer plusieurs requ�tes HTTP simultan�ment. Exemple n�1 : cette r�gle de routage intercepte toutes les requ�tes de type GET dont l'URL commence par /files/, et renvoie comme r�ponse le contenu d'un fichier statique stock� sur le serveur (QDir::currentPath() indique le r�pertoire parent du stockage des fichiers statiques, et 5000 correspond � la taille pour l'envoi des fichiers par bloc / chunked response, ce dernier param�tre �tant optionnel) :
Exemple n�2 : cette r�gle de routage intercepte toutes les requ�tes de type POST dont l'URL est /qx, et utilise le module QxRestApi (qui propose une API JSON pour requ�ter les donn�es persistantes). Ces 2 exemples (avec l'exemple n�1 qui renvoie des fichiers statiques) sont suffisants pour d�marrer une application web de type SPA (Single-Page Applications) avec les c�l�bres frameworks Javascript comme AngularJS, React, Meteor.js, etc...
Exemple n�3 : cette r�gle de routage intercepte toutes les requ�tes de type GET dont l'URL est /test_big_json, et construit une r�ponse de type JSON contenant un tableau de 10000 �l�ments :
Remarque : l'ordre dans lequel est appel� les diff�rentes fonctions dispatch() est important. Le 1er �l�ment trouv� par le dispatcher correspondant � l'URL demand�e est ex�cut� (et ignore les �l�ments suivants). Il est donc indispensable de d�finir en 1er les URL les plus sp�cifiques, jusqu'aux URL les plus g�n�riques (par exemple, le pattern correspondant � toutes les URL est /*).
Le dispatcher du module QxHttpServer supporte �galement la notion de routage dynamique
des URL.
Il est possible de d�finir des variables au niveau de l'URL � router avec la syntaxe : <var_name:var_type> (var_type �tant optionnel, et peut prendre comme valeur : int, long, float, double, string). Le routage dynamique est particuli�rement utile pour mettre en place une API REST. Par exemple, le pattern /blog/<blog_id:int> associ� � la m�thode HTTP GET peut �tre utilis� pour r�cup�rer les donn�es d'un blog en fonction de son identifiant de type num�rique (fetch_by_id). Il faut voir l'URL comme une liste de segments dont le s�parateur est le caract�re /. Le dispatcher v�rifie que chaque segment de l'URL demand�e correspond au pattern utilis� afin de valider une fonction (ou lambda) � ex�cuter. Pour r�cup�rer les valeurs des param�tres de l'URL, il faut utiliser : request.dispatchParams().value("var_name") (retourne un QVariant). Exemple : la r�gle de routage suivante intercepte toutes les requ�tes de type GET dont l'URL commence /params/, suivi par un segment qui sera associ� � la variable var1, suivi par un segment de type num�rique associ� � la variable var2. Elle construit une r�ponse qui renvoie la valeur des 2 param�tres var1 et var2. Si le navigateur web appelle l'URL /params/abc/123/ alors la fonction (ou lambda) sera ex�cut�e, par contre si le navigateur web appelle l'URL /params/abc/def/ alors la fonction (ou lambda) ne sera pas ex�cut�e (car def n'est pas num�rique) et cherchera un autre �l�ment du dispatcher :
Remarque : il est �galement possible de d�finir une expression r�guli�re pour router les URL avec la syntaxe : <var_name:{my_reg_exp}> . R�cup�rer les param�tres de la requ�te HTTP
La classe qx::QxHttpRequest (ou son alias qx_http_request) contient tous les param�tres d'appel de la
requ�te HTTP :
La classe qx::QxHttpResponse (ou son alias qx_http_response) permet de g�n�rer la r�ponse HTTP avec les
m�thodes suivantes :
Sessions (stockage par client c�t� serveur)
Les sessions HTTP sont un m�canisme c�t� serveur web permettant de stocker des donn�es
sp�cifiques � un client.
Ces donn�es sont accessibles pendant un laps de temps pour toutes les requ�tes envoy�es par le
client.
Lors du 1er acc�s � une session pour un client, un cookie HTTP
contenant un identifiant unique est g�n�r� et associ� automatiquement � la r�ponse HTTP.
Par la suite, toutes les requ�tes HTTP envoy�es par le client contiendront automatiquement un cookie HTTP avec l'identifiant unique calcul� pr�c�demment.
Lorsqu'une session n'est plus utilis�e pendant un certain laps de temps, alors elle est d�truite
automatiquement.
La classe qx::QxHttpSession (ou son alias qx_http_session) repr�sente une session HTTP c�t� serveur. Une session est accessible avec le singleton qx::QxHttpSessionManager :
Remarque : la classe qx::QxHttpSession contient une hash-map (QHash<QByteArray, QVariant>) pouvant stocker n'importe quelle valeur. Autre remarque : la dur�e (en milli-secondes) pour supprimer une session non utilis�e est param�tr�e par la m�thode : qx::service::QxConnect::setSessionTimeOut().
Les cookies HTTP sont un
m�canisme d'�change de donn�es entre client HTTP et serveur HTTP.
Les cookies sont utilis�s par exemple pour :
Les classes qx::QxHttpRequest et qx::QxHttpResponse disposent des m�thodes n�cessaires pour lire les cookies envoy�s par le navigateur web client ou bien g�n�rer un cookie dans la r�ponse HTTP. Par exemple :
Gestion des fichiers statiques
La classe qx::QxHttpServer (ou son alias qx_http_server) dispose d'une m�thode statique permettant
d'envoyer au navigateur web client des fichiers stock�s sur le serveur (par exemple : fichiers
HTML, Javascript, CSS, images PNG, JPEG, vid�os, etc...).
Encodage de transfert en bloc (chunked responses)
D�finition du
site Wikipedia : Chunked transfer encoding (ou Encodage de transfert en bloc) est un
m�canisme de transfert de donn�es de la version 1.1 du protocole Hypertext Transfer Protocol
(HTTP), qui permet � un serveur ou � un client de commencer � transmettre des donn�es par blocs
sans avoir � conna�tre � l'avance la taille totale des donn�es qui seront transmises.
Dans le protocole HTTP, l'en-t�te "Content-Length" peut remplacer la directive "Chunked transfer
encoding" d�crite ici.
La taille en octets de chaque bloc est envoy�e, sous forme de texte en hexadecimal, juste avant
le bloc lui-m�me afin que le serveur puisse dire au client quand il a fini de recevoir les
donn�es de ce bloc.
Le transfert total d'un fichier encod� par blocs se termine par un bloc final au contenu nul.
L'introduction de l'encodage de transfert en bloc du protocole HTTP 1.1 a fourni un certain nombre d'avantages :
La classe qx::QxHttpResponse dispose de la m�thode qx_bool writeChunked(const QByteArray & data). Cette m�thode permet d'envoyer la r�ponse par bloc. Elle est utilis�e par exemple pour envoyer des fichiers statiques volumineux :
Remarque : le 1er appel de la m�thode response.writeChunked() d�clenche automatiquement l'envoi de tous les en-t�tes HTTP de la r�ponse. Il faut donc d�finir tous les en-t�tes de la r�ponse HTTP avant d'appeler response.writeChunked() pour la 1�re fois. Requ�tes par les API JSON (module QxRestApi)
Le module QxRestApi propose une API JSON g�n�rique pour requ�ter les
donn�es persistantes (op�rations CRUD, requ�tes complexes avec plusieurs niveaux de relations,
possibilit� de d�finir un format de sortie JSON, appels dynamiques � des fonctions natives C++,
validation d'instances, requ�tes personnalis�es � la base de donn�es).
Ce manuel utilisateur dispose d'un chapitre entier d�di� au module QxRestApi : il contient notamment de nombreux exemples d'utilisation. En combinant le module QxRestApi et le module QxHttpServer : vous avez tous les outils n�cessaires pour d�velopper des applications web modernes. Par exemple, des applications web de type SPA (Single-Page Applications) avec les c�l�bres frameworks Javascript comme AngularJS, React, Meteor.js, etc... Remarque : le package QxOrm contient un projet de test qxBlogRestApi. Ce projet pr�sente la cr�ation d'un serveur web HTTP avec QxOrm, et �galement l'�criture de la partie cliente en HTML + Javascript (avec utilisation de jQuery). Par exemple, voici la fonction Javascript utilis�e pour envoyer les requ�tes JSON (m�thode POST) depuis le client (navigateur web) vers le serveur web HTTP QxOrm (toutes les requ�tes sont envoy�es � la m�me adresse /qx) :
C�t� serveur, la r�ception et le traitement de ces requ�tes est tr�s simple : la classe qx::QxHttpServer (ou son alias qx_http_server) dispose de la m�thode statique qx::QxHttpServer::buildResponseQxRestApi() :
Voici un exemple de requ�te JSON envoy�e par le navigateur web, elle r�cup�re la liste des tous les blogs de la base de donn�es (fetch_all) : {
"request_id": "2b393e4c-a00c-45dc-a279-e9d76f1c55cf",
"action": "fetch_all",
"entity": "blog"
}
Voici la r�ponse JSON renvoy�e par le serveur web HTTP contenant la liste de blogs : {
"data": [
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-03-27T20:51:23.107",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 2,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-03-27T20:51:23.107",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 3,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-03-27T20:51:23.107",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 4,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-03-27T20:51:23.107",
"list_category": [],
"list_comment": []
}
],
"request_id": "2b393e4c-a00c-45dc-a279-e9d76f1c55cf"
}
D�finition du site Wikipedia
: le protocole WebSocket vise � d�velopper un canal de communication full-duplex sur un
socket TCP pour les navigateurs et les serveurs web et permet :
La biblioth�que QxOrm est bas�e sur le framework Qt qui dispose d�j� d'une impl�mentation WebSocket. La mise en place d'un serveur web avec les WebSockets Qt est tr�s simple : il y a plusieurs exemples dans la documentation Qt. Il est donc tout � fait possible d'impl�menter un serveur web avec :
Remarque : une connexion WebSocket �tant g�n�ralement cr��e par le code Javascript du navigateur web client, le fait d'avoir 2 ports ouverts sur le serveur web n'est pas un probl�me. Performance (test� avec Apache Benchmark)
Voici les r�sultats d'un test de performance r�alis� avec les param�tres suivants :
Le r�sultat indique que le serveur web HTTP QxOrm peut g�rer plus de 12000 requ�tes par seconde :
Am�liorer les performances avec epoll dispatcher sous Linux
Sous Linux, il est possible d'ameliorer significativement les performances du serveur web
HTTP en utilisant le m�canisme
epoll pour g�rer les socket.
Par d�faut, le framework Qt utilise un autre m�canisme (select) plus lent, mais donne
la possibilit� de d�finir une autre gestion d'�v�nements.
Plusieurs biblioth�ques existent, par exemple :
La classe qx::QxHttpServer (ou son alias qx_http_server) dispose de la m�thode suivante pour d�finir des �v�nements bas�s sur epoll (� appeler avant le d�marrage du serveur web) :
API REST JSON (module QxRestApi)
Le module QxRestApi est une API JSON pour g�rer (de fa�on g�n�rique) la couche de donn�es
persistantes (base de donn�es) ou appeler des fonctions natives C++
(enregistr�es dans le contexte QxOrm).
Le module QxRestApi est bas� sur un m�canisme requ�te/r�ponse : envoi d'une requ�te au
format JSON et r�ception d'une r�ponse au format JSON.
Le module QxRestApi est particuli�rement adapt� pour d�velopper des services
REST.
Le module QxRestApi supporte les fonctionnalit�s suivantes :
Le module QxRestApi est tr�s simple d'utilisation : la classe qx::QxRestApi
permet d'utiliser les API JSON avec une seule m�thode : processRequest().
Pr�requis : pour pouvoir utiliser le module QxRestApi, les classes enregistr�es dans le contexte QxOrm doivent impl�menter l'interface qx::IxPersistable. La structure d'une requ�te JSON est g�n�rique et contient les �l�ments suivants : {
"request_id" : // [optional] unique identifier generated by client to associate response to request (if provided by caller, then the response will contain the same unique identifier)
"action" : // [required] what is the action to execute on the server
"entity" : // [optional or required depending on action] C++ class registered in QxOrm context
"data" : // [optional or required depending on action] data in JSON format needed to execute action
"columns" : // [optional] list of columns to fetch or update (if empty, means all columns)
"relations" : // [optional] list of relationships to fetch or save
"query" : // [optional or required depending on action] query to execute on database
"output_format" : // [optional] output fields for the response (filter), if empty then response will contain all fields
"fct" : // [required only with action 'call_entity_function'] used to call C++ native functions
"save_mode" : // [optional] used only with action 'save' to define insert or update or check both insert/update
}
La r�ponse JSON contient les �l�ments suivants : {
"request_id" : // unique identifier generated by client's request (if any)
"data" : // contain the response data
"error" : // if an error occured, then contain a code and description of the error
}
Plusieurs langages de programmation supporte le JSON nativement (Javascript, PHP, Python,
etc...).
Le module QxRestApi permet ainsi une interop�rabilit� entre la biblioth�que QxOrm et
d'autres applications utilisant d'autres technologies (autre que C++/Qt par exemple).
Le module QxRestApi peut �tre utilis� :
Projet de test qxBlogRestApi (QML et serveur web HTTP)
Le package QxOrm est livr� avec un projet de test nomm� qxBlogRestApi (dans le dossier
./test/qxBlogRestApi/).
Ce projet de test montre 2 cas d'utilisation du module QxRestApi :
Ces 2 fen�tres sont d�velopp�es avec 2 technologies diff�rentes (QML versus HTML + Javascript), mais proposent exactement les m�mes fonctionnalit�s :
R�cup�ration de donn�es (fetch/count/exist)
Ce chapitre d�taille les diff�rentes m�thodes pour r�cup�rer les donn�es issues de la base de
donn�es :
L'action fetch_all permet de r�cup�rer tous les �l�ments d'une table de la base
de donn�es (et �ventuellement les relations associ�es sur plusieurs
niveaux).
-- Exemple n�1 -- r�cup�rer tous les blogs (sous forme de liste) : Requ�te JSON : {
"request_id": "5e988bac-c812-4cb1-b0d8-6a2c9dc4478b",
"action": "fetch_all",
"entity": "blog"
}
{
"data": [
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 2,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 3,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 4,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
],
"request_id": "5e988bac-c812-4cb1-b0d8-6a2c9dc4478b"
}
-- Exemple n�2 -- r�cup�rer tous les blogs (sous forme de collection type hash-map avec cl�/valeur) : Requ�te JSON : {
"request_id": "ad400135-19fd-40e0-8034-201be6a2ff7a",
"action": "fetch_all",
"entity": "blog",
"data": [
{
"key": "",
"value": ""
}
]
}
{
"data": [
{
"key": 1,
"value": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
},
{
"key": 2,
"value": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 2,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
},
{
"key": 3,
"value": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 3,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
},
{
"key": 4,
"value": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 4,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
}
],
"request_id": "ad400135-19fd-40e0-8034-201be6a2ff7a"
}
-- Exemple n�3 -- r�cup�rer tous les blogs et toutes les relations associ�es sur 2 niveaux : Requ�te JSON : {
"request_id": "cf9ea2a8-3e41-438f-9a48-bbc8593d2b99",
"action": "fetch_all",
"entity": "blog",
"relations": [
"*->*"
]
}
{
"data": [
{
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"list_blog": [
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 2,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 3,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 4,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
],
"name": "author name modified at index 1 => container is dirty !!!",
"sex": 1
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [
{
"key": 1,
"value": {
"category_id": 1,
"description": "desc_1",
"list_blog": [
{
"key": 1,
"value": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
}
],
"name": "category_1"
}
},
{
"key": 3,
"value": {
"category_id": 3,
"description": "desc_3",
"list_blog": [
{
"key": 1,
"value": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
}
],
"name": "category_3"
}
}
],
"list_comment": [
{
"blog_id": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
"comment_id": 1,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"blog_id": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
"comment_id": 3,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"blog_id": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
"comment_id": 5,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"blog_id": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
"comment_id": 7,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"blog_id": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
"comment_id": 2,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"blog_id": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
"comment_id": 4,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"blog_id": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
"comment_id": 6,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"blog_id": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
"comment_id": 8,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
}
]
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"list_blog": [
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 2,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 3,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 4,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
],
"name": "author name modified at index 1 => container is dirty !!!",
"sex": 1
},
"blog_id": 2,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [
{
"key": 4,
"value": {
"category_id": 4,
"description": "desc_1",
"list_blog": [
{
"key": 2,
"value": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 2,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
}
],
"name": "category_1"
}
},
{
"key": 5,
"value": {
"category_id": 5,
"description": "desc_3",
"list_blog": [
{
"key": 2,
"value": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 2,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
}
],
"name": "category_3"
}
}
],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"list_blog": [
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 2,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 3,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 4,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
],
"name": "author name modified at index 1 => container is dirty !!!",
"sex": 1
},
"blog_id": 3,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [
{
"key": 6,
"value": {
"category_id": 6,
"description": "desc_1",
"list_blog": [
{
"key": 3,
"value": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 3,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
}
],
"name": "category_1"
}
},
{
"key": 7,
"value": {
"category_id": 7,
"description": "desc_3",
"list_blog": [
{
"key": 3,
"value": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 3,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
}
],
"name": "category_3"
}
}
],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"list_blog": [
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 2,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 3,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 4,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
],
"name": "author name modified at index 1 => container is dirty !!!",
"sex": 1
},
"blog_id": 4,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [
{
"key": 8,
"value": {
"category_id": 8,
"description": "desc_1",
"list_blog": [
{
"key": 4,
"value": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 4,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
}
],
"name": "category_1"
}
},
{
"key": 9,
"value": {
"category_id": 9,
"description": "desc_3",
"list_blog": [
{
"key": 4,
"value": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 4,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
}
],
"name": "category_3"
}
}
],
"list_comment": []
}
],
"request_id": "cf9ea2a8-3e41-438f-9a48-bbc8593d2b99"
}
-- Exemple n�4 -- r�cup�rer tous les blogs et plusieurs relations associ�es en d�finissant un format de sortie (toutes les propri�t�s ne feront pas partie de la r�ponse JSON) : Requ�te JSON : {
"request_id": "4c45fdf9-8001-4509-bb4b-ce27a4a8708a",
"action": "fetch_all",
"entity": "blog",
"relations": [
"<blog_alias> { blog_text }",
"author_id <author_alias> { name, birthdate }",
"list_comment <list_comment_alias> { comment_text } -> blog_id <blog_alias_2> -> * <..._my_alias_suffix>"
],
"output_format": [
"{ blog_text }",
"author_id { name, birthdate }",
"list_comment { comment_text } -> blog_id -> *"
]
}
{
"data": [
{
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"name": "author name modified at index 1 => container is dirty !!!"
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"list_comment": [
{
"blog_id": {
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"name": "author name modified at index 1 => container is dirty !!!",
"sex": 1
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [
{
"key": 1,
"value": {
"category_id": 1,
"description": "desc_1",
"name": "category_1"
}
},
{
"key": 3,
"value": {
"category_id": 3,
"description": "desc_3",
"name": "category_3"
}
}
],
"list_comment": [
{
"comment_id": 1,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 3,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 5,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 7,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 2,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 4,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 6,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 8,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
}
]
},
"comment_id": 1,
"comment_text": "comment_1 text"
},
{
"blog_id": {
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"name": "author name modified at index 1 => container is dirty !!!",
"sex": 1
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [
{
"key": 1,
"value": {
"category_id": 1,
"description": "desc_1",
"name": "category_1"
}
},
{
"key": 3,
"value": {
"category_id": 3,
"description": "desc_3",
"name": "category_3"
}
}
],
"list_comment": [
{
"comment_id": 1,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 3,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 5,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 7,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 2,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 4,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 6,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 8,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
}
]
},
"comment_id": 3,
"comment_text": "comment_1 text"
},
{
"blog_id": {
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"name": "author name modified at index 1 => container is dirty !!!",
"sex": 1
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [
{
"key": 1,
"value": {
"category_id": 1,
"description": "desc_1",
"name": "category_1"
}
},
{
"key": 3,
"value": {
"category_id": 3,
"description": "desc_3",
"name": "category_3"
}
}
],
"list_comment": [
{
"comment_id": 1,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 3,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 5,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 7,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 2,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 4,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 6,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 8,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
}
]
},
"comment_id": 5,
"comment_text": "comment_1 text"
},
{
"blog_id": {
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"name": "author name modified at index 1 => container is dirty !!!",
"sex": 1
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [
{
"key": 1,
"value": {
"category_id": 1,
"description": "desc_1",
"name": "category_1"
}
},
{
"key": 3,
"value": {
"category_id": 3,
"description": "desc_3",
"name": "category_3"
}
}
],
"list_comment": [
{
"comment_id": 1,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 3,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 5,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 7,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 2,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 4,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 6,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 8,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
}
]
},
"comment_id": 7,
"comment_text": "comment_1 text"
},
{
"blog_id": {
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"name": "author name modified at index 1 => container is dirty !!!",
"sex": 1
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [
{
"key": 1,
"value": {
"category_id": 1,
"description": "desc_1",
"name": "category_1"
}
},
{
"key": 3,
"value": {
"category_id": 3,
"description": "desc_3",
"name": "category_3"
}
}
],
"list_comment": [
{
"comment_id": 1,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 3,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 5,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 7,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 2,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 4,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 6,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 8,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
}
]
},
"comment_id": 2,
"comment_text": "comment_2 text"
},
{
"blog_id": {
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"name": "author name modified at index 1 => container is dirty !!!",
"sex": 1
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [
{
"key": 1,
"value": {
"category_id": 1,
"description": "desc_1",
"name": "category_1"
}
},
{
"key": 3,
"value": {
"category_id": 3,
"description": "desc_3",
"name": "category_3"
}
}
],
"list_comment": [
{
"comment_id": 1,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 3,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 5,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 7,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 2,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 4,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 6,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 8,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
}
]
},
"comment_id": 4,
"comment_text": "comment_2 text"
},
{
"blog_id": {
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"name": "author name modified at index 1 => container is dirty !!!",
"sex": 1
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [
{
"key": 1,
"value": {
"category_id": 1,
"description": "desc_1",
"name": "category_1"
}
},
{
"key": 3,
"value": {
"category_id": 3,
"description": "desc_3",
"name": "category_3"
}
}
],
"list_comment": [
{
"comment_id": 1,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 3,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 5,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 7,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 2,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 4,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 6,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 8,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
}
]
},
"comment_id": 6,
"comment_text": "comment_2 text"
},
{
"blog_id": {
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"name": "author name modified at index 1 => container is dirty !!!",
"sex": 1
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [
{
"key": 1,
"value": {
"category_id": 1,
"description": "desc_1",
"name": "category_1"
}
},
{
"key": 3,
"value": {
"category_id": 3,
"description": "desc_3",
"name": "category_3"
}
}
],
"list_comment": [
{
"comment_id": 1,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 3,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 5,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 7,
"comment_text": "comment_1 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 2,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 4,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 6,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
},
{
"comment_id": 8,
"comment_text": "comment_2 text",
"date_creation": "2019-04-01T16:18:54"
}
]
},
"comment_id": 8,
"comment_text": "comment_2 text"
}
]
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"name": "author name modified at index 1 => container is dirty !!!"
},
"blog_id": 2,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"name": "author name modified at index 1 => container is dirty !!!"
},
"blog_id": 3,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"name": "author name modified at index 1 => container is dirty !!!"
},
"blog_id": 4,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"list_comment": []
}
],
"request_id": "4c45fdf9-8001-4509-bb4b-ce27a4a8708a"
}
L'action fetch_by_id permet de r�cup�rer les donn�es d'un �l�ment d'une table
en fonction de son identifiant unique.
-- Exemple n�1 -- r�cup�rer les donn�es du blog qui a pour identifiant unique 1 : Requ�te JSON : {
"request_id": "4d6fbb9e-e088-482a-abfa-4e7ddee80569",
"action": "fetch_by_id",
"entity": "blog",
"data": {
"blog_id": 1
}
}
{
"data": {
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
"request_id": "4d6fbb9e-e088-482a-abfa-4e7ddee80569"
}
-- Exemple n�2 -- r�cup�re uniquement quelques donn�es du blog qui a pour identifiant unique 1 (les autres donn�es font partie du JSON mais avec une valeur vide ou null) : Requ�te JSON : {
"request_id": "72c9b362-d194-410e-98ed-23797a34318e",
"action": "fetch_by_id",
"entity": "blog",
"data": {
"blog_id": 1
},
"columns": [
"blog_text",
"date_creation"
]
}
{
"data": {
"author_id": null,
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
"request_id": "72c9b362-d194-410e-98ed-23797a34318e"
}
-- Exemple n�3 -- r�cup�re une liste de blogs en fonction de leur identifiant : Requ�te JSON : {
"request_id": "59c37f70-26ee-42e5-9177-b32c331adce1",
"action": "fetch_by_id",
"entity": "blog",
"data": [
{
"blog_id": 1
},
{
"blog_id": 2
},
{
"blog_id": 3
}
]
}
{
"data": [
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 2,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 3,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
],
"request_id": "59c37f70-26ee-42e5-9177-b32c331adce1"
}
-- Exemple n�4 -- r�cup�re une liste de blogs (avec quelques relations associ�es) en fonction de leur identifiant, et d�fini un format de sortie (toutes les propri�t�s ne feront pas partie de la r�ponse JSON) : Requ�te JSON : {
"request_id": "325d64f4-29ac-47ab-9846-d6a71a9e9d73",
"action": "fetch_by_id",
"entity": "blog",
"data": [
{
"blog_id": 1
},
{
"blog_id": 2
}
],
"relations": [
"{ blog_text }",
"author_id <author_alias> { name, birthdate }",
"list_comment <list_comment_alias> { comment_text }"
],
"output_format": [
"{ blog_text }",
"author_id { name, birthdate }",
"list_comment { comment_text }"
]
}
{
"data": [
{
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"name": "author name modified at index 1 => container is dirty !!!"
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"list_comment": [
{
"comment_id": 1,
"comment_text": "comment_1 text"
},
{
"comment_id": 2,
"comment_text": "comment_2 text"
},
{
"comment_id": 3,
"comment_text": "comment_1 text"
},
{
"comment_id": 4,
"comment_text": "comment_2 text"
},
{
"comment_id": 5,
"comment_text": "comment_1 text"
},
{
"comment_id": 6,
"comment_text": "comment_2 text"
},
{
"comment_id": 7,
"comment_text": "comment_1 text"
},
{
"comment_id": 8,
"comment_text": "comment_2 text"
}
]
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"name": "author name modified at index 1 => container is dirty !!!"
},
"blog_id": 2,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"list_comment": []
}
],
"request_id": "325d64f4-29ac-47ab-9846-d6a71a9e9d73"
}
L'action fetch_by_query permet de r�cup�rer les �l�ments d'une table filtr�s par une requ�te.
-- Exemple n�1 -- r�cup�re uniquement les �l�ments de la table author dont le sexe est de type female (female == enum dont la valeur est 1) : Requ�te JSON : {
"request_id": "c178194c-a76f-4a77-af12-2b97fc7078e4",
"action": "fetch_by_query",
"entity": "author",
"query": {
"sql": "WHERE author.sex = :sex",
"params": [
{
"key": ":sex",
"value": 1
}
]
}
}
{
"data": [
{
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"list_blog": [],
"name": "author name modified at index 1 => container is dirty !!!",
"sex": 1
},
{
"author_id": "author_id_3",
"birthdate": "1998-03-06",
"list_blog": [],
"name": "author_3",
"sex": 1
}
],
"request_id": "c178194c-a76f-4a77-af12-2b97fc7078e4"
}
-- Exemple n�2 -- r�cup�re uniquement les �l�ments de la table author (et toutes ses relations associ�es) dont le sexe est de type female : Requ�te JSON : {
"request_id": "84e2e13a-0bf9-4d78-b655-970568a97e4c",
"action": "fetch_by_query",
"entity": "author",
"query": {
"sql": "WHERE author.sex = :sex",
"params": [
{
"key": ":sex",
"value": 1,
"type": "in"
}
]
},
"relations": [
"*"
]
}
{
"data": [
{
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"list_blog": [
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 2,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 3,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_2",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 4,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54",
"list_category": [],
"list_comment": []
}
],
"name": "author name modified at index 1 => container is dirty !!!",
"sex": 1
},
{
"author_id": "author_id_3",
"birthdate": "1998-03-06",
"list_blog": [],
"name": "author_3",
"sex": 1
}
],
"request_id": "84e2e13a-0bf9-4d78-b655-970568a97e4c"
}
-- Exemple n�3 -- r�cup�re uniquement les �l�ments de la table author (et toutes ses relations associ�es) dont le sexe est de type female, et d�fini un format de sortie (toutes les propri�t�s ne feront pas partie de la r�ponse JSON) : Requ�te JSON : {
"request_id": "c18b59e7-54f9-4a4f-843d-f0797f4fb676",
"action": "fetch_by_query",
"entity": "author",
"query": {
"sql": "WHERE author.sex = :sex",
"params": [
{
"key": ":sex",
"value": 1,
"type": "in"
}
]
},
"relations": [
"*"
],
"output_format": [
"{ birthdate, name }",
"list_blog { blog_text, date_creation }"
]
}
{
"data": [
{
"author_id": "author_id_2",
"birthdate": "2019-04-01",
"list_blog": [
{
"blog_id": 1,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54"
},
{
"blog_id": 2,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54"
},
{
"blog_id": 3,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54"
},
{
"blog_id": 4,
"blog_text": "blog property 'text' modified => blog is dirty !!!",
"date_creation": "2019-04-01T16:18:54"
}
],
"name": "author name modified at index 1 => container is dirty !!!"
},
{
"author_id": "author_id_3",
"birthdate": "1998-03-06",
"list_blog": [],
"name": "author_3"
}
],
"request_id": "c18b59e7-54f9-4a4f-843d-f0797f4fb676"
}
L'action count permet de compter les �l�ments d'une table avec ou sans requ�te
(et avec ou sans relation).
-- Exemple n�1 -- compter le nombre de blogs dans la base de donn�es : Requ�te JSON : {
"request_id": "1ef62fd7-d847-4d67-9fd0-0207af463aa4",
"action": "count",
"entity": "blog"
}
{
"data": {
"count": 4
},
"request_id": "1ef62fd7-d847-4d67-9fd0-0207af463aa4"
}
-- Exemple n�2 -- compter tous les author dont le sexe est de type female : Requ�te JSON : {
"request_id": "a80646d1-5a42-46fb-9306-3b91c7f594c8",
"action": "count",
"entity": "author",
"query": {
"sql": "WHERE author.sex = :sex",
"params": [
{
"key": ":sex",
"value": 1
}
]
}
}
{
"data": {
"count": 2
},
"request_id": "a80646d1-5a42-46fb-9306-3b91c7f594c8"
}
-- Exemple n�3 -- compter tous les blogs dont l'author est de type female : Requ�te JSON : {
"request_id": "6ef252f7-385c-465e-8304-b9afa9fea490",
"action": "count",
"entity": "blog",
"query": {
"sql": "WHERE author_alias.sex = :sex",
"params": [
{
"key": ":sex",
"value": 1
}
]
},
"relations": [
"author_id <author_alias> { sex }"
]
}
{
"data": {
"count": 4
},
"request_id": "6ef252f7-385c-465e-8304-b9afa9fea490"
}
L'action exist permet de tester l'existence d'un ou plusieurs �l�ments d'une
table en fonction de l'identifiant.
-- Exemple n�1 -- tester l'existence d'un blog dont l'identifiant unique a pour valeur 1 : Requ�te JSON : {
"request_id": "e8db33db-b249-4349-93fe-ad12e208520e",
"action": "exist",
"entity": "blog",
"data": {
"blog_id": 1
}
}
{
"data": {
"exist": true
},
"request_id": "e8db33db-b249-4349-93fe-ad12e208520e"
}
-- Exemple n�2 -- tester l'existence de plusieurs blogs : Requ�te JSON : {
"request_id": "f2d6ca3f-36de-4920-8f4c-c04842603467",
"action": "exist",
"entity": "blog",
"data": [
{
"blog_id": 1
},
{
"blog_id": 999
},
{
"blog_id": 3
}
]
}
{
"data": {
"exist": false
},
"request_id": "f2d6ca3f-36de-4920-8f4c-c04842603467"
}
-- Exemple n�3 -- tester l'existence d'un author : Requ�te JSON : {
"request_id": "2c7df172-8010-4816-b8e1-3edbb0b0b90e",
"action": "exist",
"entity": "author",
"data": {
"author_id": "author_id_2"
}
}
{
"data": {
"exist": true
},
"request_id": "2c7df172-8010-4816-b8e1-3edbb0b0b90e"
}
L'action insert permet d'ins�rer un ou plusieurs �l�ments dans la base de donn�es.
Les identifiants uniques g�n�r�s par la base de donn�es (par exemple identifiant
auto-incr�ment�) sont fournis dans la r�ponse JSON.
-- Exemple n�1 -- ins�rer un blog dans la base de donn�es : Requ�te JSON : {
"request_id": "573e4940-607a-4037-8a09-11ec52deb21c",
"action": "insert",
"entity": "blog",
"data": {
"blog_text": "this is a new blog from QxOrm REST API !",
"date_creation": "2018-01-30T12:42:01",
"author_id": "author_id_2"
}
}
{
"data": {
"blog_id": 5
},
"request_id": "573e4940-607a-4037-8a09-11ec52deb21c"
}
-- Exemple n�2 -- ins�rer une liste de blogs dans la base de donn�es : Requ�te JSON : {
"request_id": "6ade2d01-086c-45d6-971b-b65e8836475f",
"action": "insert",
"entity": "blog",
"data": [
{
"blog_text": "new blog from QxOrm REST API !",
"date_creation": "2018-01-30T12:42:01",
"author_id": "author_id_2"
},
{
"blog_text": "another blog from QxOrm REST API !",
"date_creation": "2016-06-12T08:33:12",
"author_id": "author_id_1"
}
]
}
{
"data": [
{
"blog_id": 6
},
{
"blog_id": 7
}
],
"request_id": "6ade2d01-086c-45d6-971b-b65e8836475f"
}
-- Exemple n�3 -- ins�rer un author dans la base de donn�es : Requ�te JSON : {
"request_id": "0cffa916-99f4-4395-bccd-02918a4b3c57",
"action": "insert",
"entity": "author",
"data": {
"author_id": "author_id_from_rest_api",
"birthdate": "1978-05-11",
"name": "new author created by QxOrm REST API",
"sex": 1
}
}
{
"data": {
"author_id": "author_id_from_rest_api"
},
"request_id": "0cffa916-99f4-4395-bccd-02918a4b3c57"
}
Remarque : l'identifiant unique de la table author doit �tre fourni par l'appelant (non auto-incr�ment�). Si on rejoue une 2�me fois la m�me requ�te, on obtient l'erreur suivante : {
"error": {
"code": 19,
"desc": "Unable to fetch row\ncolumn author_id is not unique"
},
"request_id": "0cffa916-99f4-4395-bccd-02918a4b3c57"
}
Mise � jour de donn�es (update)
L'action update permet une mise � jour de un ou plusieurs �l�ments dans la base de
donn�es.
-- Exemple n�1 -- mise � jour du blog avec pour identifiant unique la valeur 1 : Requ�te JSON : {
"request_id": "4fa24a7f-a3d8-4bbf-85c1-c86df83dec0b",
"action": "update",
"entity": "blog",
"data": {
"blog_id": 1,
"blog_text": "modify blog from QxOrm REST API",
"date_creation": "2013-11-25T09:56:33",
"author_id": "author_id_1"
}
}
{
"data": {
"blog_id": 1
},
"request_id": "4fa24a7f-a3d8-4bbf-85c1-c86df83dec0b"
}
-- Exemple n�2 -- mise � jour uniquement de certaines colonnes d'un blog : Requ�te JSON : {
"request_id": "d0704db1-5c3a-48ad-b27e-14aa54ac0efb",
"action": "update",
"entity": "blog",
"data": {
"blog_id": 2,
"blog_text": "modify blog from QxOrm REST API",
"date_creation": "2013-11-25T09:56:33"
},
"columns": [
"blog_text",
"date_creation"
]
}
{
"data": {
"blog_id": 2
},
"request_id": "d0704db1-5c3a-48ad-b27e-14aa54ac0efb"
}
-- Exemple n�3 -- mise � jour de plusieurs author : Requ�te JSON : {
"request_id": "26ec3a7b-cf2d-47f7-bab7-db303f15ee51",
"action": "update",
"entity": "author",
"data": [
{
"author_id": "author_id_from_rest_api",
"birthdate": "1992-11-03",
"name": "modify author from QxOrm REST API",
"sex": 0
},
{
"author_id": "author_id_1",
"birthdate": "1978-12-25",
"name": "modify another author from QxOrm REST API",
"sex": 2
}
]
}
{
"data": [
{
"author_id": "author_id_from_rest_api"
},
{
"author_id": "author_id_1"
}
],
"request_id": "26ec3a7b-cf2d-47f7-bab7-db303f15ee51"
}
L'action save permet d'ins�rer ou mettre � jour (insert ou update)
un ou plusieurs �l�ments dans la base de donn�es.
En cas d'insertion, les identifiants uniques g�n�r�s par la base de donn�es (par exemple
identifiant auto-incr�ment�) sont fournis dans la r�ponse JSON.
La requ�te JSON dispose d'un param�tre optionnel nomm� save_mode qui peut prendre les valeurs suivantes :
-- Exemple n�1 -- sauvegarde (ins�re ou met � jour suivant l'identifant unique) un blog dans la base de donn�es : Requ�te JSON : {
"request_id": "ec3c71eb-5014-4b36-85a0-aeb7ae48a5e9",
"action": "save",
"entity": "blog",
"data": {
"blog_id": 1,
"blog_text": "modify blog from QxOrm REST API",
"date_creation": "2013-11-25T09:56:33",
"author_id": "author_id_1"
}
}
{
"data": {
"blog_id": 1
},
"request_id": "ec3c71eb-5014-4b36-85a0-aeb7ae48a5e9"
}
-- Exemple n�2 -- sauvegarde (ins�re ou met � jour suivant l'identifant unique) une liste de blogs dans la base de donn�es : Requ�te JSON : {
"request_id": "dc7c804e-f95a-4a9b-a4e3-547adcacf090",
"action": "save",
"entity": "blog",
"data": [
{
"blog_id": 1,
"blog_text": "save blog from QxOrm REST API !",
"date_creation": "2018-01-30T12:42:01",
"author_id": "author_id_2"
},
{
"blog_text": "save another blog from QxOrm REST API !",
"date_creation": "2016-06-12T08:33:12",
"author_id": "author_id_1"
}
]
}
{
"data": [
{
"blog_id": 1
},
{
"blog_id": 5
}
],
"request_id": "dc7c804e-f95a-4a9b-a4e3-547adcacf090"
}
-- Exemple n�3 -- sauvegarde (ins�re ou met � jour suivant l'identifant unique) un blog et toutes ses relations sur plusieurs niveaux (de fa�on r�cursive) : Requ�te JSON : {
"request_id": "5b78e468-2fa3-4aeb-82ce-4d85408f5fa7",
"action": "save",
"entity": "blog",
"data": {
"blog_id": 1,
"blog_text": "save recursive blog from QxOrm REST API",
"date_creation": "2013-11-25T09:56:33",
"author_id": {
"author_id": "author_id_1",
"birthdate": "1965-07-21",
"name": "save recursive author from QxOrm REST API",
"sex": 0
}
},
"save_mode": "check_insert_or_update"
}
{
"data": {
"blog_id": 1
},
"request_id": "5b78e468-2fa3-4aeb-82ce-4d85408f5fa7"
}
-- Exemple n�4 -- ins�re (save_mode = insert_only) un blog et toutes ses relations sur plusieurs niveaux (de fa�on r�cursive) : Requ�te JSON : {
"request_id": "ef147c62-74e0-4be2-a294-ffeb020d5304",
"action": "save",
"entity": "blog",
"data": {
"blog_text": "save recursive - new blog from QxOrm REST API",
"date_creation": "2013-11-25T09:56:33",
"author_id": {
"author_id": "author_id_save_recursive",
"birthdate": "1965-07-21",
"name": "save recursive (insert only) author from QxOrm REST API",
"sex": 0
}
},
"save_mode": "insert_only"
}
{
"data": {
"blog_id": 7
},
"request_id": "ef147c62-74e0-4be2-a294-ffeb020d5304"
}
Suppression de donn�es (delete)
Ce chapitre d�taille les diff�rentes m�thodes pour supprimer des �l�ments de la base de donn�es
:
Remarque : la diff�rence entre delete et destroy est li�e � la suppression logique (soft delete) d'un �l�ment.
Les actions delete_all et destroy_all permettent de supprimer
tous les �l�ments d'une table.
La diff�rence entre delete et destroy est li�e � la suppression logique (soft delete) d'un �l�ment.
-- Exemple n�1 -- supprime tous les �l�ments de la table comment : Requ�te JSON : {
"request_id": "7b06b5c0-409f-4e0d-bfc4-acafbfe7e796",
"action": "delete_all",
"entity": "comment"
}
{
"data": {
"deleted": true
},
"request_id": "7b06b5c0-409f-4e0d-bfc4-acafbfe7e796"
}
delete_by_query / destroy_by_query
Les actions delete_by_query et destroy_by_query permettent de
supprimer les �l�ments d'une table en fonction d'une requ�te.
La diff�rence entre delete et destroy est li�e � la suppression logique (soft delete) d'un �l�ment.
-- Exemple n�1 -- supprime les �l�ments de la table author qui ont un sexe de type female (female = enum de valeur 1) : Requ�te JSON : {
"request_id": "169ff0be-6e49-457b-a99c-22bd7141dc02",
"action": "delete_by_query",
"entity": "author",
"query": {
"sql": "WHERE author.sex = :sex",
"params": [
{
"key": ":sex",
"value": 1
}
]
}
}
{
"data": {
"deleted": true
},
"request_id": "169ff0be-6e49-457b-a99c-22bd7141dc02"
}
Les actions delete_by_id et destroy_by_id permettent de supprimer
les �l�ments d'une table en fonction de leur identifiant unique.
La diff�rence entre delete et destroy est li�e � la suppression logique (soft delete) d'un �l�ment.
-- Exemple n�1 -- supprime de la base de donn�es le blog qui a pour identifiant unique la valeur 4 : Requ�te JSON : {
"request_id": "80bff383-8ebd-4bde-bb42-37b6f67bc39f",
"action": "delete_by_id",
"entity": "blog",
"data": {
"blog_id": 4
}
}
{
"data": {
"blog_id": 4
},
"request_id": "80bff383-8ebd-4bde-bb42-37b6f67bc39f"
}
-- Exemple n�2 -- supprime de la base de donn�es les blogs qui ont pour identifiant unique les valeurs 2 et 3 : Requ�te JSON : {
"request_id": "38020cb7-d725-4c0e-80a0-63db7569155e",
"action": "delete_by_id",
"entity": "blog",
"data": [
{
"blog_id": 3
},
{
"blog_id": 2
}
]
}
{
"data": [
{
"blog_id": 3
},
{
"blog_id": 2
}
],
"request_id": "38020cb7-d725-4c0e-80a0-63db7569155e"
}
Validation de donn�es (validate)
L'action validate permet de valider les propri�t�s d'une instance (sans d�clencher
d'action sur la base de donn�es).
L'action validate appelle le module QxValidator de la
biblioth�que QxOrm.
-- Exemple n�1 -- un blog doit contenir du texte (propri�t� blog_text) pour pouvoir �tre sauvegard� dans la base de donn�es. La requ�te JSON suivante permet d'indiquer que l'instance est non valide avec un message explicite : Requ�te JSON : {
"request_id": "92043c2b-4ba8-4583-8fad-c828251734ba",
"action": "validate",
"entity": "blog",
"data": {
"blog_id": 9999,
"blog_text": ""
}
}
{
"data": {
"invalid_values": [
"blog",
[
{
"message": "'blog_text' property cannot be empty",
"path": "blog"
}
]
]
},
"request_id": "92043c2b-4ba8-4583-8fad-c828251734ba"
}
-- Exemple n�2 -- en ajoutant une valeur � la propri�t� blog_text, alors le blog devient valide (la r�ponse JSON dispose d'un champ invalid_values qui vaut null) : Requ�te JSON : {
"request_id": "92043c2b-4ba8-4583-8fad-c828251734ba",
"action": "validate",
"entity": "blog",
"data": {
"blog_id": 9999,
"blog_text": "my blog text !!!"
}
}
{
"data": {
"invalid_values": null
},
"request_id": "92043c2b-4ba8-4583-8fad-c828251734ba"
}
Appel RAW SQL ou proc�dure stock�e
L'action call_custom_query permet d'appeler une requ�te SQL personnalis�e ou une
proc�dure stock�e.
-- Exemple n�1 -- ins�re dans la base de donn�es un nouveau author avec une requ�te SQL personnalis�e : Requ�te JSON : {
"request_id": "ff2a2256-041d-4c5f-bd86-3745ce46ead8",
"action": "call_custom_query",
"query": {
"sql": "INSERT INTO author (author_id, name, birthdate, sex) VALUES (:author_id, :name, :birthdate, :sex)",
"params": [
{
"key": ":author_id",
"value": "author_id_custom_query"
},
{
"key": ":name",
"value": "new author inserted by custom query"
},
{
"key": ":birthdate",
"value": "20190215"
},
{
"key": ":sex",
"value": 2
}
]
}
}
{
"data": {
"query_output": {
"distinct": false,
"list_values": {
":author_id": [
"author_id_custom_query",
1
],
":birthdate": [
"20190215",
1
],
":name": [
"new author inserted by custom query",
1
],
":sex": [
2,
1
]
},
"parenthesis_count": 0,
"query": [
"INSERT INTO author (author_id, name, birthdate, sex) VALUES (:author_id, :name, :birthdate, :sex)"
],
"response": "",
"result_position_by_key": {},
"result_values": [],
"sql_element_index": 0,
"sql_element_list": [],
"sql_element_temp_type": 0,
"type": ""
}
},
"request_id": "ff2a2256-041d-4c5f-bd86-3745ce46ead8"
}
L'action call_entity_function permet d'appeler des fonctions natives C++
enregistr�es dans le contexte QxOrm.
Pr�requis : la fonction native C++ doit �tre une fonction static avec pour signature : static QJsonValue myNativeCppFct(const QJsonValue & request); Voici un exemple d'enregistrement de fonction native C++ pouvant �tre appel�e par les API JSON de la biblioth�que QxOrm :
Voici comment appeler cette fonction helloWorld avec les API JSON en utilisant l'action call_entity_function : Requ�te JSON : {
"request_id": "ab1ba7d3-9f98-4b18-a310-a9c34498d043",
"action": "call_entity_function",
"entity": "blog",
"fct": "helloWorld",
"data": {
"param1": "test",
"param2": "static fct call"
}
}
{
"data": {
"request": {
"param1": "test",
"param2": "static fct call"
},
"response": "Hello World !"
},
"request_id": "ab1ba7d3-9f98-4b18-a310-a9c34498d043"
}
Meta-data (structure des classes C++ enregistr�es dans le contexte QxOrm)
L'action get_meta_data permet de r�cup�rer les m�ta-donn�es d'une ou de toutes les
entit�s enregistr�es dans le contexte QxOrm (structure des classes avec liste des propri�t�s et
relations).
-- Exemple n�1 -- r�cup�re toutes les m�ta-donn�es du projet d'exemple qxBlogRestApi : Requ�te JSON : {
"request_id": "842ed7b5-9b94-455f-86dc-32992866b3d5",
"action": "get_meta_data",
"entity": "*"
}
{
"data": {
"entities": [
{
"base_entity": "",
"description": "",
"entity_id": {
"description": "",
"key": "author_id",
"type": "QString"
},
"key": "author",
"name": "author",
"properties": [
{
"description": "",
"key": "name",
"type": "QString"
},
{
"description": "",
"key": "birthdate",
"type": "QDate"
},
{
"description": "",
"key": "sex",
"type": "enum author::enum_sex *"
}
],
"relations": [
{
"description": "",
"key": "list_blog",
"target": "blog",
"type": "std::vector<std::shared_ptr<blog>>",
"type_relation": "relation one-to-many"
}
],
"version": 0
},
{
"base_entity": "",
"description": "",
"entity_id": {
"description": "",
"key": "blog_id",
"type": "long"
},
"key": "blog",
"name": "blog",
"properties": [
{
"description": "",
"key": "blog_text",
"type": "QString"
},
{
"description": "",
"key": "date_creation",
"type": "QDateTime"
}
],
"relations": [
{
"description": "",
"key": "author_id",
"target": "author",
"type": "std::shared_ptr<author>",
"type_relation": "relation many-to-one"
},
{
"description": "",
"key": "list_comment",
"target": "comment",
"type": "QList<std::shared_ptr<comment>>",
"type_relation": "relation one-to-many"
},
{
"description": "",
"key": "list_category",
"target": "category",
"type": "qx::QxCollection<long, QSharedPointer<category>>",
"type_relation": "relation many-to-many"
}
],
"version": 0
},
{
"base_entity": "",
"description": "",
"entity_id": {
"description": "",
"key": "comment_id",
"type": "long"
},
"key": "comment",
"name": "comment",
"properties": [
{
"description": "",
"key": "comment_text",
"type": "QString"
},
{
"description": "",
"key": "date_creation",
"type": "QDateTime"
}
],
"relations": [
{
"description": "",
"key": "blog_id",
"target": "blog",
"type": "std::shared_ptr<blog>",
"type_relation": "relation many-to-one"
}
],
"version": 0
},
{
"base_entity": "",
"description": "",
"entity_id": {
"description": "",
"key": "category_id",
"type": "long"
},
"key": "category",
"name": "category",
"properties": [
{
"description": "",
"key": "name",
"type": "QString"
},
{
"description": "",
"key": "description",
"type": "QString"
}
],
"relations": [
{
"description": "",
"key": "list_blog",
"target": "blog",
"type": "qx::QxCollection<long, std::shared_ptr<blog>>",
"type_relation": "relation many-to-many"
}
],
"version": 0
}
]
},
"request_id": "842ed7b5-9b94-455f-86dc-32992866b3d5"
}
Envoyer une liste de requ�tes JSON
Afin de limiter le nombre de transactions entre le client et le serveur, il est possible
d'envoyer une liste de requ�tes JSON au module QxRestApi.
Chaque requ�te JSON de la liste peut disposer de son propre identifiant request_id (afin
d'associer une r�ponse JSON � la requ�te correspondante).
Lorsqu'une liste de requ�tes JSON est envoy�e au module QxRestApi, alors une transaction (commit/rollback) est automatiquement cr��e (ainsi en
cas d'erreur, tous les traitements sur la base de donn�es sont annul�s).
-- Exemple n�1 -- envoi 4 requ�tes JSON au module QxRestApi (1 requ�te pour r�cup�rer les m�ta-donn�es du projet + 3 requ�tes fetch_all avec diff�rent niveau de r�cup�ration des relations) : Requ�te JSON : [
{
"request_id": "53c96a23-2566-4b3d-ae6c-bff634600e79",
"action": "get_meta_data",
"entity": "*"
},
{
"request_id": "56e3ca99-5c12-4aca-aa6c-7d0e43c1e636",
"action": "fetch_all",
"entity": "blog"
},
{
"request_id": "692968e4-8885-41ad-b918-6ce2791b3bb8",
"action": "fetch_all",
"entity": "blog",
"data": [
{
"key": "",
"value": ""
}
]
},
{
"request_id": "4ffe38a6-d642-44b0-8be1-198e84256321",
"action": "fetch_all",
"entity": "blog",
"relations": [
"*->*"
]
}
]
[
{
"data": {
"entities": [
{
"base_entity": "",
"description": "",
"entity_id": {
"description": "",
"key": "author_id",
"type": "QString"
},
"key": "author",
"name": "author",
"properties": [
{
"description": "",
"key": "name",
"type": "QString"
},
{
"description": "",
"key": "birthdate",
"type": "QDate"
},
{
"description": "",
"key": "sex",
"type": "enum author::enum_sex *"
}
],
"relations": [
{
"description": "",
"key": "list_blog",
"target": "blog",
"type": "std::vector<std::shared_ptr<blog>>",
"type_relation": "relation one-to-many"
}
],
"version": 0
},
{
"base_entity": "",
"description": "",
"entity_id": {
"description": "",
"key": "blog_id",
"type": "long"
},
"key": "blog",
"name": "blog",
"properties": [
{
"description": "",
"key": "blog_text",
"type": "QString"
},
{
"description": "",
"key": "date_creation",
"type": "QDateTime"
}
],
"relations": [
{
"description": "",
"key": "author_id",
"target": "author",
"type": "std::shared_ptr<author>",
"type_relation": "relation many-to-one"
},
{
"description": "",
"key": "list_comment",
"target": "comment",
"type": "QList<std::shared_ptr<comment>>",
"type_relation": "relation one-to-many"
},
{
"description": "",
"key": "list_category",
"target": "category",
"type": "qx::QxCollection<long, QSharedPointer<category>>",
"type_relation": "relation many-to-many"
}
],
"version": 0
},
{
"base_entity": "",
"description": "",
"entity_id": {
"description": "",
"key": "comment_id",
"type": "long"
},
"key": "comment",
"name": "comment",
"properties": [
{
"description": "",
"key": "comment_text",
"type": "QString"
},
{
"description": "",
"key": "date_creation",
"type": "QDateTime"
}
],
"relations": [
{
"description": "",
"key": "blog_id",
"target": "blog",
"type": "std::shared_ptr<blog>",
"type_relation": "relation many-to-one"
}
],
"version": 0
},
{
"base_entity": "",
"description": "",
"entity_id": {
"description": "",
"key": "category_id",
"type": "long"
},
"key": "category",
"name": "category",
"properties": [
{
"description": "",
"key": "name",
"type": "QString"
},
{
"description": "",
"key": "description",
"type": "QString"
}
],
"relations": [
{
"description": "",
"key": "list_blog",
"target": "blog",
"type": "qx::QxCollection<long, std::shared_ptr<blog>>",
"type_relation": "relation many-to-many"
}
],
"version": 0
}
]
},
"request_id": "53c96a23-2566-4b3d-ae6c-bff634600e79"
},
{
"data": [
{
"author_id": {
"author_id": "author_id_1",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "save recursive blog from QxOrm REST API",
"date_creation": "2013-11-25T09:56:33",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_1",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 5,
"blog_text": "save another blog from QxOrm REST API !",
"date_creation": "2016-06-12T08:33:12",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_save_recursive",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 6,
"blog_text": "save recursive - new blog from QxOrm REST API",
"date_creation": "2013-11-25T09:56:33",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_save_recursive",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 7,
"blog_text": "save recursive - new blog from QxOrm REST API",
"date_creation": "2013-11-25T09:56:33",
"list_category": [],
"list_comment": []
}
],
"request_id": "56e3ca99-5c12-4aca-aa6c-7d0e43c1e636"
},
{
"data": [
{
"key": 1,
"value": {
"author_id": {
"author_id": "author_id_1",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "save recursive blog from QxOrm REST API",
"date_creation": "2013-11-25T09:56:33",
"list_category": [],
"list_comment": []
}
},
{
"key": 5,
"value": {
"author_id": {
"author_id": "author_id_1",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 5,
"blog_text": "save another blog from QxOrm REST API !",
"date_creation": "2016-06-12T08:33:12",
"list_category": [],
"list_comment": []
}
},
{
"key": 6,
"value": {
"author_id": {
"author_id": "author_id_save_recursive",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 6,
"blog_text": "save recursive - new blog from QxOrm REST API",
"date_creation": "2013-11-25T09:56:33",
"list_category": [],
"list_comment": []
}
},
{
"key": 7,
"value": {
"author_id": {
"author_id": "author_id_save_recursive",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 7,
"blog_text": "save recursive - new blog from QxOrm REST API",
"date_creation": "2013-11-25T09:56:33",
"list_category": [],
"list_comment": []
}
}
],
"request_id": "692968e4-8885-41ad-b918-6ce2791b3bb8"
},
{
"data": [
{
"author_id": {
"author_id": "author_id_1",
"birthdate": "2019-04-02",
"list_blog": [
{
"author_id": {
"author_id": "author_id_1",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 5,
"blog_text": "save another blog from QxOrm REST API !",
"date_creation": "2016-06-12T08:33:12",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_1",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "save recursive blog from QxOrm REST API",
"date_creation": "2013-11-25T09:56:33",
"list_category": [],
"list_comment": []
}
],
"name": "author_1",
"sex": 0
},
"blog_id": 1,
"blog_text": "save recursive blog from QxOrm REST API",
"date_creation": "2013-11-25T09:56:33",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_1",
"birthdate": "2019-04-02",
"list_blog": [
{
"author_id": {
"author_id": "author_id_1",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 5,
"blog_text": "save another blog from QxOrm REST API !",
"date_creation": "2016-06-12T08:33:12",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_1",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 1,
"blog_text": "save recursive blog from QxOrm REST API",
"date_creation": "2013-11-25T09:56:33",
"list_category": [],
"list_comment": []
}
],
"name": "author_1",
"sex": 0
},
"blog_id": 5,
"blog_text": "save another blog from QxOrm REST API !",
"date_creation": "2016-06-12T08:33:12",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_save_recursive",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 6,
"blog_text": "save recursive - new blog from QxOrm REST API",
"date_creation": "2013-11-25T09:56:33",
"list_category": [],
"list_comment": []
},
{
"author_id": {
"author_id": "author_id_save_recursive",
"birthdate": null,
"list_blog": [],
"name": "",
"sex": 2
},
"blog_id": 7,
"blog_text": "save recursive - new blog from QxOrm REST API",
"date_creation": "2013-11-25T09:56:33",
"list_category": [],
"list_comment": []
}
],
"request_id": "4ffe38a6-d642-44b0-8be1-198e84256321"
}
]
|
|
|
� 2011-202XDL Teamty - ic-east.com |