Files
XdlOrm/doc/qxorm_fr/manual.html
2026-04-03 11:32:07 +08:00

13103 lines
916 KiB
HTML
Raw Blame History

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
<title>QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic
editor (data model designer and source code generator)</title>
<link rel='stylesheet' type='text/css' href='./resource/qxorm_style.css'>
<script type="text/javascript" src="./resource/jquery.min.js"></script>
<script type="text/javascript" src="./resource/qxorm_script.js"></script>
</head>
<body>
<table border="0" style="width: 80%" align="center">
<col>
<col>
<col>
<col>
<col>
<tbody>
<tr>
<td><a href="./home.html"><img alt="QxOrm" src="./resource/logo_qxorm_and_qxee.png" align="left"
border="0"></a></td>
<td align="right" style="vertical-align:bottom">
<div id="qx_search">
<gcse:search></gcse:search>
</div>
</td>
<td width="15"></td>
<td align="right" style="vertical-align:bottom">
<img alt="Windows" src="./resource/logo_windows.gif" width="35" height="35">
<img alt="Linux" src="./resource/logo_linux.gif" width="35" height="35">
<img alt="Macintosh" src="./resource/logo_mac.gif" width="35" height="35">
</td>
<td width="70"><img alt="C++" src="./resource/logo_cpp.gif" width="50" height="50" align="right"></td>
</tr>
</tbody>
</table>
<hr style="width: 80%" align="center" size="1" color="#100D5A">
<table border="0" style="width: 80%" align="center">
<col>
<col>
<col>
<col>
<col>
<col>
<tbody>
<tr>
<td align="center"><a href="./home.html" class="btn_nav">Accueil</a></td>
<td align="center"><a href="./download.html" class="btn_nav">T<EFBFBD>l<EFBFBD>chargement</a></td>
<td align="center"><a href="./quick_sample.html" class="btn_nav">Exemple rapide</a></td>
<td align="center" onmouseover="showHideElementById('menu_tuto', true);"
onmouseout="showHideElementById('menu_tuto', false);">
<a href="./tutorial.html" class="btn_nav">Tutoriel (4)</a>
<table class="table_menu_tuto">
<tbody>
<tr>
<td>
<div id="menu_tuto" class="div_menu_tuto">
<a href="./tutorial_3.html" class="btn_sub_menu">install QxOrm</a><br />
<a href="./tutorial.html" class="btn_sub_menu">qxBlog</a><br />
<a href="./tutorial_2.html" class="btn_sub_menu">qxClientServer</a><br />
<a href="./tutorial_4.html" class="btn_sub_menu">QxEntityEditor videos</a>
</div>
</td>
</tr>
</tbody>
</table>
</td>
<td align="center" onmouseover="showHideElementById('menu_manual', true);"
onmouseout="showHideElementById('menu_manual', false);">
<a href="./manual.html" class="btn_nav">Manuel (2)</a>
<table class="table_menu_manual">
<tbody>
<tr>
<td>
<div id="menu_manual" class="div_menu_manual">
<a href="./manual.html" class="btn_sub_menu">Manuel QxOrm</a><br />
<a href="./manual_qxee.html" class="btn_sub_menu">Manuel QxEntityEditor</a><br />
</div>
</td>
</tr>
</tbody>
</table>
</td>
<td align="center"><a href="./link.html" class="btn_nav">Forum</a></td>
<td align="center"><a href="./customer.html" class="btn_nav">Nos clients</a></td>
</tr>
</tbody>
</table>
<hr style="width: 80%" align="center" size="1" color="#100D5A">
<table border="0" style="width: 80%" align="center">
<col>
<col>
<col>
<col>
<col>
<col>
<tbody>
<tr>
<td align="left" valign="top">
<font size="2" class="txt_with_shadow">QxOrm &gt;&gt; Manuel d'utilisation de la biblioth<74>que QxOrm
</font>
</td>
<td align="right" valign="top">
<table cellspacing="0" cellpadding="1">
<col>
<col>
<tbody>
<tr>
<td align="right" valign="top">
<font size="2" class="txt_with_shadow">Version courante :&nbsp;</font>
</td>
<td align="left" valign="top">
<font size="2" class="txt_with_shadow">QxOrm 1.5.0 - <a href="../doxygen/index.html"
target="_blank">documentation en ligne de la biblioth<74>que QxOrm</a> - <a
href="https://github.com/QxOrm/QxOrm" target="_blank">GitHub</a></font>
</td>
</tr>
<tr>
<td align="right" valign="top">
<font size="2" class="txt_with_shadow"></font>
</td>
<td align="left" valign="top">
<font size="2" class="txt_with_shadow">QxEntityEditor 1.2.8</font>
</td>
</tr>
</tbody>
</table>
</td>
<td width="10px"></td>
<td width="40px" height="30px"><a href="../qxorm_fr/manual.html"><img alt="Version fran<61>aise du site"
src="./resource/FR.png" width="40" height="30" border="0"></a></td>
<td width="40px" height="30px"><a href="../qxorm_en/manual.html"><img alt="Web site english version"
src="./resource/GB.png" width="40" height="30" border="0"></a></td>
<td width="40px" height="30px"><a href="http://sites.google.com/site/qxormpostgres/" target="_blank"><img
alt="" src="./resource/ES.png" width="40" height="30" border="0"></a></td>
</tr>
</tbody>
</table>
<table border="1" frame="vsides" rules="cols" style="width: 80%" align="center" cellpadding="6" bgcolor="#F2F2F4">
<col>
<tbody>
<tr>
<td align="justify">
<script>$(function () { initQxOrmManualWebPage(); });</script>
<table border="0" cellpadding="4">
<col>
<col>
<tbody>
<tr>
<td>
<font class="txt_with_shadow" color="#0B0B61" size="4"><i>S<EFBFBD>lection du manuel : </i></font>
</td>
<td align="left">
<a href="./manual.html" class="btn_tuto_selected">Manuel QxOrm</a>
<a href="./manual_qxee.html" class="btn_tuto">Manuel QxEntityEditor</a>
</td>
</tr>
</tbody>
</table>
<hr style="width: 100%" align="center" size="1" color="#100D5A">
<br>
<table border="0" style="width: 100%" align="center">
<col>
<col>
<tbody>
<tr>
<td>
<h3 class="txt_slogan"><b>Manuel d'utilisation de la biblioth<74>que QxOrm - Table des
mati<74>res</b></h3>
<div id="manual_table_of_contents">
<ol class="manual_manual_ol_title_1" type="I">
<li class="manual_li_title_1">
<a href="#manual_10">Introduction</a>
<ol class="manual_ol_title_2" type="1">
<li class="manual_li_title_2">
<a href="#manual_100">Biblioth<EFBFBD>que QxOrm</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_110">Aper<EFBFBD>u rapide de l'application QxEntityEditor</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_120">Convention d'<27>criture C++ utilis<69>e par la biblioth<74>que
QxOrm</a>
</li>
</ol>
</li>
<li class="manual_li_title_1">
<a href="#manual_20">Installation</a>
<ol class="manual_ol_title_2" type="1">
<li class="manual_li_title_2">
<a href="#manual_200">D<EFBFBD>pendance <20> Qt</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_210">D<EFBFBD>pendance <20> boost (optionnel)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_220">Fichier de configuration QxOrm.pri (ou QxOrm.cmake)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_230">Compiler la biblioth<74>que QxOrm (avec qmake ou CMake)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_240">Pilotes SQL fournis par Qt (drivers)</a>
</li>
</ol>
</li>
<li class="manual_li_title_1">
<a href="#manual_30">Persistance - Object Relational Mapping (ORM)</a>
<ol class="manual_ol_title_2" type="1">
<li class="manual_li_title_2">
<a href="#manual_300">D<EFBFBD>finir une classe dans le contexte QxOrm (mapping)</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_3000">Cl<EFBFBD> primaire autre que le type par d<>faut
"long"</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_3010">Cl<EFBFBD> primaire sur plusieurs colonnes (composite
key)</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_3020">Donn<EFBFBD>es membres public/protected/private</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_3030">Espace de nom (namespace)</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_3040">Types C++ support<72>s par QxOrm</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_3050">D<EFBFBD>finir une donn<6E>e membre <i>transient</i></a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_310">Connexion <20> la base de donn<6E>es</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_320">Sauvegarder une instance C++ en base de donn<6E>es
(insert/update)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_340">Supprimer une instance C++ de la base de donn<6E>es
(delete)</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_3400">Suppression logique (soft delete)</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_350">R<EFBFBD>cup<EFBFBD>rer une instance C++ de la base de donn<6E>es
(fetch)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_360">Requ<EFBFBD>tes SQL</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_3600">Utilisation de la classe qx::QxSqlQuery (ou son
alias qx_query)</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_3610">Appel de proc<6F>dure stock<63>e ou requ<71>te SQL
personnalis<69>e</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_370">Transactions (commit, rollback, session)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_380">Moteur de relations</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_3800">one-to-many (1-n)</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_3810">many-to-one (n-1)</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_3820">many-to-many (n-n)</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_3830">one-to-one (1-1)</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_3840">Requ<EFBFBD>te SQL avec relations</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_3850">S<EFBFBD>lectionner les colonnes des relations <20>
r<>cup<75>rer et d<>finition des alias SQL</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_3855">Ajout SQL dans les clauses LEFT OUTER JOIN /
INNER JOIN</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_390">Collections support<72>es par QxOrm</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_3900">Collections de Qt</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_3910">Collections de boost</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_3920">Collections fournies par l'espace de nom standard
std</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_3930">qx::QxCollection</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_400">Pointeurs intelligents support<72>s par QxOrm
(smart-pointers)</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_4000">Pointeurs intelligents de Qt</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_4010">Pointeurs intelligents de boost</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_4020">Pointeurs intelligents fournis par l'espace de
nom standard std</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_4030">qx::dao::ptr</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_410">D<EFBFBD>clencheurs (triggers)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_420">Validation d'une instance C++ (validators)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_430">G<EFBFBD>rer la valeur NULL de la base de donn<6E>es</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_4300">boost::optional</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_4310">QVariant</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_440">H<EFBFBD>ritage et polymorphisme</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_450">Interface qx::IxPersistable (classe abstraite)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_455">Utiliser le pattern C++ PIMPL (Private Implementation
idiom ou d-pointer)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_460">Persister des types personnalis<69>s</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_470">G<EFBFBD>n<EFBFBD>rer le sch<63>ma DDL SQL de la base de donn<6E>es</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_475">Associer un type SQL <20> une classe C++</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_480">Effectuer des requ<71>tes asynchrones <20> la base de
donn<6E>es</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_490">Gestion du cache pour sauvegarder des instances C++
(module QxCache)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_500">Travailler avec plusieurs bases de donn<6E>es</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_510">D<EFBFBD>clarer une classe abstraite dans le contexte QxOrm</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_520">D<EFBFBD>clarer automatiquement les m<>ta-propri<72>t<EFBFBD>s de Qt
(macro <i>Q_PROPERTY</i>)</a>
</li>
</ol>
</li>
<li class="manual_li_title_1">
<a href="#manual_60">S<EFBFBD>rialisation</a>
<ol class="manual_ol_title_2" type="1">
<li class="manual_li_title_2">
<a href="#manual_605">N<EFBFBD> version pour assurer une compatibilit<69> ascendante</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_600">Moteur QDataStream de Qt</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_606">Moteur JSON de Qt</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_610">Moteur XML de boost::serialization</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_620">Moteur binaire de boost::serialization</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_630">Autres types de s<>rialisation propos<6F>s par boost</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_640">Cloner une instance C++</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_650">Afficher le d<>tail d'une instance C++ (dump au format
XML ou JSON)</a>
</li>
</ol>
</li>
<li class="manual_li_title_1">
<a href="#manual_70">Introspection - R<>flexion</a>
<ol class="manual_ol_title_2" type="1">
<li class="manual_li_title_2">
<a href="#manual_710">Obtenir dynamiquement la valeur d'une donn<6E>e membre</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_720">Valoriser dynamiquement une donn<6E>e membre</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_730">Appeler dynamiquement une fonction</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_740">Cr<EFBFBD>er une instance C++ dynamiquement</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_750">Parcourir la liste des classes/propri<72>t<EFBFBD>s enregistr<74>es
dans le contexte QxOrm</a>
</li>
</ol>
</li>
<li class="manual_li_title_1">
<a href="#manual_80">Services : transf<73>rer la couche de donn<6E>es persistante sur le
r<>seau (module QxService)</a>
<ol class="manual_ol_title_2" type="1">
<li class="manual_li_title_2">
<a href="#manual_810">Param<EFBFBD>tres d'entr<74>e/sortie d'un service
(requ<71>te/r<>ponse)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_820">D<EFBFBD>finir les fonctions publi<6C>es par un service</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_825">Liste des options disponibles c<>t<EFBFBD> serveur</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_826">Param<EFBFBD>trage de la connexion c<>t<EFBFBD> client</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_830">Gestion de l'authentification dans un service</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_840">Requ<EFBFBD>tes client/serveur asynchrones</a>
</li>
</ol>
</li>
<li class="manual_li_title_1">
<a href="#manual_90">Moteur mod<6F>le/vue (module QxModelView)</a>
<ol class="manual_ol_title_2" type="1">
<li class="manual_li_title_2">
<a href="#manual_910">D<EFBFBD>finir un mod<6F>le "simple" (sans relation)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_920">Mod<EFBFBD>les avec relations (notion de mod<6F>les imbriqu<71>s)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_940">Int<EFBFBD>raction avec les vues QML</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_950">Int<EFBFBD>raction avec les vues QtWidget</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_960">Connexion d'un mod<6F>le au module QxService</a>
</li>
</ol>
</li>
<li class="manual_li_title_1">
<a href="#manual_95">QxOrm et MongoDB (C++ ODM Object Document Mapper)</a>
<ol class="manual_ol_title_2" type="1">
<li class="manual_li_title_2">
<a href="#manual_980">Pr<EFBFBD>-requis : driver <i>libmongoc</i> et
<i>libbson</i></a>
</li>
<li class="manual_li_title_2">
<a href="#manual_981">Param<EFBFBD>trage du fichier <i>QxOrm.pri</i> (ou
<i>QxOrm.cmake</i>)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_982">Connexion <20> la base de donn<6E>es MongoDB</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_983">D<EFBFBD>finition d'une classe persistante MongoDB (Collection)
dans le contexte QxOrm (mapping)</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_9830">Gestion des cl<63>s primaires ObjectId</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_984">Ins<EFBFBD>rer une instance C++ (Document) dans la base de
donn<6E>es MongoDB (INSERT)</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_9840">Ins<EFBFBD>rer une liste d'instances C++ (plusieurs
Documents) dans la base de donn<6E>es MongoDB (INSERT)</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_986">Mettre <20> jour une instance C++ (Document) dans la base
de donn<6E>es MongoDB (UPDATE)</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_9860">Mettre <20> jour une liste d'instances C++
(plusieurs Documents) dans la base de donn<6E>es MongoDB (UPDATE)</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_988">Supprimer une instance C++ (Document) de la base de
donn<6E>es MongoDB (DELETE)</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_9880">Supprimer une liste d'instances C++ (plusieurs
Documents) de la base de donn<6E>es MongoDB (DELETE)</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_990">R<EFBFBD>cup<EFBFBD>rer une instance C++ (Document) de la base de
donn<6E>es MongoDB (FETCH)</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_9900">R<EFBFBD>cup<EFBFBD>rer une liste d'instances C++ (plusieurs
Documents) de la base de donn<6E>es MongoDB (FETCH)</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_992">Requ<EFBFBD>tes JSON</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_9920">Utilisation de la classe qx::QxSqlQuery (ou son
alias qx_query)</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_9921">Utiliser le moteur d'aggregation MongoDB</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_9922">Ajouter des propri<72>t<EFBFBD>s <20> la requ<71>te de type :
'sort', 'limit', 'skip', etc...</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_9923">Ex<EFBFBD>cuter une requ<71>te personnalis<69>e</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_993">Moteur de relations (n<>cessite une version MongoDB 3.6
ou +)</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_9930">Relations : Embedded vs Referenced</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_994">Cr<EFBFBD>ation automatique des index</a>
</li>
</ol>
</li>
<li class="manual_li_title_1">
<a href="#manual_96">Serveur web HTTP/HTTPS (module QxHttpServer)</a>
<ol class="manual_ol_title_2" type="1">
<li class="manual_li_title_2">
<a href="#manual_995">Hello World !</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_996">Param<EFBFBD>trage du serveur web HTTP</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_9961">Connexions s<>curis<69>es SSL/TLS</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_997">Routage des URL (d<>finir les endpoints / dispatcher)</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_9962">Routage dynamique des URL</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_856">R<EFBFBD>cup<EFBFBD>rer les param<61>tres de la requ<71>te HTTP</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_857">G<EFBFBD>n<EFBFBD>rer la r<>ponse HTTP</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_998">Sessions (stockage par client c<>t<EFBFBD> serveur)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_999">Cookies</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_851">Gestion des fichiers statiques</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_852">Encodage de transfert en bloc (chunked responses)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_853">Requ<EFBFBD>tes par les API JSON (module QxRestApi)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_854">WebSocket</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_855">Performance (test<73> avec Apache Benchmark)</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_8550">Am<EFBFBD>liorer les performances avec epoll dispatcher
sous Linux</a>
</li>
</ol>
</li>
</ol>
</li>
<li class="manual_li_title_1">
<a href="#manual_97">API REST JSON (module QxRestApi)</a>
<ol class="manual_ol_title_2" type="1">
<li class="manual_li_title_2">
<a href="#manual_971">Principe de fonctionnement</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_9710">Cas d'utilisation</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_972">Projet de test qxBlogRestApi (QML et serveur web
HTTP)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_973">R<EFBFBD>cup<EFBFBD>ration de donn<6E>es (fetch/count/exist)</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_9731">fetch_all</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_9732">fetch_by_id</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_9733">fetch_by_query</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_9734">count</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_9735">exist</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_974">Ajout de donn<6E>es (insert)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_975">Mise <20> jour de donn<6E>es (update)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_976">Sauvegarde de donn<6E>es (save)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_977">Suppression de donn<6E>es (delete)</a>
<ol class="manual_ol_title_3" type="a">
<li class="manual_li_title_3">
<a href="#manual_9771">delete_all / destroy_all</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_9772">delete_by_query / destroy_by_query</a>
</li>
<li class="manual_li_title_3">
<a href="#manual_9773">delete_by_id / destroy_by_id</a>
</li>
</ol>
</li>
<li class="manual_li_title_2">
<a href="#manual_978">Validation de donn<6E>es (validate)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_979">Appel RAW SQL ou proc<6F>dure stock<63>e</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_961">Appel fonctions natives C++</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_962">Meta-data (structure des classes C++ enregistr<74>es dans
le contexte QxOrm)</a>
</li>
<li class="manual_li_title_2">
<a href="#manual_963">Envoyer une liste de requ<71>tes JSON</a>
</li>
</ol>
</li>
</ol>
</div>
</td>
<td width="200" align="center" valign="top"><a href="./resource/qt_ambassador_logo.png"
target="_blank"><img alt="qt_ambassador" src="./resource/qt_ambassador_logo_150x150.png"
width="150" height="150" border="0"></a><br>
<b>
<font size="2">QxOrm library has been accepted into the <a
href="http://forum.qt.io/category/24/qt-ambassador-program" target="_blank">Qt
Ambassador Program</a></font>
</b>
</td>
</tr>
</tbody>
</table>
<br>
<hr width="90%">
<div id="manual_content">
<p class="manual_p_title_1"><a class="manual_a_title_1" name="manual_10">Introduction</a></p>
<div class="manual_div_content_1">
L'objectif de ce manuel utilisateur est de pr<70>senter de mani<6E>re structur<75>e l'ensemble des
fonctionnalit<69>s propos<6F>es par la biblioth<74>que <b>QxOrm</b>.
Ce manuel est destin<69> aux d<>veloppeurs et architectes logiciel qui souhaitent g<>rer une couche de
donn<6E>es persistante en C++/Qt.
Des comp<6D>tences techniques en C++ et base de donn<6E>es sont requises pour la bonne compr<70>hension de
ce document.
<br><br>
<b>Remarque :</b> la plupart des fonctionnalit<69>s pr<70>sent<6E>es dans ce manuel peuvent <20>tre d<>finies
rapidement et facilement avec l'application <b>QxEntityEditor</b> (l'<27>diteur graphique de la
biblioth<74>que <b>QxOrm</b>).
<a href="./manual_qxee.html">Une documentation d<>di<64>e <20> l'application <b>QxEntityEditor</b> est
<20>galement disponible.</a>
<br><br>
<b>Autre remarque :</b> ce manuel est bas<61> en grande partie sur <a href="./faq.html">l'ancienne FAQ
du site QxOrm, toujours accessible en cliquant ici</a>.
<br><br>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_100">Biblioth<EFBFBD>que QxOrm</a>
</p>
<div class="manual_div_content">
<b>QxOrm est une biblioth<74>que C++ open source de gestion de donn<6E>es (Object Relational Mapping,
ORM).</b><br>
<b>QxOrm</b> est d<>velopp<70> XDL Teamarty, Ing<6E>nieur en d<>veloppement logiciel depuis 2003.<br>
<br>
<20> partir d'une simple <i>fonction de param<61>trage</i> (que l'on peut comparer avec un fichier de
mapping XML <a href="http://hibernate.org/" target="_blank"><i>Hibernate</i></a>), vous aurez
acc<63>s aux fonctionnalit<69>s suivantes :
<ul>
<li><b>
<font style="background-color:yellow"><a href="./manual.html#manual_30">Persistance</a>
</font>
</b> : support des bases de donn<6E>es SQLite, MySQL, PostgreSQL, Oracle, MS SQL Server, <a
href="./manual.html#manual_95">MongoDB</a> (gestion des relations <i>1-1</i>,
<i>1-n</i>, <i>n-1</i> et <i>n-n</i>) ;
</li>
<li><b>
<font style="background-color:yellow"><a
href="./manual.html#manual_60">S<EFBFBD>rialisation</a></font>
</b> des donn<6E>es (flux JSON, binaire et XML) ;</li>
<li><b>
<font style="background-color:yellow"><a href="./manual.html#manual_70">R<EFBFBD>flexion</a>
</font>
</b> (ou <b>
<font style="background-color:yellow"><a
href="./manual.html#manual_70">introspection</a></font>
</b>) pour acc<63>der dynamiquement aux classes, attributs et invoquer des m<>thodes ;</li>
<li><b>
<font style="background-color:yellow"><a href="./manual.html#manual_96">Serveur web
HTTP</a></font>
</b> : serveur web compatible HTTP 1.1 autonome, performant, multi-plateforme et simple
d'utilisation ;</li>
<li><b>
<font style="background-color:yellow"><a href="./manual.html#manual_97">API JSON</a>
</font>
</b> : interop<6F>rabilit<69> avec d'autres technologies que C++/Qt (web services REST,
applications QML, langages de script).</li>
</ul>
<b>QxOrm</b> est d<>pendant des excellentes biblioth<74>ques <a href="http://www.qt.io/"
target="_blank"><b>Qt</b></a> (compatible <20> partir de la version 4.5.0) et <a
href="http://www.boost.org/" target="_blank"><b>boost</b></a> (compatible <20> partir de la
version 1.38, par d<>faut seuls les fichiers d'en-t<>te <i>*.hpp</i> sont n<>cessaires).<br>
La biblioth<74>que <b>QxOrm</b> a <20>t<EFBFBD> retenue pour faire partie du programme <a
href="http://forum.qt.io/category/24/qt-ambassador-program" target="_blank"><b>Qt
Ambassador</b></a>.
<br><br>
Si vous trouvez un bug ou si vous avez une question concernant le fonctionnement de la
biblioth<74>que <b>QxOrm</b>,
vous pouvez envoyer un mail <20> : <u><i>support@qxorm.com</i></u>.<br>
Un forum (en anglais) d<>di<64> <20> <b>QxOrm</b> est disponible <a
href="https://www.qxorm.com/forum/phpbb/" target="_blank">en cliquant ici</a>.<br>
Vous pouvez <20>galement retrouver la communaut<75> fran<61>aise de <b>QxOrm</b> sur <a
href="http://www.developpez.net/forums/f1563/c-cpp/bibliotheques/qt/bases-donnees/qxorm/"
target="_blank">le forum de Developpez.com</a>.
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_110">Aper<EFBFBD>u rapide de
l'application QxEntityEditor</a></p>
<div class="manual_div_content">
<b>QxEntityEditor</b> est un <20>diteur graphique pour la biblioth<74>que <b>QxOrm</b> :
<b>QxEntityEditor</b> permet de g<>rer graphiquement le mod<6F>le d'entit<69>s.<br>
<b>QxEntityEditor</b> est multi-plateforme (disponible pour Windows, Linux et Mac OS X) et
g<>n<EFBFBD>re du code natif pour tous les environnements : bureau (Windows, Linux, Mac OS X), embarqu<71>
et mobile (Android, iOS, Windows Phone, Raspberry Pi, etc.).<br>
<a href="./tutorial_4.html">Une vid<69>o de pr<70>sentation de l'application <b>QxEntityEditor</b> est
disponible</a>.<br>
<br>
<b>QxEntityEditor</b> est bas<61> sur un syst<73>me de plugins et propose diverses fonctionnalit<69>s
pour importer/exporter le mod<6F>le de donn<6E>es :
<ul>
<li>g<EFBFBD>n<EFBFBD>ration automatique du code C++ (classes persistantes enregistr<74>es dans le contexte
QxOrm) ;</li>
<li>g<EFBFBD>n<EFBFBD>ration automatique des scripts SQL DDL (sch<63>ma de base de donn<6E>es) pour les bases
SQLite, MySQL, PostgreSQL, Oracle et MS SQL Server ;</li>
<li>supporte l'<27>volution du sch<63>ma de base de donn<6E>es pour chaque version d'un projet
(<i>ALTER TABLE</i>, <i>ADD COLUMN</i>, <i>DROP INDEX</i>, etc.) ;</li>
<li>g<EFBFBD>n<EFBFBD>ration automatique des classes C++ de services pour transf<73>rer le mod<6F>le de donn<6E>es
sur le r<>seau, en utilisant le module <a href="../doxygen/html/group___qx_service.html"
target="_blank">QxService</a>, pour cr<63>er rapidement des applications client/serveur ;
</li>
<li>importation automatique des structures de bases de donn<6E>es existantes (par connexion
ODBC) pour les bases SQLite, MySQL, PostgreSQL, Oracle et MS SQL Server ;</li>
<li>parce que chaque projet est diff<66>rent, QxEntityEditor propose plusieurs outils pour
personnaliser les fichiers g<>n<EFBFBD>r<EFBFBD>s (notamment un moteur javascript et un d<>bogueur
int<6E>gr<67>).</li>
</ul>
<a href="../qxentityeditor/resource/qxee_sample.png" target="_blank"><img alt="QxEntityEditor"
src="../qxentityeditor/resource/qxee_sample_small.png" border="0"
class="img_with_shadow"></a>
<br><br>
<b>QxEntityEditor</b> est d<>velopp<70> XDL Teamarty, Ing<6E>nieur en d<>veloppement logiciel depuis
2003.<br>
<a href="./manual_qxee.html">Un manuel utilisateur d<>di<64> <20> l'application <b>QxEntityEditor</b>
est disponible.</a><br>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_120">Convention d'<27>criture C++
utilis<69>e par la biblioth<74>que QxOrm</a></p>
<div class="manual_div_content">
La biblioth<74>que <b>QxOrm</b> utilise les <b>conventions d'<27>criture de code C++</b> suivantes :
<ul>
<li>toutes les classes, fonctions, variables, etc... sont d<>finies dans <a
href="./resource/qxorm.namespace.qx.jpg"><i>l'espace de nom (namespace) qx</i></a> ;
</li>
<li>les macro de QxOrm sont <20>crites sous la forme <i>QX_...</i> ;
</li>
<li>les classes abstraites (ou interfaces) ont le pr<70>fixe <i>Ix</i> (par exemple :
<i>IxFactory</i> est une interface pour la cr<63>ation d'instances) ;
</li>
<li>les autres classes ont le pr<70>fixe <i>Qx</i> (par exemple : <i>QxDataMember</i>) ;
</li>
<li>les collections d'objets ont pour suffixe <i>X</i> (par exemple : <i>QxDataMemberX</i>
est une collection de <i>QxDataMember</i>) ;
</li>
<li>les fonctions pour communiquer avec les bases de donn<6E>es se trouvent sous le <a
href="./resource/qxorm.namespace.qx.dao.jpg"><i>namespace qx::dao</i></a> (par exemple
: <i>qx::dao::fetch_by_id()</i>) ;
</li>
<li>les fonctions pour la <i>serialization</i> des donn<6E>es se trouvent sous le <a
href="./resource/qxorm.namespace.qx.serialization.jpg"><i>namespace
qx::serialization</i></a> (par exemple : <i>qx::serialization::xml::to_file()</i>) ;
</li>
<li>le moteur de <i>reflection</i> (ou <i>introspection</i>) est accessible depuis la classe
<b><i>qx::QxClassX</i></b> (par exemple <i>qx::QxClassX::invoke()</i> pour invoquer une
m<>thode de classe) ;
</li>
<li>les classes de traits se trouvent sous le <a
href="./resource/qxorm.namespace.qx.trait.jpg"><i>namespace qx::trait</i></a> (par
exemple : <i>qx::trait::is_smart_ptr&lt;T&gt;</i>).
</li>
</ul>
</div>
</div>
<p class="manual_p_title_1"><a class="manual_a_title_1" name="manual_20">Installation</a></p>
<div class="manual_div_content_1">
La biblioth<74>que QxOrm est multi-plateforme et peut <20>tre install<6C>e sur tous types d'environnement :
Windows, Linux (Unix), Mac OS X, Android, iOS, Windows Phone, etc...<br>
<a href="./tutorial_3.html">Un tutoriel complet (avec captures d'<27>cran) pour installer un
environnement de d<>veloppement avec QxOrm sous Windows est disponible en cliquant ici.</a>
<br><br>
L'objectif de ce chapitre est de pr<70>senter rapidement les diff<66>rentes <20>tapes <20> suivre pour
installer QxOrm sur tous types d'environnement :
<ul>
<li><a href="#manual_200">T<EFBFBD>l<EFBFBD>chargement et installation du framework Qt ;</a></li>
<li><a href="#manual_210">T<EFBFBD>l<EFBFBD>chargement et installation de la biblioth<74>que boost (optionnel)
;</a></li>
<li><a href="#manual_220">Param<EFBFBD>trage du fichier de configuration <i>QxOrm.pri</i> (ou
<i>QxOrm.cmake</i>) ;</a></li>
<li><a href="#manual_230">Compilation de la biblioth<74>que QxOrm (avec qmake ou CMake) ;</a></li>
<li><a href="#manual_240">V<EFBFBD>rification des pilotes SQL fournis par Qt (drivers).</a></li>
</ul>
<br>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_200">D<EFBFBD>pendance <20> Qt</a></p>
<div class="manual_div_content">
<table border="0" style="width: 100%" align="center">
<col>
<col>
<tbody>
<tr>
<td valign="middle" width="161" align="center"><a href="http://www.qt.io/"
target="_blank"><img alt="Qt" src="./resource/logo_qt.jpg" width="42" height="50"
border="0"></a></td>
<td align="justify"><b>Qt</b> : biblioth<74>que compl<70>te : IHM
(<i>QtGui</i>), r<>seau (<i>QtNetwork</i>), XML (<i>QtXml</i>), base de donn<6E>es
(<i>QtSql</i>), etc.<br>
La documentation est excellente et le code C++ <20>crit <20> partir
de cette biblioth<74>que est <20> la fois performant et simple de
compr<70>hension.<br>
Depuis le rachat par Nokia puis Digia et sa nouvelle licence LGPL, Qt est
sans contexte la biblioth<74>que phare du moment.<br>
QxOrm est compatible avec les principaux objets d<>finis par Qt
: <i>QObject, QString, QDate, QTime, QDateTime, QList, QHash,
QSharedPointer, QScopedPointer, etc.</i><br>
Il est conseill<6C> d'installer et d'utiliser la derni<6E>re version
de Qt disponible <20> l'adresse suivante : <a href="http://www.qt.io/"
target="_blank">http://www.qt.io/</a></td>
</tr>
</tbody>
</table>
<br>
<b>Remarque :</b> par d<>faut, la biblioth<74>que QxOrm d<>pend uniquement des modules <a
href="http://doc.qt.io/qt-5/qtcore-index.html" target="_blank">QtCore</a> et <a
href="http://doc.qt.io/qt-5/qtsql-index.html" target="_blank">QtSql</a>.
Il est possible d'activer des fonctionnalit<69>s suppl<70>mentaires gr<67>ce au <a
href="#manual_220">fichier de configuration <i>QxOrm.pri</i> (ou <i>QxOrm.cmake</i>)</a> :
ces nouvelles fonctionnalit<69>s peuvent alors ajouter des d<>pendances <20> QxOrm.
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_210">D<EFBFBD>pendance <20> boost
(optionnel)</a></p>
<div class="manual_div_content">
Par d<>faut, la biblioth<74>que QxOrm d<>pend uniquement de Qt (<i>QtCore</i> et <i>QtSql</i>).
L'installation de boost est optionnelle et non requise avec la configuration par d<>faut.
<br />
<b>Remarque :</b> QxOrm propose 2 niveaux de d<>pendance <20> boost en option :
<ul>
<li>une d<>pendance uniquement aux fichiers d'en-t<>tes de boost (<i>*.hpp</i>) : option de
compilation <i>_QX_ENABLE_BOOST</i> ;</li>
<li>une d<>pendance au module <a
href="http://www.boost.org/doc/libs/release/libs/serialization/doc/index.html"
target="_blank">boost serialization</a> : option de compilation
<i>_QX_ENABLE_BOOST_SERIALIZATION</i>.
</li>
</ul>
<br />
<table border="0" style="width: 100%" align="center">
<col>
<col>
<tbody>
<tr>
<td valign="middle" width="161" align="center"><a href="http://www.boost.org/"
target="_blank"><img alt="boost" src="./resource/logo_boost.jpg" width="161"
height="50" border="0"></a></td>
<td align="justify"><b>boost</b> : de nombreux modules de la
biblioth<74>que boost font partie de la nouvelle norme C++.<br>
C'est une biblioth<74>que reconnue pour sa qualit<69>, son code 'C++
moderne', sa documentation, sa portabilit<69>, etc...<br>
QxOrm utilise les fonctionnalit<69>s suivantes de boost :
<i>smart_pointer, type_traits,
multi_index_container, unordered_container, any, tuple,
foreach, function.</i> Toutes ces fonctionnalit<69>s sont <i>header only</i>, la
d<>pendance au module <i>serialization</i> est optionnelle.<br>
Il est conseill<6C> d'installer et d'utiliser la derni<6E>re version
de boost disponible <20> l'adresse suivante : <a href="http://www.boost.org/"
target="_blank">http://www.boost.org/</a>
</td>
</tr>
</tbody>
</table>
<br>
<b>Remarque importante :</b> avec l'option de compilation <i>_QX_ENABLE_BOOST</i>, la
biblioth<74>que QxOrm d<>pend uniquement des fichiers d'en-t<>te <i>*.hpp</i> de boost (utilisation
des biblioth<74>ques <i>header only</i> uniquement).
L'installation de boost est donc tr<74>s simple puisqu'il suffit de d<>zipper le package boost (pour
disposer des fichiers d'en-t<>te <i>*.hpp</i>).
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_220">Fichier de configuration
QxOrm.pri (ou QxOrm.cmake)</a></p>
<div class="manual_div_content">
Le fichier de configuration <i>QxOrm.pri</i> (ou <i>QxOrm.cmake</i>) est divis<69> en plusieurs
sections (chacune <20>tant comment<6E>e) et regroupe les diff<66>rents param<61>trages et options de
compilation disponibles.
Il est fortement recommand<6E> de lire attentivement le fichier de configuration <i>QxOrm.pri</i>
avant de compiler la biblioth<74>que QxOrm.
Il est possible de conserver le param<61>trage par d<>faut, seule la variable
<b>QX_BOOST_INCLUDE_PATH</b> est n<>cessaire si votre projet utilise le framework boost : cette
variable indique o<> trouver les fichiers d'en-t<>te <i>*.hpp</i> de la biblioth<74>que boost :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre> <i>isEmpty(QX_BOOST_INCLUDE_PATH) { QX_BOOST_INCLUDE_PATH = $$quote(<font style="background-color:yellow">D:/Dvlp/_Libs/Boost/1_57/include</font>) }</i> </pre>
</td>
</tr>
</tbody>
</table>
<br>
Si vous ne souhaitez pas modifier le fichier de configuration <i>QxOrm.pri</i>, il est possible
de d<>finir une variable d'environnement nomm<6D>e <b>BOOST_INCLUDE</b> : cette variable
d'environnement sera alors utilis<69>e automatiquement pour valoriser <b>QX_BOOST_INCLUDE_PATH</b>
(lire le fichier <i>QxOrm.pri</i> pour plus d'informations).
<br><br>
Voici une liste non exhaustive des diff<66>rentes options de compilation disponibles (lire le
fichier de configuration <i>QxOrm.pri</i> pour plus de d<>tails), aucune n'<27>tant activ<69>e par
d<>faut :
<ul>
<li><b>_QX_ENABLE_BOOST :</b> ajoute une d<>pendance aux fichiers d'en-t<>tes de boost
(<i>*.hpp</i>), support des classes <i>boost::shared_ptr</i>, <i>boost::optional</i>,
<i>boost::container</i>, etc... ;
</li>
<li><b>_QX_ENABLE_BOOST_SERIALIZATION :</b> active <a href="#manual_610">les fonctionnalit<69>s
de s<>rialisation avec le module boost::serialization</a>. Cette option n<>cessite la
compilation du binaire <a
href="http://www.boost.org/doc/libs/release/libs/serialization/doc/index.html"
target="_blank">boost::serialization</a> et ajoute donc une d<>pendance <20> QxOrm ;</li>
<br>
<li><b>_QX_ENABLE_QT_GUI :</b> support de la s<>rialisation des types du module <a
href="http://doc.qt.io/qt-5/qtgui-index.html" target="_blank">QtGui</a> : <i>QBrush,
QColor, QFont, QImage, QMatrix, QPicture, QPixmap, QRegion</i>. Cette option ajoute une
d<>pendance <20> QxOrm (<a href="http://doc.qt.io/qt-5/qtgui-index.html"
target="_blank">QtGui</a>) ;</li>
<li><b>_QX_ENABLE_QT_NETWORK :</b> active <a href="#manual_80">le module QxService</a> pour
transf<73>rer la couche de donn<6E>es persistante sur le r<>seau (application client/serveur).
Cette option ajoute une d<>pendance <20> QxOrm (<a
href="http://doc.qt.io/qt-5/qtnetwork-index.html" target="_blank">QtNetwork</a>) ;</li>
<li><b>_QX_NO_PRECOMPILED_HEADER :</b> d<>sactive l'utilisation d'un en-t<>te pr<70>compil<69>
(permet de r<>duire les temps de compilation d'un projet) : cette option est n<>cessaire
pour <a href="https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56926"
target="_blank">contourner un bug des versions r<>centes de MinGW</a>, pour tous les
autres compilateurs il est recommand<6E> de travailler avec un <i>precompiled header</i> ;
</li>
<li><b>_QX_NO_RTTI :</b> permet de compiler QxOrm et les projets d<>pendants sans <a
href="https://en.wikipedia.org/wiki/Run-time_type_information" target="_blank">les
informations de type C++ RTTI</a> ;</li>
<li><b>_QX_STATIC_BUILD :</b> permet de compiler la biblioth<74>que QxOrm en mode statique ;
</li>
<li><b>_QX_UNITY_BUILD :</b> r<>duit les temps de compilation de la biblioth<74>que QxOrm en
utilisant le concept <i>unity build</i> : un seul fichier source <i>all.cpp</i> <20>
compiler. Il est recommand<6E> d'activer cette option avec <a href="https://cmake.org/"
target="_blank">CMake</a> (car ne supporte pas nativement les en-t<>tes pr<70>compil<69>s) ;
</li>
<li><b>_QX_ENABLE_MONGODB :</b> <a href="#manual_95">support de la base de donn<6E>es
MongoDB</a>, la biblioth<74>que QxOrm devient ainsi un ODM (Object Document Mapper).</li>
</ul>
<br>
<b>Remarque :</b> le fichier de configuration <i>QxOrm.pri</i> (ou <i>QxOrm.cmake</i>) devra
<20>tre inclus dans tous les projets d<>pendants de la biblioth<74>que QxOrm en ajoutant la ligne
suivante dans le fichier <i>*.pro</i> du projet :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre> include(<i>my_path_to_QxOrm_library</i>/QxOrm.pri) </pre>
</td>
</tr>
</tbody>
</table>
<br>
<b>Autre remarque :</b> <20> la place de <i>qmake</i>, il est possible d'utiliser <a
href="https://cmake.org/" target="_blank">l'outil de compilation CMake</a> pour configurer et
construire la biblioth<74>que QxOrm.
CMake propose un outil graphique afin de visualiser et param<61>trer les diff<66>rentes options
disponibles :
<br><br>
<img alt="QxOrm and CMake" src="./resource/qxorm_cmake.png" />
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_230">Compiler la biblioth<74>que
QxOrm (avec qmake ou CMake)</a></p>
<div class="manual_div_content">
<b>QxOrm</b> utilise le processus <i>qmake</i> de la biblioth<74>que <b>Qt</b> pour g<>n<EFBFBD>rer les
<i>makefile</i> et compiler le projet (il est <20>galement possible d'utiliser <a
href="https://cmake.org/" target="_blank">l'outil de compilation CMake</a>, un fichier
<i>CMakeLists.txt</i> <20>tant fourni avec la biblioth<74>que QxOrm).<br>
<i>qmake</i> est multi-plateforme et fonctionne parfaitement sous Windows, Linux (Unix) et Mac
OS X.<br>
Pour compiler <b>QxOrm</b>, il suffit d'ex<65>cuter les commandes suivantes :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre> <i>qmake</i>
<i>make debug</i>
<i>make release</i> </pre>
</td>
</tr>
</tbody>
</table>
<br>
Sous <b>Windows</b>, des fichiers <i>*.vcproj</i> et <i>*.sln</i> sont disponibles pour les
<20>diteurs <b>Microsoft Visual C++</b>.<br>
Les fichiers <i>*.pro</i> sont lisibles par l'<27>diteur <b>Qt Creator</b>, et des plugins existent
permettant de s'interfacer avec de nombreux <20>diteurs C++.<br>
Les fichiers <i>mingw_build_all_debug.bat</i> et <i>mingw_build_all_release.bat</i> pr<70>sents
dans le dossier <i>./tools/</i> permettent de compiler rapidement QxOrm ainsi que tous les tests
avec le compilateur <b>MinGW</b> sous Windows.<br>
Les fichiers <i>gcc_build_all_debug.sh</i> et <i>gcc_build_all_release.sh</i> pr<70>sents dans le
dossier <i>./tools/</i> permettent de compiler rapidement QxOrm ainsi que tous les tests avec
<b>GCC</b> sous <b>Linux</b>.<br>
Enfin, les fichiers <i>osx_build_all_debug.sh</i> et <i>osx_build_all_release.sh</i> pr<70>sents
dans le dossier <i>./tools/</i> permettent de compiler rapidement QxOrm ainsi que tous les tests
sous <b>Mac OS X</b> (merci <20> Dominique Billet pour l'<27>criture des scripts).<br>
<br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_240">Pilotes SQL fournis par
Qt (drivers)</a></p>
<div class="manual_div_content">
<b>QxOrm</b> utilise le moteur <a href="http://doc.qt.io/qt-5/sql-programming.html"
target="_blank"><i>QtSql</i></a> de <b>Qt</b> bas<61> sur un syst<73>me de plugin.<br>
<a href="http://doc.qt.io/qt-5/sql-driver.html" target="_blank">Une liste d<>taill<6C>e des bases de
donn<6E>es support<72>es est disponible sur le site de Qt</a>.<br>
Le plugin <i>ODBC</i> (<i>QODBC</i>) assure une compatibilit<69> avec de nombreuses bases de
donn<6E>es.<br>
Pour des performances optimales, il est conseill<6C> d'utiliser un plugin sp<73>cifique <20> une base de
donn<6E>es :
<ul>
<li><i>QMYSQL</i> : MySQL ;</li>
<li><i>QPSQL</i> : PostgreSQL (versions 7.3 and above) ;</li>
<li><i>QOCI</i> : Oracle Call Interface Driver ;</li>
<li><i>QSQLITE</i> : SQLite version 3 ;</li>
<li><i>QDB2</i> : IBM DB2 (version 7.1 and above) ;</li>
<li><i>QIBASE</i> : Borland InterBase ;</li>
<li><i>QTDS</i> : Sybase Adaptive Server.</li>
</ul>
<b>Remarque :</b> pour se connecter <20> une base de donn<6E>es <i>Microsoft SQL Server</i>, il est
n<>cessaire d'utiliser le pilote <i>ODBC</i> (plugin <i>QODBC</i>).
<br><br>
<b>Autre remarque :</b> la biblioth<74>que QxOrm supporte <20>galement <a href="#manual_95">la base de
donn<6E>es MongoDB (C++ ODM Object Document Mapper)</a>.
<br><br>
</div>
</div>
<p class="manual_p_title_1"><a class="manual_a_title_1" name="manual_30">Persistance - Object
Relational Mapping (ORM)</a></p>
<div class="manual_div_content_1">
La biblioth<74>que QxOrm fournit un moteur de persistance des donn<6E>es bas<61> sur le module <a
href="http://doc.qt.io/qt-5/sql-programming.html" target="_blank"><i>QtSql</i></a> de Qt.
Ce moteur de persistance utilise la technique de programmation : <b>Object Relational Mapping
(ORM)</b>.
<br><br>
<a href="https://fr.wikipedia.org/wiki/Mapping_objet-relationnel" target="_blank">D<EFBFBD>finition du
site Wikipedia :</a> un mapping objet-relationnel (en anglais <i>object-relational mapping</i>
ou <i>ORM</i>) est une technique de programmation informatique qui cr<63>e l'illusion d'une base de
donn<6E>es orient<6E>e objet <20> partir d'une base de donn<6E>es relationnelle en d<>finissant des
correspondances entre cette base de donn<6E>es et les objets du langage utilis<69>. On pourrait le
d<>signer par <20> correspondance entre monde objet et monde relationnel <20>.
Le mapping objet-relationnel consiste <20> 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'<27>liminer la duplication de code dans les
op<6F>rations <i>CRUD</i>.
<br><br>
Pour effectuer cette correspondance entre le monde objet et le monde relationnel, ainsi pour que
proposer l'ensemble de ses fonctionnalit<69>s, la biblioth<74>que QxOrm impose l'enregistrement de
classes C++ dans le contexte QxOrm.
Nous allons donc d<>buter ce chapitre de la fa<66>on suivante : <b>comment enregistrer une classe C++
dans le contexte QxOrm ?</b>
<br><br>
<b>Remarque :</b> la biblioth<74>que QxOrm supporte <20>galement <a href="#manual_95">la base de donn<6E>es
MongoDB (C++ ODM Object Document Mapper)</a>.
<br><br>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_300">D<EFBFBD>finir une classe dans
le contexte QxOrm (mapping)</a></p>
<div class="manual_div_content">
Toutes les classes C++ peuvent <20>tre enregistr<74>es dans le contexte QxOrm : il n'y a pas besoin de
d<>river d'un super objet, et vous pouvez <20>crire vos m<>thodes de classes et accesseurs sans
aucune contrainte.
Enregistrer une classe C++ dans le contexte QxOrm signifie :
<ul>
<li>dans le fichier en-t<>te <i>*.h</i> contenant la d<>finition de la classe : utilisation de
la macro <b>QX_REGISTER_HPP(class_name, base_class, class_version)</b> ;</li>
<li>dans le fichier source <i>*.cpp</i> contenant l'impl<70>mentation de la classe : utilisation
de la macro <b>QX_REGISTER_CPP(class_name)</b> ;</li>
<li>dans le fichier source <i>*.cpp</i> contenant l'impl<70>mentation de la classe :
sp<73>cialisation de la fonction template : <b>void
qx::register_class&lt;T&gt;(qx::QxClass&lt;T&gt; & t)</b>.</li>
</ul>
Par exemple, voici comment d<>clarer une classe <i>person</i> avec 4 propri<72>t<EFBFBD>s enregistr<74>es dans
le contexte QxOrm : <i>id</i>, <i>firstName</i>, <i>lastName</i>, <i>birthDate</i> :<br>
<br>
<i>* Fichier person.h :</i><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="person.h">
<pre><span class="pre">#ifndef _PERSON_H_
#define _PERSON_H_
</span><span class="keyword">
class</span> person<span class="operator">
{</span><span class="keyword">
public</span><span class="operator">:</span><span class="type">
long</span> id<span class="operator">;</span>
QString firstName<span class="operator">;</span>
QString lastName<span class="operator">;</span>
QDateTime birthDate<span class="operator">;</span>
person<span class="operator">() :</span> id<span class="operator">(</span><span class="int">0</span><span class="operator">) { ; }</span><span class="keyword">
virtual</span><span class="operator"> ~</span>person<span class="operator">() { ; }
};</span>
<font style="background-color:yellow">QX_REGISTER_HPP_MY_TEST_EXE<span class="operator">(</span>person<span class="operator">,</span> qx<span class="operator">::</span>trait<span class="operator">::</span>no_base_class_defined<span class="operator">,</span><span class="int"> 0</span><span class="operator">)</span></font><span class="comment">
/* This macro is necessary to register 'person' class in QxOrm context */
/* param 1 : the current class to register =&gt; 'person' */
/* param 2 : the base class, if no base class, use the qx trait =&gt; 'qx::trait::no_base_class_defined' */
/* param 3 : the class version used by serialization engine to provide 'ascendant compatibility' */</span><span class="pre">
#endif <span class="comment">// _PERSON_H_</span></span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<i>* Fichier person.cpp :</i><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="person.cpp">
<pre><span class="pre">#include <span class="string">"precompiled.h"</span> <span class="comment">// Precompiled-header with '#include &lt;QxOrm.h&gt;' and '#include "export.h"'</span>
#include <span class="string">"person.h"</span> <span class="comment">// Class definition 'person'</span>
#include <span class="string">&lt;QxOrm_Impl.h&gt;</span> <span class="comment">// Automatic memory leak detection and boost serialization export macro</span>
</span>
<font style="background-color:yellow">QX_REGISTER_CPP_MY_TEST_EXE<span class="operator">(</span>person<span class="operator">)</span></font><span class="comment"> // This macro is necessary to register 'person' class in QxOrm context
</span><span class="keyword">
namespace</span> qx<span class="operator"> {</span><font style="background-color:yellow"><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>person<span class="operator">&gt; &amp;</span> t<span class="operator">)</span></font>
<span class="operator">{</span>
t<span class="operator">.</span>setName<span class="operator">(</span><span class="string">"t_person"</span><span class="operator">)</span>;<span class="comment"> // 'person' C++ class is mapped to 't_person' database table</span>
t<span class="operator">.</span>id<span class="operator">(&amp;</span> person<span class="operator">::</span>id<span class="operator">,</span><span class="string"> "id"</span><span class="operator">);</span><span class="comment"> // Register 'person::id' &lt;=&gt; primary key in your database
</span> t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>firstName<span class="operator">,</span><span class="string"> "first_name"</span><span class="operator">);</span><span class="comment"> // Register 'person::firstName' property mapped to 'first_name' database column name
</span> t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>lastName<span class="operator">,</span><span class="string"> "last_name"</span><span class="operator">);</span><span class="comment"> // Register 'person::lastName' property mapped to 'last_name' database column name
</span> t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>birthDate<span class="operator">,</span><span class="string"> "birth_date"</span><span class="operator">);</span><span class="comment"> // Register 'person::birthDate' property mapped to 'birth_date' database column name
</span><span class="operator">}}</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
<b>Remarque :</b> les m<>thodes <a href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::id()</a> et <a
href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::data()</a> retournent une instance de type : <a
href="../doxygen/html/classqx_1_1_ix_data_member.html" target="_blank">qx::IxDataMember</a>
(classe de base pour l'enregistrement des donn<6E>es membre).
Gr<47>ce <20> cette instance, il est possible de personnaliser le comportement par d<>faut propos<6F> par
la classe <a href="../doxygen/html/classqx_1_1_ix_data_member.html"
target="_blank">qx::IxDataMember</a>, comme par exemple dans le chapitre : <a
href="#manual_3050">D<EFBFBD>finir une donn<6E>e membre <i>transient</i></a>.
<br><br>
<b>Autre remarque :</b> il est <20>galement possible d'enregistrer des m<>thodes de classe dans le
contexte QxOrm (gestion des m<>thodes <i>static</i> et <i>non static</i>) avec les m<>thodes <a
href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::fct_0()</a>, <a
href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::fct_1()</a>, etc...
Cette fonctionnalit<69> fait partie du <a href="#manual_70">moteur d'introspection</a> de la
biblioth<74>que QxOrm, plus de d<>tails dans le chapitre : <a href="#manual_730">Appeler
dynamiquement une fonction</a>.
<br><br>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3000">Cl<EFBFBD> primaire autre
que le type par d<>faut "long"</a></p>
<div class="manual_div_content">
Par d<>faut, lorsqu'un mapping d'une classe C++ est <20>crit avec la m<>thode <i>void
qx::register_class&lt;T&gt;</i>, l'identifiant associ<63> <20> la classe est de type <i>long</i>
(cl<63> primaire avec auto-incr<63>mentation dans la base de donn<6E>es).<br>
<br>
Il est possible de d<>finir un identifiant d'un autre type en utilisant la macro
<b>QX_REGISTER_PRIMARY_KEY</b>.<br>
Cette macro sp<73>cialise le template <i>qx::trait::get_primary_key&lt;T&gt;</i> pour associer
un type d'identifiant <20> une classe C++.<br>
<br>
Par exemple, pour d<>finir un identifiant unique de type <i>QString</i> pour la classe C++
<i>myClass</i> (mapp<70>e vers une table de la BDD avec une colonne de type <i>VARCHAR</i> pour
cl<63> primaire), il suffit d'<27>crire :
<i><b>QX_REGISTER_PRIMARY_KEY(myClass, QString)</b></i><br>
<br>
Voici un exemple d'utilisation de la macro <b>QX_REGISTER_PRIMARY_KEY</b> avec une classe
<i>author</i> poss<73>dant un identifiant de type <i>QString</i> :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="pre">#ifndef _QX_BLOG_AUTHOR_H_
#define _QX_BLOG_AUTHOR_H_
</span><span class="keyword">
class</span> author<span class="operator">
{</span><span class="keyword">
public</span><span class="operator">:</span><span class="comment">
// -- propri<72>t<EFBFBD>s
</span> QString m_id<span class="operator">;</span>
QString m_name<span class="operator">;</span><span class="comment">
// -- constructeur, destructeur virtuel
</span> author<span class="operator">() { ; }</span><span class="keyword">
virtual</span><span class="operator"> ~</span>author<span class="operator">() { ; }
};</span>
<font style="background-color:yellow">QX_REGISTER_PRIMARY_KEY<span class="operator">(</span>author<span class="operator">,</span> QString<span class="operator">)</span></font>
QX_REGISTER_HPP_QX_BLOG<span class="operator">(</span>author<span class="operator">,</span> qx<span class="operator">::</span>trait<span class="operator">::</span>no_base_class_defined<span class="operator">,</span><span class="int"> 0</span><span class="operator">)</span><span class="pre">
#endif // _QX_BLOG_AUTHOR_H_</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3010">Cl<EFBFBD> primaire sur
plusieurs colonnes (composite key)</a></p>
<div class="manual_div_content">
<b>QxOrm</b> supporte la notion de '<i>multi-columns primary key</i>'.<br>
L'identifiant de la classe doit <20>tre du type suivant :
<ul>
<li><i>QPair</i> ou <i>std::pair</i> pour d<>finir deux colonnes ;
</li>
<li><i>boost::tuple</i> (ou <i>std::tuple</i>) pour d<>finir de deux <20> neuf colonnes.
</li>
</ul>
Il est n<>cessaire d'utiliser la macro <b><i>QX_REGISTER_PRIMARY_KEY()</i></b> pour
sp<73>cialiser le template et ainsi d<>finir le type d'identifiant sur plusieurs colonnes.<br>
La liste des noms des colonnes doit <20>tre de la forme suivante :
'<i>column1|column2|column3|etc.</i>'.<br>
<br>
<b>Exemple d'utilisation</b> avec la classe '<i>author</i>' du projet
'<i>qxBlogCompositeKey</i>', cette classe poss<73>de un identifiant sur trois colonnes :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="pre">#ifndef _QX_BLOG_AUTHOR_H_
#define _QX_BLOG_AUTHOR_H_
</span><span class="keyword">
class</span> blog<span class="operator">;</span><span class="keyword">
class</span> QX_BLOG_DLL_EXPORT author<span class="operator">
{</span>
QX_REGISTER_FRIEND_CLASS<span class="operator">(</span>author<span class="operator">)</span><span class="keyword">
public</span><span class="operator">:</span><span class="comment">
// -- cl<63> compos<6F>e (cl<63> primaire d<>finie sur plusieurs colonnes dans la base de donn<6E>es)
</span> <font style="background-color:yellow"><span class="keyword">typedef</span> boost<span class="operator">::</span>tuple<span class="operator">&lt;</span>QString<span class="operator">,</span><span class="type"> long</span><span class="operator">,</span> QString<span class="operator">&gt;</span> type_composite_key<span class="operator">;</span></font>
<font style="background-color:yellow"><span class="keyword">static</span> QString str_composite_key<span class="operator">() {</span><span class="flow"> return</span><span class="string"> "author_id_0|author_id_1|author_id_2"</span><span class="operator">; }</span></font><span class="comment">
// -- typedef
</span><span class="keyword"> typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>blog<span class="operator">&gt;</span> blog_ptr<span class="operator">;</span><span class="keyword">
typedef</span> std<span class="operator">::</span>vector<span class="operator">&lt;</span>blog_ptr<span class="operator">&gt;</span> list_blog<span class="operator">;</span><span class="comment">
// -- enum
</span><span class="keyword"> enum</span> enum_sex<span class="operator"> {</span> male<span class="operator">,</span> female<span class="operator">,</span> unknown<span class="operator"> };</span><span class="comment">
// -- propri<72>t<EFBFBD>s
</span> type_composite_key m_id<span class="operator">;</span>
QString m_name<span class="operator">;</span>
QDate m_birthdate<span class="operator">;</span>
enum_sex m_sex<span class="operator">;</span>
list_blog m_blogX<span class="operator">;</span><span class="comment">
// -- constructeur, destructeur virtuel
</span> author<span class="operator">() :</span> m_id<span class="operator">(</span><span class="string">""</span><span class="operator">,</span><span class="int"> 0</span><span class="operator">,</span><span class="string"> ""</span><span class="operator">),</span> m_sex<span class="operator">(</span>unknown<span class="operator">) { ; }</span><span class="keyword">
virtual</span><span class="operator"> ~</span>author<span class="operator">() { ; }</span><span class="comment">
// -- m<>thodes
</span><span class="type"> int</span> age<span class="operator">()</span><span class="keyword"> const</span><span class="operator">;</span><span class="comment">
// -- m<>thodes d'acc<63>s <20> la cl<63> compos<6F>e
</span> type_composite_key getId<span class="operator">()</span><span class="keyword"> const</span><span class="operator"> {</span><span class="flow"> return</span> m_id<span class="operator">; }</span>
QString getId_0<span class="operator">()</span><span class="keyword"> const</span><span class="operator"> {</span><span class="flow"> return</span> boost<span class="operator">::</span>tuples<span class="operator">::</span>get<span class="operator">&lt;</span><span class="int">0</span><span class="operator">&gt;(</span>m_id<span class="operator">); }</span><span class="type">
long</span> getId_1<span class="operator">()</span><span class="keyword"> const</span><span class="operator"> {</span><span class="flow"> return</span> boost<span class="operator">::</span>tuples<span class="operator">::</span>get<span class="operator">&lt;</span><span class="int">1</span><span class="operator">&gt;(</span>m_id<span class="operator">); }</span>
QString getId_2<span class="operator">()</span><span class="keyword"> const</span><span class="operator"> {</span><span class="flow"> return</span> boost<span class="operator">::</span>tuples<span class="operator">::</span>get<span class="operator">&lt;</span><span class="int">2</span><span class="operator">&gt;(</span>m_id<span class="operator">); }</span><span class="comment">
// -- m<>thodes de modification de la cl<63> compos<6F>e
</span><span class="type"> void</span> setId_0<span class="operator">(</span><span class="keyword">const</span> QString<span class="operator"> &amp;</span> s<span class="operator">) {</span> boost<span class="operator">::</span>tuples<span class="operator">::</span>get<span class="operator">&lt;</span><span class="int">0</span><span class="operator">&gt;(</span>m_id<span class="operator">) =</span> s<span class="operator">; }</span><span class="type">
void</span> setId_1<span class="operator">(</span><span class="type">long</span> l<span class="operator">) {</span> boost<span class="operator">::</span>tuples<span class="operator">::</span>get<span class="operator">&lt;</span><span class="int">1</span><span class="operator">&gt;(</span>m_id<span class="operator">) =</span> l<span class="operator">; }</span><span class="type">
void</span> setId_2<span class="operator">(</span><span class="keyword">const</span> QString<span class="operator"> &amp;</span> s<span class="operator">) {</span> boost<span class="operator">::</span>tuples<span class="operator">::</span>get<span class="operator">&lt;</span><span class="int">2</span><span class="operator">&gt;(</span>m_id<span class="operator">) =</span> s<span class="operator">; }
};</span>
<font style="background-color:yellow">QX_REGISTER_PRIMARY_KEY<span class="operator">(</span>author<span class="operator">,</span> author<span class="operator">::</span>type_composite_key<span class="operator">)</span></font>
QX_REGISTER_HPP_QX_BLOG<span class="operator">(</span>author<span class="operator">,</span> qx<span class="operator">::</span>trait<span class="operator">::</span>no_base_class_defined<span class="operator">,</span><span class="int"> 0</span><span class="operator">)</span><span class="keyword">
typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>author<span class="operator">&gt;</span> author_ptr<span class="operator">;</span><span class="keyword">
typedef</span> qx<span class="operator">::</span>QxCollection<span class="operator">&lt;</span>author<span class="operator">::</span>type_composite_key<span class="operator">,</span> author_ptr<span class="operator">&gt;</span> list_author<span class="operator">;</span><span class="pre">
#endif // _QX_BLOG_AUTHOR_H_
</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="pre">#include "../include/precompiled.h"
#include "../include/author.h"
#include "../include/blog.h"
#include &lt;QxOrm_Impl.h&gt;
</span>
QX_REGISTER_CPP_QX_BLOG<span class="operator">(</span>author<span class="operator">)</span><span class="keyword">
namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>author<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span>
<font style="background-color:yellow">t<span class="operator">.</span>id<span class="operator">(&amp;</span> author<span class="operator">::</span>m_id<span class="operator">,</span> author<span class="operator">::</span>str_composite_key<span class="operator">());</span></font>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> author<span class="operator">::</span>m_name<span class="operator">,</span><span class="string"> "name"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> author<span class="operator">::</span>m_birthdate<span class="operator">,</span><span class="string"> "birthdate"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> author<span class="operator">::</span>m_sex<span class="operator">,</span><span class="string"> "sex"</span><span class="operator">);</span>
t<span class="operator">.</span>relationOneToMany<span class="operator">(&amp;</span> author<span class="operator">::</span>m_blogX<span class="operator">,</span> blog<span class="operator">::</span>str_composite_key<span class="operator">(),</span> author<span class="operator">::</span>str_composite_key<span class="operator">());</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">int</span><span class="operator">&gt;(&amp;</span> author<span class="operator">::</span>age<span class="operator">,</span><span class="string"> "age"</span><span class="operator">);
}}</span><span class="type">
int</span> author<span class="operator">::</span>age<span class="operator">()</span><span class="keyword"> const</span><span class="operator">
{</span><span class="flow">
if</span><span class="operator"> (!</span> m_birthdate<span class="operator">.</span>isValid<span class="operator">()) {</span><span class="flow"> return</span><span class="operator"> -</span><span class="int">1</span><span class="operator">; }</span><span class="flow">
return</span><span class="operator"> (</span>QDate<span class="operator">::</span>currentDate<span class="operator">().</span>year<span class="operator">() -</span> m_birthdate<span class="operator">.</span>year<span class="operator">());
}</span>
</pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3020">Donn<EFBFBD>es membres
public/protected/private</a></p>
<div class="manual_div_content">
Pour enregistrer des membres <i>private</i> ou <i>protected</i> dans le contexte QxOrm
(fonction <i>qx::register_class&lt;T&gt;</i>), il faut d<>clarer les <i>friend class</i>
n<>cessaires.<br>
Pour simplifier l'<27>criture avec les <i>template</i> C++, la biblioth<74>que QxOrm fournit la
macro suivante : <b>QX_REGISTER_FRIEND_CLASS(myClass)</b>.<br>
Un exemple d'utilisation se trouve dans le dossier <i>./test/qxDllSample/dll1/</i> du package
QxOrm avec la classe <i>CPerson</i> :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">namespace</span> qx<span class="operator"> {</span><span class="keyword">
namespace</span> test<span class="operator"> {</span><span class="keyword">
class</span> QX_DLL1_EXPORT CPerson<span class="operator"> :</span><span class="keyword"> public</span> QObject<span class="operator">
{</span>
Q_OBJECT
<font style="background-color:yellow">QX_REGISTER_FRIEND_CLASS<span class="operator">(</span>qx<span class="operator">::</span>test<span class="operator">::</span>CPerson<span class="operator">)</span></font><span class="comment">
// etc...
</span><span class="operator">
};
}</span><span class="comment"> // namespace test
</span><span class="operator">}</span><span class="comment"> // namespace qx</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3030">Espace de nom
(namespace)</a></p>
<div class="manual_div_content">
Si une classe est d<>finie dans un espace de nom (<i>namespace</i>), alors une erreur de
compilation se produit avec l'utilisation des macros : <b>QX_REGISTER_HPP</b> et
<b>QX_REGISTER_CPP</b>.
Pour <20>viter ces erreurs de compilation, il est n<>cessaire d'utiliser les macros suivantes :
<b>QX_REGISTER_COMPLEX_CLASS_NAME_HPP</b> et <b>QX_REGISTER_COMPLEX_CLASS_NAME_CPP</b>.<br>
<br>
Vous trouverez un exemple d'utilisation dans le dossier <i>./test/qxDllSample/dll1/</i> de la
distribution de QxOrm avec la classe <i>CPerson</i> d<>finie dans l'espace de nom
<i>qx::test</i> :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre> QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QX_DLL1(qx::test::CPerson, QObject, 0, qx_test_CPerson) </pre>
</td>
</tr>
</tbody>
</table>
<br>
Les macros <b>QX_REGISTER_COMPLEX_CLASS_NAME...</b> n<>cessitent un param<61>tre suppl<70>mentaire
(dans l'exemple ci-dessus il s'agit du param<61>tre <i>qx_test_CPerson</i>) afin de cr<63>er une
variable globale.<br>
Celle-ci est appel<65>e d<>s le lancement de l'application.<br>
La construction de cette instance globale d<>clare la classe dans <a href="#manual_740">le
module <i>QxFactory</i> (mod<6F>le de conception fabrique ou design pattern factory)</a>.<br>
Un objet C++ ne pouvant pas se nommer avec des caract<63>res "<i>::</i>", le param<61>tre
suppl<70>mentaire de la macro permet de remplacer tous les "<i>::</i>" par des "<i>_</i>".
<br><br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3040">Types C++ support<72>s
par QxOrm</a></p>
<div class="manual_div_content">
La biblioth<74>que QxOrm supporte la plupart des types primitifs du standard C++ et du framework
Qt (num<75>riques, bool<6F>ens, chaines de caract<63>res, date/heure, collections, pointeurs et
pointeurs intelligents, etc...).
Voici un exemple pr<70>sentant une liste (non exhaustive) de types C++ support<72>s ainsi que
l'association par d<>faut du type de base de donn<6E>es (format <i>SQLite</i>) :
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="string">"bool"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "SMALLINT"
"qx_bool"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "SMALLINT"
"short"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "SMALLINT"
"int"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "INTEGER"
"long"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "INTEGER"
"long long"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "INTEGER"
"float"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "FLOAT"
"double"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "FLOAT"
"long double"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "FLOAT"
"unsigned short"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "SMALLINT"
"unsigned int"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "INTEGER"
"unsigned long"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "INTEGER"
"unsigned long long"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "INTEGER"
"std::string"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TEXT"
"std::wstring"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TEXT"
"QString"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TEXT"
"QVariant"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TEXT"
"QUuid"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TEXT"
"QDate"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "DATE"
"QTime"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TIME"
"QDateTime"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TIMESTAMP"
"QByteArray"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "BLOB"
"qx::QxDateNeutral"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TEXT"
"qx::QxTimeNeutral"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TEXT"
"qx::QxDateTimeNeutral"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TEXT"</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<b>Remarque :</b> il est <20>galement possible de persister un type non g<>r<EFBFBD> par d<>faut par la
biblioth<74>que QxOrm. Rendez-vous au chapitre <a href="#manual_460"><b><i>Persister des types
personnalis<69>s</i></b></a> pour plus de d<>tails sur cette fonctionnalit<69>.
<br><br>
<b>Autre remarque :</b> concernant l'association d'un type C++ avec le type de base de
donn<6E>es associ<63>, rendez-vous au chapitre <a href="#manual_475"><b><i>Associer un type SQL <20>
une classe C++</i></b></a> pour plus de d<>tails.
<br><br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3050">D<EFBFBD>finir une donn<6E>e
membre <i>transient</i></a></p>
<div class="manual_div_content">
Une donn<6E>e membre <i>transient</i> n'est pas associ<63>e <20> une colonne d'une table de la base de
donn<6E>es.
Le <a href="../doxygen/html/group___qx_dao.html" target="_blank">module QxDao</a> ignore donc
cette propri<72>t<EFBFBD> pour toutes les requ<71>tes <20> la base de donn<6E>es.
<br><br>
A quoi sert l'enregistrement d'une donn<6E>e membre <i>transient</i> dans le contexte QxOrm
?<br>
Enregistrer une donn<6E>e membre <i>transient</i> dans le contexte QxOrm permet de disposer des
autres fonctionnalit<69>s de la biblioth<74>que QxOrm sur cette propri<72>t<EFBFBD>, comme par exemple : <a
href="./manual.html#manual_60">s<EFBFBD>rialisation</a>, <a
href="./manual.html#manual_70">introspection</a>, etc...
<br><br>
La m<>thode <a href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::data()</a> dispose d'un param<61>tre optionnel nomm<6D> :
<i>bool bDao</i> (par d<>faut, valeur <20> <i>true</i>).
Par exemple, ajoutons une propri<72>t<EFBFBD> <i>transient</i> nomm<6D>e <i>age</i> <20> la classe
<i>person</i> (cette propri<72>t<EFBFBD> n'a pas besoin d'<27>tre stock<63>e en base de donn<6E>es puisque nous
disposons d<>j<EFBFBD> de la propri<72>t<EFBFBD> <i>birthDate</i>) :
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>person<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span>
t<span class="operator">.</span>id<span class="operator">(&amp;</span> person<span class="operator">::</span>id<span class="operator">,</span><span class="string"> "id"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>firstName<span class="operator">,</span><span class="string"> "first_name"</span><span class="operator">;);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>lastName<span class="operator">,</span><span class="string"> "last_name"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>birthDate<span class="operator">,</span><span class="string"> "birth_date"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>age<span class="operator">,</span><span class="string"> "age"</span><span class="operator">,</span><span class="int"> 0</span><span class="operator">,</span><span class="bool"> true</span><span class="operator">,</span><span class="bool"> <font style="background-color:yellow">false</font></span><span class="operator">);
}}</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Voici une autre fa<66>on de d<>finir une propri<72>t<EFBFBD> <i>transient</i> en r<>cup<75>rant l'instance de
type <a href="../doxygen/html/classqx_1_1_ix_data_member.html"
target="_blank">qx::IxDataMember</a> :
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>person<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span>
t<span class="operator">.</span>id<span class="operator">(&amp;</span> person<span class="operator">::</span>id<span class="operator">,</span><span class="string"> "id"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>firstName<span class="operator">,</span><span class="string"> "first_name"</span><span class="operator">;);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>lastName<span class="operator">,</span><span class="string"> "last_name"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>birthDate<span class="operator">,</span><span class="string"> "birth_date"</span><span class="operator">);</span>
IxDataMember<span class="operator"> *</span> pDataMember<span class="operator"> =</span> t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>age<span class="operator">,</span><span class="string"> "age"</span><span class="operator">);</span>
<font style="background-color:yellow">pDataMember<span class="operator">-&gt;</span>setDao<span class="operator">(</span><span class="bool">false</span><span class="operator">);</span></font>
<span class="operator">}}</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_310">Connexion <20> la base de
donn<6E>es</a></p>
<div class="manual_div_content">
La connexion <20> la base de donn<6E>es peut <20>tre param<61>tr<74>e avec la classe singleton : <a
href="../doxygen/html/classqx_1_1_qx_sql_database.html"
target="_blank"><b>qx::QxSqlDatabase</b></a>.<br>
Voici un exemple de param<61>trage <20> une base de donn<6E>es SQLite nomm<6D>e <i>test_qxorm.db</i> :
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="qx::QxSqlDatabase">
<pre> <span class="comment">// Init parameters to connect to database</span>
qx<span class="operator">::</span>QxSqlDatabase<span class="operator">::</span>getSingleton<span class="operator">()-&gt;</span>setDriverName<span class="operator">(</span><span class="string">"QSQLITE"</span><span class="operator">);</span>
qx<span class="operator">::</span>QxSqlDatabase<span class="operator">::</span>getSingleton<span class="operator">()-&gt;</span>setDatabaseName<span class="operator">(</span><span class="string">"./test_qxorm.db"</span><span class="operator">);</span>
qx<span class="operator">::</span>QxSqlDatabase<span class="operator">::</span>getSingleton<span class="operator">()-&gt;</span>setHostName<span class="operator">(</span><span class="string">"localhost"</span><span class="operator">);</span>
qx<span class="operator">::</span>QxSqlDatabase<span class="operator">::</span>getSingleton<span class="operator">()-&gt;</span>setUserName<span class="operator">(</span><span class="string">"root"</span><span class="operator">);</span>
qx<span class="operator">::</span>QxSqlDatabase<span class="operator">::</span>getSingleton<span class="operator">()-&gt;</span>setPassword<span class="operator">(</span><span class="string">""</span><span class="operator">);</span>
</pre>
</td>
</tr>
</tbody>
</table>
<br>
Une fois les param<61>tres de connexion renseign<67>s dans la classe singleton <a
href="../doxygen/html/classqx_1_1_qx_sql_database.html"
target="_blank"><b>qx::QxSqlDatabase</b></a>, toutes les op<6F>rations avec la base de donn<6E>es
effectu<74>es par la biblioth<74>que QxOrm utiliserons ces param<61>tres.
Pour plus d'informations sur les param<61>tres de connexion <20> renseigner, il est recommand<6E> de lire
<a href="http://doc.qt.io/qt-5/qsqldatabase.html" target="_blank">la documentation de la classe
QSqlDatabase du framework Qt</a>.
<br><br>
<b>Remarque :</b> la classe <a href="../doxygen/html/classqx_1_1_qx_sql_database.html"
target="_blank"><b>qx::QxSqlDatabase</b></a> g<>re automatiquement les appels <20> la base de
donn<6E>es dans diff<66>rents threads (<i>multi-threading</i>).
<br><br>
<b>Autre remarque :</b> il est possible de g<>rer son propre pool de connexions <20> la base de
donn<6E>es, et de travailler <20>galement avec plusieurs bases de donn<6E>es distinctes : rendez-vous
dans le chapitre <a href="#manual_500"><b><i>Travailler avec plusieurs bases de
donn<6E>es</i></b></a> pour plus d'informations sur cette fonctionnalit<69>.
<br><br>
<b>Autre remarque :</b> suivant le pilote SQL renseign<67> dans les param<61>tres de connexion, la
biblioth<74>que QxOrm associe automatiquement un g<>n<EFBFBD>rateur SQL.
Ce g<>n<EFBFBD>rateur SQL permet de g<>rer les sp<73>cificit<69>s propres <20> chaque type de base de donn<6E>es.
Tous les g<>n<EFBFBD>rateurs SQL h<>ritent de la classe de base : <a
href="../doxygen/html/classqx_1_1dao_1_1detail_1_1_ix_sql_generator.html"
target="_blank">qx::dao::detail::IxSqlGenerator</a> :
<ul>
<li>pilote <i>QMYSQL</i> : g<>n<EFBFBD>rateur SQL <a
href="../doxygen/html/classqx_1_1dao_1_1detail_1_1_qx_sql_generator___my_s_q_l.html"
target="_blank">qx::dao::detail::QxSqlGenerator_MySQL</a> ;</li>
<li>pilote <i>QPSQL</i> : g<>n<EFBFBD>rateur SQL <a
href="../doxygen/html/classqx_1_1dao_1_1detail_1_1_qx_sql_generator___postgre_s_q_l.html"
target="_blank">qx::dao::detail::QxSqlGenerator_PostgreSQL</a> ;</li>
<li>pilote <i>QSQLITE</i> : g<>n<EFBFBD>rateur SQL <a
href="../doxygen/html/classqx_1_1dao_1_1detail_1_1_qx_sql_generator___s_q_lite.html"
target="_blank">qx::dao::detail::QxSqlGenerator_SQLite</a> ;</li>
<li>pilote <i>QOCI</i> : g<>n<EFBFBD>rateur SQL <a
href="../doxygen/html/classqx_1_1dao_1_1detail_1_1_qx_sql_generator___oracle.html"
target="_blank">qx::dao::detail::QxSqlGenerator_Oracle</a> ;</li>
<li>pour tous les autres pilotes : g<>n<EFBFBD>rateur SQL <a
href="../doxygen/html/classqx_1_1dao_1_1detail_1_1_qx_sql_generator___standard.html"
target="_blank">qx::dao::detail::QxSqlGenerator_Standard</a> (il est possible de cr<63>er
son propre g<>n<EFBFBD>rateur SQL en <20>crivant une classe h<>ritant de <a
href="../doxygen/html/classqx_1_1dao_1_1detail_1_1_ix_sql_generator.html"
target="_blank">qx::dao::detail::IxSqlGenerator</a>) ;</li>
<li>pour se connecter <20> une base de donn<6E>es <i>Microsoft SQL Server</i>, on utilise le pilote
<i>QODBC</i> : il est alors n<>cessaire de pr<70>ciser le g<>n<EFBFBD>rateur SQL <20> utiliser en
appelant la fonction :
</li>
</ul>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="SQL generator">
<pre> qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>IxSqlGenerator_ptr pSqlGenerator<span class="operator">;</span>
pSqlGenerator<span class="operator">.</span>reset<span class="operator">(</span><span class="keyword">new</span> qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>QxSqlGenerator_MSSQLServer<span class="operator">());</span>
qx<span class="operator">::</span>QxSqlDatabase<span class="operator">::</span>getSingleton<span class="operator">()-&gt;</span>setSqlGenerator<span class="operator">(</span>pSqlGenerator<span class="operator">);</span> </pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_320">Sauvegarder une instance
C++ en base de donn<6E>es (insert/update)</a></p>
<div class="manual_div_content">
Toutes les fonctions li<6C>es <20> la base de donn<6E>es sont disponibles dans <a
href="../doxygen/html/namespaceqx_1_1dao.html" target="_blank">l'espace de nom
qx::dao</a>.<br>
<br>
Pour sauvegarder une instance C++ (ou une liste d'instances C++) en base de donn<6E>es, la
biblioth<74>que QxOrm fournit les fonctions suivantes :
<ul>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::insert</i></a> : ins<6E>re une instance (ou une liste
d'instances) en base de donn<6E>es ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::insert_with_relation</i></a> : ins<6E>re une instance (ou une
liste d'instances) + ses relations en base de donn<6E>es ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::insert_with_all_relation</i></a> : ins<6E>re une instance (ou
une liste d'instances) + toutes ses relations en base de donn<6E>es ;</li>
<br>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::update</i></a> : met <20> jour une instance (ou une liste
d'instances) en base de donn<6E>es ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::update_with_relation</i></a> : met <20> jour une instance (ou
une liste d'instances) + ses relations en base de donn<6E>es ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::update_with_all_relation</i></a> : met <20> jour une instance
(ou une liste d'instances) + toutes ses relations en base de donn<6E>es ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::update_by_query</i></a> : met <20> jour une instance (ou une
liste d'instances) en base de donn<6E>es en filtrant avec une requ<71>te SQL ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::update_by_query_with_relation</i></a> : met <20> jour une
instance (ou une liste d'instances) + ses relations en base de donn<6E>es en filtrant avec
une requ<71>te SQL ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::update_by_query_with_all_relation</i></a> : met <20> jour une
instance (ou une liste d'instances) + toutes ses relations en base de donn<6E>es en filtrant
avec une requ<71>te SQL ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::update_optimized</i></a> : met <20> jour uniquement les champs
modifi<66>s d'une instance (ou d'une liste d'instances) en base de donn<6E>es en utilisant le
pattern <i>is dirty</i> et les fonctionnalit<69>s de la classe <a
href="#manual_4030">qx::dao::ptr</a> ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::update_optimized_by_query</i></a> : met <20> jour uniquement
les champs modifi<66>s d'une instance (ou d'une liste d'instances) en base de donn<6E>es en
utilisant le pattern <i>is dirty</i> et les fonctionnalit<69>s de la classe <a
href="#manual_4030">qx::dao::ptr</a> et en filtrant avec une requ<71>te SQL ;</li>
<br>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::save</i></a> : ins<6E>re (si l'<27>l<EFBFBD>ment n'existe pas en base de
donn<6E>es) ou met <20> jour (si l'<27>l<EFBFBD>ment existe d<>j<EFBFBD> en base de donn<6E>es) ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::save_with_relation</i></a> : ins<6E>re (si l'<27>l<EFBFBD>ment n'existe
pas en base de donn<6E>es) ou met <20> jour (si l'<27>l<EFBFBD>ment existe d<>j<EFBFBD> en base de donn<6E>es) + ses
relations ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::save_with_all_relation</i></a> : ins<6E>re (si l'<27>l<EFBFBD>ment
n'existe pas en base de donn<6E>es) ou met <20> jour (si l'<27>l<EFBFBD>ment existe d<>j<EFBFBD> en base de
donn<6E>es) + toutes ses relations ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::save_with_relation_recursive</i></a> : ins<6E>re (si l'<27>l<EFBFBD>ment
n'existe pas en base de donn<6E>es) ou met <20> jour (si l'<27>l<EFBFBD>ment existe d<>j<EFBFBD> en base de
donn<6E>es) + toutes les relations sur tous les niveaux : utile pour sauvegarder en 1
commande une structure en arbre par exemple.</li>
</ul>
<br>
<b>Par exemple :</b><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="insert/update">
<pre> <span class="comment">// Create 3 drugs instances
// It is possible to use 'boost' and 'Qt' smart pointer : 'boost::shared_ptr', 'QSharedPointer', etc...
</span><span class="keyword"> typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>drug<span class="operator">&gt;</span> drug_ptr<span class="operator">;</span>
drug_ptr d1<span class="operator">;</span> d1<span class="operator">.</span>reset<span class="operator">(</span><span class="keyword">new</span> drug<span class="operator">());</span> d1<span class="operator">-&gt;</span>name<span class="operator"> =</span><span class="string"> "name1"</span><span class="operator">;</span> d1<span class="operator">-&gt;</span>description<span class="operator"> =</span><span class="string"> "desc1"</span><span class="operator">;</span>
drug_ptr d2<span class="operator">;</span> d2<span class="operator">.</span>reset<span class="operator">(</span><span class="keyword">new</span> drug<span class="operator">());</span> d2<span class="operator">-&gt;</span>name<span class="operator"> =</span><span class="string"> "name2"</span><span class="operator">;</span> d2<span class="operator">-&gt;</span>description<span class="operator"> =</span><span class="string"> "desc2"</span><span class="operator">;</span>
drug_ptr d3<span class="operator">;</span> d3<span class="operator">.</span>reset<span class="operator">(</span><span class="keyword">new</span> drug<span class="operator">());</span> d3<span class="operator">-&gt;</span>name<span class="operator"> =</span><span class="string"> "name3"</span><span class="operator">;</span> d3<span class="operator">-&gt;</span>description<span class="operator"> =</span><span class="string"> "desc3"</span><span class="operator">;</span>
<span class="comment">// Insert some drugs into a container
// It is possible to use many containers from 'std', 'boost', 'Qt' and 'qx::QxCollection&lt;Key, Value&gt;'
</span><span class="keyword"> typedef</span> std<span class="operator">::</span>vector<span class="operator">&lt;</span>drug_ptr<span class="operator">&gt;</span> type_lst_drug<span class="operator">;</span>
type_lst_drug lst_drug<span class="operator">;</span>
lst_drug<span class="operator">.</span>push_back<span class="operator">(</span>d1<span class="operator">);</span>
lst_drug<span class="operator">.</span>push_back<span class="operator">(</span>d2<span class="operator">);</span>
lst_drug<span class="operator">.</span>push_back<span class="operator">(</span>d3<span class="operator">);</span><span class="comment">
// Insert drugs from container to database
// 'id' property of 'd1', 'd2' and 'd3' are auto-updated
</span> QSqlError daoError<span class="operator"> =</span> <font style="background-color:yellow">qx<span class="operator">::</span>dao<span class="operator">::</span>insert<span class="operator">(</span>lst_drug<span class="operator">);</span></font><span class="comment">
// Modify and update the second drug into database
</span> d2<span class="operator">-&gt;</span>name<span class="operator"> =</span><span class="string"> "name2 modified"</span><span class="operator">;</span>
d2<span class="operator">-&gt;</span>description<span class="operator"> =</span><span class="string"> "desc2 modified"</span><span class="operator">;</span>
daoError<span class="operator"> =</span> <font style="background-color:yellow">qx<span class="operator">::</span>dao<span class="operator">::</span>update<span class="operator">(</span>d2<span class="operator">);</span></font>
</pre>
</td>
</tr>
</tbody>
</table>
<br><br>
<b>Remarque :</b> toutes les fonctions de <a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank">l'espace de nom qx::dao</a> sont flexibles au niveau des param<61>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 :
<ul>
<li><i>my_entity t; &nbsp;&nbsp;&nbsp;&nbsp;/* ... */&nbsp;&nbsp;&nbsp;&nbsp;
qx::dao::insert(t);</i></li>
<li><i>my_entity * t; &nbsp;&nbsp;&nbsp;&nbsp;/* ... */&nbsp;&nbsp;&nbsp;&nbsp;
qx::dao::insert(t);</i></li>
<li><i>std::shared_ptr&lt;my_entity&gt; t; &nbsp;&nbsp;&nbsp;&nbsp;/* ...
*/&nbsp;&nbsp;&nbsp;&nbsp; qx::dao::insert(t);</i></li>
<li><i>QList&lt;my_entity&gt; lst; &nbsp;&nbsp;&nbsp;&nbsp;/* ... */&nbsp;&nbsp;&nbsp;&nbsp;
qx::dao::insert(lst);</i></li>
<li><i>QList&lt;std::shared_ptr&lt;my_entity&gt; &gt; lst; &nbsp;&nbsp;&nbsp;&nbsp;/* ...
*/&nbsp;&nbsp;&nbsp;&nbsp; qx::dao::insert(lst);</i></li>
</ul>
Pour connaitre la liste des collections support<72>es, rendez-vous dans le chapitre : <a
href="#manual_390">Collections support<72>es par QxOrm</a>.<br>
Pour connaitre la liste des pointeurs intelligents support<72>s, rendez-vous dans le chapitre : <a
href="#manual_400">Pointeurs intelligents support<72>s par QxOrm (smart-pointers)</a>.<br>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_340">Supprimer une instance
C++ de la base de donn<6E>es (delete)</a></p>
<div class="manual_div_content">
Toutes les fonctions li<6C>es <20> la base de donn<6E>es sont disponibles dans <a
href="../doxygen/html/namespaceqx_1_1dao.html" target="_blank">l'espace de nom
qx::dao</a>.<br>
<br>
Pour supprimer une instance C++ (ou une liste d'instances C++) en base de donn<6E>es, la
biblioth<74>que QxOrm fournit les fonctions suivantes :
<ul>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::delete_by_id</i></a> : supprime de la base de donn<6E>es
l'<27>l<EFBFBD>ment (ou une liste d'<27>l<EFBFBD>ments) associ<63> <20> l'id pass<73> en param<61>tre ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::delete_all</i></a> : supprime toutes les entr<74>es d'une
table de la base de donn<6E>es ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::delete_by_query</i></a> : supprime les entr<74>es d'une table
de la base de donn<6E>es en fonction d'une requ<71>te SQL ;</li>
<br>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::destroy_by_id</i></a> : supprime de la base de donn<6E>es
l'<27>l<EFBFBD>ment (ou une liste d'<27>l<EFBFBD>ments) associ<63> <20> l'id pass<73> en param<61>tre avec prise en compte
de <a href="#manual_3400">la fonctionnalit<69> de suppression logique</a> ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::destroy_all</i></a> : supprime toutes les entr<74>es d'une
table de la base de donn<6E>es avec prise en compte de <a href="#manual_3400">la
fonctionnalit<69> de suppression logique</a> ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::destroy_by_query</i></a> : supprime les entr<74>es d'une table
de la base de donn<6E>es en fonction d'une requ<71>te SQL avec prise en compte de <a
href="#manual_3400">la fonctionnalit<69> de suppression logique</a> ;</li>
</ul>
<br>
<b>Par exemple :</b><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="delete">
<pre> <span class="comment">// Create a drug instance with id '18'
</span>drug d<span class="operator">;</span> d<span class="operator">.</span>setId<span class="operator">(</span><span class="int">18</span><span class="operator">);</span><span class="comment">
// Delete the drug with id '18' from database
</span>QSqlError daoError<span class="operator"> =</span> <font style="background-color:yellow">qx<span class="operator">::</span>dao<span class="operator">::</span>delete_by_id<span class="operator">(</span>d<span class="operator">);</span></font><span class="comment">
// Delete all drugs from database
</span>daoError<span class="operator"> =</span> <font style="background-color:yellow">qx<span class="operator">::</span>dao<span class="operator">::</span>delete_all<span class="operator">&lt;</span>drug<span class="operator">&gt;();</span></font></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3400">Suppression logique
(soft delete)</a></p>
<div class="manual_div_content">
Une suppression logique permet de ne pas effacer de ligne dans une table d'une base de
donn<6E>es (contrairement <20> une suppression physique) : une colonne suppl<70>mentaire est ajout<75>e <20>
la d<>finition de la table pour indiquer que la ligne est supprim<69>e ou non.<br>
Cette colonne peut contenir soit un bool<6F>en (1 signifie ligne supprim<69>e, 0 ou vide signifie
ligne non supprim<69>e), soit la date-heure de suppression de la ligne (si vide, la ligne est
consid<69>r<EFBFBD>e comme non supprim<69>e).<br>
Il est donc <20> tout moment possible de r<>activer une ligne supprim<69>e en r<>initialisant la
valeur <20> vide dans la table de la base de donn<6E>es.<br>
<br>
Pour activer le m<>canisme de suppression logique avec la biblioth<74>que QxOrm, il faut utiliser
la classe <a href="../doxygen/html/classqx_1_1_qx_soft_delete.html"
target="_blank"><b>qx::QxSoftDelete</b></a> dans la fonction de mapping
<i>qx::register_class&lt;T&gt;</i>.<br>
Voici un exemple d'utilisation avec une classe <i>Bar</i> contenant deux propri<72>t<EFBFBD>s
<i>m_id</i> et <i>m_desc</i> :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>Bar<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span>
<font style="background-color:yellow">t<span class="operator">.</span>setSoftDelete<span class="operator">(</span>qx<span class="operator">::</span>QxSoftDelete<span class="operator">(</span><span class="string">"deleted_at"</span><span class="operator">));</span></font>
t<span class="operator">.</span>id<span class="operator">(&amp;</span> Bar<span class="operator">::</span>m_id<span class="operator">,</span><span class="string"> "id"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> Bar<span class="operator">::</span>m_desc<span class="operator">,</span><span class="string"> "desc"</span><span class="operator">);
}}</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Les requ<71>tes SQL g<>n<EFBFBD>r<EFBFBD>es automatiquement par la biblioth<74>que QxOrm vont prendre en compte ce
param<61>tre de suppression logique pour ajouter les conditions n<>cessaires (ne pas r<>cup<75>rer
les <20>l<EFBFBD>ments supprim<69>s, ne pas supprimer physiquement une ligne, etc.).<br>
Par exemple, si vous ex<65>cutez les lignes suivantes avec la classe <i>Bar</i> :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>Bar_ptr pBar<span class="operator">;</span> pBar<span class="operator">.</span>reset<span class="operator">(</span><span class="keyword">new</span> Bar<span class="operator">());</span>
pBar<span class="operator">-&gt;</span>setId<span class="operator">(</span><span class="int">5</span><span class="operator">);</span>
QSqlError daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>delete_by_id<span class="operator">(</span>pBar<span class="operator">);</span> qAssert<span class="operator">(!</span> daoError<span class="operator">.</span>isValid<span class="operator">());</span>
qx_bool bDaoExist<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>exist<span class="operator">(</span>pBar<span class="operator">);</span> qAssert<span class="operator">(!</span> bDaoExist<span class="operator">);</span>
daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>delete_all<span class="operator">&lt;</span>Bar<span class="operator">&gt;();</span> qAssert<span class="operator">(!</span> daoError<span class="operator">.</span>isValid<span class="operator">());</span><span class="type">
long</span> lBarCount<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>count<span class="operator">&lt;</span>Bar<span class="operator">&gt;();</span> qAssert<span class="operator">(</span>lBarCount<span class="operator"> ==</span><span class="int"> 0</span><span class="operator">);</span>
daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>destroy_all<span class="operator">&lt;</span>Bar<span class="operator">&gt;();</span> qAssert<span class="operator">(!</span> daoError<span class="operator">.</span>isValid<span class="operator">());</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Vous obtiendrez les traces suivantes :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="operator">[</span>QxOrm<span class="operator">]</span> sql query<span class="operator"> (</span><span class="int">93</span> ms<span class="operator">) :</span> UPDATE Bar SET deleted_at<span class="operator"> =</span><span class="char"> '20110617115148615'</span> WHERE id<span class="operator"> = :</span>id<span class="operator">
[</span>QxOrm<span class="operator">]</span> sql query<span class="operator"> (</span><span class="int">0</span> ms<span class="operator">) :</span> SELECT Bar<span class="operator">.</span>id AS Bar_id_0<span class="operator">,</span> Bar<span class="operator">.</span>deleted_at FROM Bar WHERE Bar<span class="operator">.</span>id<span class="operator"> = :</span>id
AND<span class="operator"> (</span>Bar<span class="operator">.</span>deleted_at IS NULL OR Bar<span class="operator">.</span>deleted_at<span class="operator"> =</span><span class="char"> ''</span><span class="operator">)
[</span>QxOrm<span class="operator">]</span> sql query<span class="operator"> (</span><span class="int">78</span> ms<span class="operator">) :</span> UPDATE Bar SET deleted_at<span class="operator"> =</span><span class="char"> '20110617115148724'</span><span class="operator">
[</span>QxOrm<span class="operator">]</span> sql query<span class="operator"> (</span><span class="int">0</span> ms<span class="operator">) :</span> SELECT COUNT<span class="operator">(*)</span> FROM Bar WHERE<span class="operator"> (</span>Bar<span class="operator">.</span>deleted_at IS NULL OR Bar<span class="operator">.</span>deleted_at<span class="operator"> =</span><span class="char"> ''</span><span class="operator">)
[</span>QxOrm<span class="operator">]</span> sql query<span class="operator"> (</span><span class="int">110</span> ms<span class="operator">) :</span> DELETE FROM Bar</pre>
</td>
</tr>
</tbody>
</table>
<br>
<b>Remarque :</b> pour supprimer physiquement une ligne de la base de donn<6E>es, il faut
utiliser les fonctions : <i>qx::dao::destroy_by_id()</i> et
<i>qx::dao::destroy_all()</i>.<br>
<br>
<b>Autre remarque :</b> il peut <20>tre int<6E>ressant de d<>finir au niveau du SGBD un index sur la
colonne <i>deleted_at</i> (ou peu importe le nom que vous donnez) afin d'acc<63>l<EFBFBD>rer
l'ex<65>cution des requ<71>tes SQL.<br>
<br>
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_350">R<EFBFBD>cup<EFBFBD>rer une instance
C++ de la base de donn<6E>es (fetch)</a></p>
<div class="manual_div_content">
Toutes les fonctions li<6C>es <20> la base de donn<6E>es sont disponibles dans <a
href="../doxygen/html/namespaceqx_1_1dao.html" target="_blank">l'espace de nom
qx::dao</a>.<br>
<br>
Pour valoriser automatiquement les propri<72>t<EFBFBD>s d'une instance C++ (ou d'une liste d'instances
C++) en fonction des donn<6E>es d'une table (ou plusieurs tables si des relations sont d<>finies) de
la base de donn<6E>es, la biblioth<74>que QxOrm fournit les fonctions suivantes :
<ul>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::fetch_by_id</i></a> : r<>cup<75>re de la base de donn<6E>es
l'<27>l<EFBFBD>ment (ou une liste d'<27>l<EFBFBD>ments) associ<63> <20> l'id pass<73> en param<61>tre ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::fetch_by_id_with_relation</i></a> : r<>cup<75>re de la base de
donn<6E>es l'<27>l<EFBFBD>ment (ou une liste d'<27>l<EFBFBD>ments) + ses relations en fonction de l'id pass<73> en
param<61>tre ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::fetch_by_id_with_all_relation</i></a> : r<>cup<75>re de la base
de donn<6E>es l'<27>l<EFBFBD>ment (ou une liste d'<27>l<EFBFBD>ments) + toutes ses relations en fonction de l'id
pass<73> en param<61>tre ;</li>
<br>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::fetch_all</i></a> : r<>cup<75>re toutes les entr<74>es d'une table
de la base de donn<6E>es ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::fetch_all_with_relation</i></a> : r<>cup<75>re toutes les
entr<74>es d'une table + ses relations de la base de donn<6E>es ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::fetch_all_with_all_relation</i></a> : r<>cup<75>re toutes les
entr<74>es d'une table + toutes ses relations de la base de donn<6E>es ;</li>
<br>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::fetch_by_query</i></a> : r<>cup<75>re toutes les entr<74>es d'une
table de la base de donn<6E>es en fonction d'une requ<71>te SQL ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::fetch_by_query_with_relation</i></a> : r<>cup<75>re toutes les
entr<74>es d'une table de la base de donn<6E>es + ses relations en fonction d'une requ<71>te SQL ;
</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::fetch_by_query_with_all_relation</i></a> : r<>cup<75>re toutes
les entr<74>es d'une table de la base de donn<6E>es + toutes ses relations en fonction d'une
requ<71>te SQL ;</li>
<br>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank"><i>qx::dao::exist</i></a> : teste l'existence d'un <20>l<EFBFBD>ment (ou d'une
liste d'<27>l<EFBFBD>ments) en base de donn<6E>es en fonction de son identifiant (<i>primary key</i>).
</li>
</ul>
<br>
<b>Par exemple :</b><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="fetch">
<pre><span class="comment"> // Fetch drug with id '3' into a new variable
</span> drug_ptr d<span class="operator">;</span> d<span class="operator">.</span>reset<span class="operator">(</span><span class="keyword">new</span> drug<span class="operator">());</span>
d<span class="operator">-&gt;</span>id<span class="operator"> =</span><span class="int"> 3</span><span class="operator">;</span>
QSqlError daoError<span class="operator"> =</span> <font style="background-color:yellow">qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_id<span class="operator">(</span>d<span class="operator">);</span></font></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_360">Requ<EFBFBD>tes SQL</a></p>
<div class="manual_div_content">
La biblioth<74>que QxOrm fournit plusieurs outils pour effectuer des requ<71>tes <20> la base de donn<6E>es
:
<ul>
<li>la classe <a href="#manual_3600">qx::QxSqlQuery (ou son alias qx_query)</a> ;</li>
<li>la fonction <a href="#manual_3610">qx::dao::execute_query<T>()</a> ;</li>
<li>la fonction <a href="#manual_3610">qx::dao::call_query()</a>.</li>
</ul>
<b>Remarque :</b> QxOrm <20>tant bas<61> sur le module <a
href="http://doc.qt.io/qt-5/sql-programming.html" target="_blank">QtSql</a> de Qt, il est
toujours possible de requ<71>ter la base de donn<6E>es en utilisant la classe <a
href="http://doc.qt.io/qt-5/qsqlquery.html" target="_blank">QSqlQuery</a> de Qt si les
fonctionnalit<69>s propos<6F>es par QxOrm ne sont pas suffisantes.
<br><br>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3600">Utilisation de la
classe qx::QxSqlQuery (ou son alias qx_query)</a></p>
<div class="manual_div_content">
La classe <i><a href="../doxygen/html/classqx_1_1_qx_sql_query.html"
target="_blank">qx::QxSqlQuery</a></i> (ou bien son alias <i>qx_query</i>) permet
d'interroger la base de donn<6E>es (trier, filtrer, etc.) de deux mani<6E>res diff<66>rentes :
<ul>
<li>en <20>crivant directement la requ<71>te SQL ;</li>
<li>en utilisant des m<>thodes C++ avec une syntaxe proche du SQL (similaire <20> ce que
propose <a href="http://subsonicproject.com/docs/Simple_Query_Tool"
target="_blank">l'excellente biblioth<74>que SubSonic pour .Net</a>).</li>
</ul>
Le principal avantage de la premi<6D>re m<>thode (<28>criture manuelle des requ<71>tes SQL) est de
pouvoir utiliser certaines optimisations sp<73>cifiques <20> chaque base de donn<6E>es.<br>
La deuxi<78>me m<>thode (utilisation du code C++ pour g<>n<EFBFBD>rer la requ<71>te SQL) permet de mapper
automatiquement les param<61>tres SQL sans utiliser la fonction
<i>qx::QxSqlQuery::bind()</i>.<br>
<br>
Voici un exemple d'utilisation de la classe <i>qx::QxSqlQuery</i> avec <20>criture manuelle
d'une requ<71>te SQL :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Construit une requ<71>te pour r<>cup<75>rer uniquement les 'author' de type 'female'
</span>qx<span class="operator">::</span>QxSqlQuery query<span class="operator">(</span><span class="string">"WHERE author.sex = :sex"</span><span class="operator">);</span>
query<span class="operator">.</span>bind<span class="operator">(</span><span class="string">":sex"</span><span class="operator">,</span> author<span class="operator">::</span>female<span class="operator">);</span>
QList<span class="operator">&lt;</span>author<span class="operator">&gt;</span> list_of_female<span class="operator">;</span>
QSqlError daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_query<span class="operator">(</span>query<span class="operator">,</span> list_of_female<span class="operator">);</span><span class="flow">
for</span><span class="operator"> (</span><span class="type">long</span> l<span class="operator"> =</span><span class="int"> 0</span><span class="operator">;</span> l<span class="operator"> &lt;</span> list_of_female<span class="operator">.</span>count<span class="operator">();</span> l<span class="operator">++)
{</span><span class="comment"> /* traitement avec la collection issue de la base de donn<6E>es */</span><span class="operator"> }</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
La biblioth<74>que QxOrm supporte trois syntaxes pour l'<27>criture des param<61>tres SQL.<br>
Le type de syntaxe peut <20>tre modifi<66> de fa<66>on globale <20> un projet en utilisant la m<>thode
suivante : <i>qx::QxSqlDatabase::getSingleton()->setSqlPlaceHolderStyle()</i>.<br>
Les trois param<61>tres possibles pour cette m<>thode sont :
<ul>
<li><i>ph_style_2_point_name</i> : "WHERE author.sex = :sex" (syntaxe par d<>faut) ;</li>
<li><i>ph_style_at_name</i> : "WHERE author.sex = @sex" ;</li>
<li><i>ph_style_question_mark</i> : "WHERE author.sex = ?".</li>
</ul>
Voici le m<>me exemple en utilisant les m<>thodes C++ de la classe <i>qx::QxSqlQuery</i> (ou
bien son alias <i>qx_query</i>) pour g<>n<EFBFBD>rer la requ<71>te automatiquement :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Construit une requ<71>te pour r<>cup<75>rer uniquement les 'author' de type 'female'
</span>qx_query query<span class="operator">;</span>
query<span class="operator">.</span>where<span class="operator">(</span><span class="string">"author.sex"</span><span class="operator">).</span>isEqualTo<span class="operator">(</span>author<span class="operator">::</span>female<span class="operator">);</span>
QList<span class="operator">&lt;</span>author<span class="operator">&gt;</span> list_of_female<span class="operator">;</span>
QSqlError daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_query<span class="operator">(</span>query<span class="operator">,</span> list_of_female<span class="operator">);</span><span class="flow">
for</span><span class="operator"> (</span><span class="type">long</span> l<span class="operator"> =</span><span class="int"> 0</span><span class="operator">;</span> l<span class="operator"> &lt;</span> list_of_female<span class="operator">.</span>count<span class="operator">();</span> l<span class="operator">++)
{</span><span class="comment"> /* traitement avec la collection issue de la base de donn<6E>es */</span><span class="operator"> }</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Cette utilisation de la classe <i>qx::QxSqlQuery</i> pr<70>sente l'avantage de ne pas avoir <20>
mapper les param<61>tres de la requ<71>te, tout en restant tr<74>s proche de l'<27>criture manuelle d'une
requ<71>te SQL.<br>
Les param<61>tres seront automatiquement inject<63>s en utilisant la syntaxe d<>finie de mani<6E>re
globale par la m<>thode :
<i>qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle()</i>.<br>
<br>
Voici un exemple pr<70>sentant diff<66>rentes m<>thodes disponibles avec la classe
<i>qx::QxSqlQuery</i> (ou bien son alias <i>qx_query</i>) :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>qx_query query<span class="operator">;</span>
query<span class="operator">.</span>where<span class="operator">(</span><span class="string">"sex"</span><span class="operator">).</span>isEqualTo<span class="operator">(</span>author<span class="operator">::</span>female<span class="operator">)
.</span>and_<span class="operator">(</span><span class="string">"age"</span><span class="operator">).</span>isGreaterThan<span class="operator">(</span><span class="int">38</span><span class="operator">)
.</span>or_<span class="operator">(</span><span class="string">"last_name"</span><span class="operator">).</span>isNotEqualTo<span class="operator">(</span><span class="string">"Dupont"</span><span class="operator">)
.</span>or_<span class="operator">(</span><span class="string">"first_name"</span><span class="operator">).</span>like<span class="operator">(</span><span class="string">"Alfred"</span><span class="operator">)
.</span>and_OpenParenthesis<span class="operator">(</span><span class="string">"id"</span><span class="operator">).</span>isLessThanOrEqualTo<span class="operator">(</span><span class="int">999</span><span class="operator">)
.</span>and_<span class="operator">(</span><span class="string">"birth_date"</span><span class="operator">).</span>isBetween<span class="operator">(</span>date1<span class="operator">,</span> date2<span class="operator">)
.</span>closeParenthesis<span class="operator">()
.</span>or_<span class="operator">(</span><span class="string">"id"</span><span class="operator">).</span>in<span class="operator">(</span><span class="int">50</span><span class="operator">,</span><span class="int"> 999</span><span class="operator">,</span><span class="int"> 11</span><span class="operator">,</span><span class="int"> 23</span><span class="operator">,</span><span class="int"> 78945</span><span class="operator">)
.</span>and_<span class="operator">(</span><span class="string">"is_deleted"</span><span class="operator">).</span>isNotNull<span class="operator">()
.</span>orderAsc<span class="operator">(</span><span class="string">"last_name"</span><span class="operator">,</span><span class="string"> "first_name"</span><span class="operator">,</span><span class="string"> "sex"</span><span class="operator">)
.</span>limit<span class="operator">(</span><span class="int">50</span><span class="operator">,</span><span class="int"> 150</span><span class="operator">);</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Ce qui produira le code SQL suivant pour les bases de donn<6E>es <i>MySQL</i>, <i>PostgreSQL</i>
et <i>SQLite</i> (pour <i>Oracle</i> et <i>SQLServer</i>, le traitement de la m<>thode
<i>limit()</i> est diff<66>rent) :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>WHERE sex<span class="operator"> = :</span>sex_1_0
AND age<span class="operator"> &gt; :</span>age_3_0
OR last_name<span class="operator"> &lt;&gt; :</span>last_name_5_0
OR first_name LIKE<span class="operator"> :</span>first_name_7_0
AND<span class="operator"> (</span> id<span class="operator"> &lt;= :</span>id_10_0 AND birth_date BETWEEN<span class="operator"> :</span>birth_date_12_0_1 AND<span class="operator"> :</span>birth_date_12_0_2<span class="operator"> )</span>
OR id IN<span class="operator"> (:</span>id_15_0_0<span class="operator">, :</span>id_15_0_1<span class="operator">, :</span>id_15_0_2<span class="operator">, :</span>id_15_0_3<span class="operator">, :</span>id_15_0_4<span class="operator">)</span>
AND is_deleted IS NOT NULL
ORDER BY last_name ASC<span class="operator">,</span> first_name ASC<span class="operator">,</span> sex ASC
LIMIT<span class="operator"> :</span>limit_rows_count_19_0 OFFSET<span class="operator"> :</span>offset_start_row_19_0</pre>
</td>
</tr>
</tbody>
</table>
<br>
Voici la liste des fonctions et m<>thodes disponibles pour utiliser la classe
<i>qx::QxSqlQuery</i> (ou bien son alias <i>qx_query</i>) :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// avec les fonctions du namespace qx::dao
</span>qx<span class="operator">::</span>dao<span class="operator">::</span>count<span class="operator">&lt;</span>T<span class="operator">&gt;()</span>
qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_query<span class="operator">&lt;</span>T<span class="operator">&gt;()</span>
qx<span class="operator">::</span>dao<span class="operator">::</span>update_by_query<span class="operator">&lt;</span>T<span class="operator">&gt;()</span>
qx<span class="operator">::</span>dao<span class="operator">::</span>delete_by_query<span class="operator">&lt;</span>T<span class="operator">&gt;()</span>
qx<span class="operator">::</span>dao<span class="operator">::</span>destroy_by_query<span class="operator">&lt;</span>T<span class="operator">&gt;()</span>
qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_query_with_relation<span class="operator">&lt;</span>T<span class="operator">&gt;()</span>
qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_query_with_all_relation<span class="operator">&lt;</span>T<span class="operator">&gt;()</span>
qx<span class="operator">::</span>dao<span class="operator">::</span>update_by_query_with_relation<span class="operator">&lt;</span>T<span class="operator">&gt;()</span>
qx<span class="operator">::</span>dao<span class="operator">::</span>update_by_query_with_all_relation<span class="operator">&lt;</span>T<span class="operator">&gt;()</span>
qx<span class="operator">::</span>dao<span class="operator">::</span>update_optimized_by_query<span class="operator">&lt;</span>T<span class="operator">&gt;()</span><span class="comment">
// avec la classe qx::QxSession
</span>qx<span class="operator">::</span>QxSession<span class="operator">::</span>count<span class="operator">&lt;</span>T<span class="operator">&gt;()</span>
qx<span class="operator">::</span>QxSession<span class="operator">::</span>fetchByQuery<span class="operator">&lt;</span>T<span class="operator">&gt;()</span>
qx<span class="operator">::</span>QxSession<span class="operator">::</span>update<span class="operator">&lt;</span>T<span class="operator">&gt;()</span>
qx<span class="operator">::</span>QxSession<span class="operator">::</span>deleteByQuery<span class="operator">&lt;</span>T<span class="operator">&gt;()</span>
qx<span class="operator">::</span>QxSession<span class="operator">::</span>destroyByQuery<span class="operator">&lt;</span>T<span class="operator">&gt;()</span><span class="comment">
// avec la classe qx::QxRepository&lt;T&gt;
</span>qx<span class="operator">::</span>QxRepository<span class="operator">&lt;</span>T<span class="operator">&gt;::</span>count<span class="operator">()</span>
qx<span class="operator">::</span>QxRepository<span class="operator">&lt;</span>T<span class="operator">&gt;::</span>fetchByQuery<span class="operator">()</span>
qx<span class="operator">::</span>QxRepository<span class="operator">&lt;</span>T<span class="operator">&gt;::</span>update<span class="operator">()</span>
qx<span class="operator">::</span>QxRepository<span class="operator">&lt;</span>T<span class="operator">&gt;::</span>deleteByQuery<span class="operator">()</span>
qx<span class="operator">::</span>QxRepository<span class="operator">&lt;</span>T<span class="operator">&gt;::</span>destroyByQuery<span class="operator">()</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<b>Remarque :</b> certaines de ces fonctions ont <20>galement deux autres param<61>tres optionnels
:
<ul>
<li><i>const QStringList & columns</i> : pour indiquer la liste des colonnes <20> r<>cup<75>rer
(par d<>faut, toutes les colonnes sont r<>cup<75>r<EFBFBD>es) ;</li>
<li><i>const QStringList & relation</i> : pour indiquer les jointures (<i>one-to-one</i>,
<i>one-to-many</i>, <i>many-to-one</i> et <i>many-to-many</i> d<>finies dans la fonction
de mapping <i>void qx::register_class&lt;T&gt;()</i>) entre les tables de la base de
donn<6E>es (par d<>faut, aucune relation).
</li>
</ul>
<br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3610">Appel de proc<6F>dure
stock<63>e ou requ<71>te SQL personnalis<69>e</a></p>
<div class="manual_div_content">
La biblioth<74>que QxOrm fournit deux fonctions pour appeler une proc<6F>dure stock<63>e ou une
requ<71>te SQL personnalis<69>e :
<ul>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank">qx::dao::execute_query&lt;T&gt;()</a></li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank">qx::dao::call_query()</a></li>
</ul>
Le premier param<61>tre de ces deux fonctions, de type <i><a
href="../doxygen/html/classqx_1_1_qx_sql_query.html"
target="_blank">qx::QxSqlQuery</a></i> (ou son alias <i>qx_query</i>), correspond <20> la
proc<6F>dure stock<63>e ou <20> la requ<71>te SQL personnalis<69>e.<br>
Pour plus d'informations sur la classe <i><a
href="../doxygen/html/classqx_1_1_qx_sql_query.html"
target="_blank">qx::QxSqlQuery</a></i>, rendez-vous sur ce chapitre du manuel
utilisateur : <a href="#manual_3600">Utilisation de la classe qx::QxSqlQuery (ou son alias
qx_query)</a>.<br>
<br>
La fonction <a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank">qx::dao::execute_query&lt;T&gt;()</a> est une fonction <i>template</i> :
le type T doit <20>tre enregistr<74> dans le contexte QxOrm (fonction
<i>qx::register_class&lt;T&gt;</i>).<br>
Toutes les donn<6E>es renvoy<6F>es par la proc<6F>dure stock<63>e ou la requ<71>te SQL personnalis<69>e qui
pourront <20>tre associ<63>es aux membres des classes C++ (de type T) seront valoris<69>es
automatiquement.<br>
Une recherche automatique est effectu<74>e sur le nom des champs associ<63>s aux donn<6E>es.<br>
Voici un exemple d'utilisation (disponible dans le projet qxBlog du package QxOrm) :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Call a custom SQL query or a stored procedure and fetch automatically properties (with a collection of items)
</span>qx_query testStoredProcBis<span class="operator">(</span><span class="string">"SELECT * FROM author"</span><span class="operator">);</span>
daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>execute_query<span class="operator">(</span>testStoredProcBis<span class="operator">,</span> authorX<span class="operator">);</span>
qAssert<span class="operator">(!</span> daoError<span class="operator">.</span>isValid<span class="operator">());</span> qAssert<span class="operator">(</span>authorX<span class="operator">.</span>count<span class="operator">() &gt;</span><span class="int"> 0</span><span class="operator">);</span>
qx<span class="operator">::</span>dump<span class="operator">(</span>authorX<span class="operator">);</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
La fonction <a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank">qx::dao::call_query()</a> n'est pas une fonction <i>template</i> : les
r<>sultats de la requ<71>te doivent <20>tre parcourus manuellement sur la classe <i><a
href="../doxygen/html/classqx_1_1_qx_sql_query.html"
target="_blank">qx::QxSqlQuery</a></i> (ou <i>qx_query</i>).<br>
Pour r<>cup<75>rer un param<61>tre de sortie (qui doit <20>tre pass<73> <20> la requ<71>te en tant que
<i>QSql::Out</i> ou <i>QSql::InOut</i>), il suffit d'utiliser la m<>thode : <i>QVariant
qx::QxSqlQuery::boundValue(const QString & sKey) const;</i>.<br>
<br>
Pour parcourir la liste des r<>sultats de la requ<71>te, il faut utiliser les m<>thodes suivantes
:
<ul>
<li><i>long qx::QxSqlQuery::getSqlResultRowCount() const;</i></li>
<li><i>long qx::QxSqlQuery::getSqlResultColumnCount() const;</i></li>
<li><i>QVariant qx::QxSqlQuery::getSqlResultAt(long row, long column) const;</i></li>
<li><i>QVariant qx::QxSqlQuery::getSqlResultAt(long row, const QString & column)
const;</i></li>
<li><i>QVector qx::QxSqlQuery::getSqlResultAllColumns() const;</i></li>
<li><i>void qx::QxSqlQuery::dumpSqlResult();</i></li>
</ul>
Voici un exemple d'utilisation avec la fonction <a
href="../doxygen/html/namespaceqx_1_1dao.html" target="_blank">qx::dao::call_query()</a>
:<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>qx_query query<span class="operator">(</span><span class="string">"CALL MyStoredProc(:param1, :param2)"</span><span class="operator">);</span>
query<span class="operator">.</span>bind<span class="operator">(</span><span class="string">":param1"</span><span class="operator">,</span><span class="string"> "myValue1"</span><span class="operator">);</span>
query<span class="operator">.</span>bind<span class="operator">(</span><span class="string">":param2"</span><span class="operator">,</span><span class="int"> 5024</span><span class="operator">,</span> QSql<span class="operator">::</span>InOut<span class="operator">);</span>
QSqlError daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>call_query<span class="operator">(</span>query<span class="operator">);</span>
QVariant vNewValue<span class="operator"> =</span> query<span class="operator">.</span>boundValue<span class="operator">(</span><span class="string">":param2"</span><span class="operator">);</span>
query<span class="operator">.</span>dumpSqlResult<span class="operator">();</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_370">Transactions (commit,
rollback, session)</a></p>
<div class="manual_div_content">
Une <a href="http://knol.google.com/k/les-transactions-base-de-donn<6E>es"
target="_blank"><b>transaction</b></a> est une suite d'op<6F>rations effectu<74>es comme une seule
unit<69> logique de travail.<br>
Une fois termin<69>e, la transaction est :
<ul>
<li>soit valid<69>e (<b>commit</b>), alors toutes les modifications sont faites dans la base de
donn<6E>es ;</li>
<li>soit annul<75>e (<b>rollback</b>), alors toutes les modifications ne sont pas enregistr<74>e.
</li>
</ul>
La classe <b><a href="../doxygen/html/classqx_1_1_qx_session.html"
target="_blank">qx::QxSession</a></b> de la biblioth<74>que QxOrm permet de g<>rer
automatiquement les transactions (validation, annulation) en utilisant le m<>canisme <a
href="http://fr.wikipedia.org/wiki/RAII" target="_blank">C++ RAII</a> :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="operator">{</span><span class="comment"> // Ouverture d'un scope o<> une session sera instanci<63>e
// Cr<43>ation d'une session : une connexion valide <20> la BDD est assign<67>e <20> la session et une transaction est d<>marr<72>e
</span>qx<span class="operator">::</span>QxSession session<span class="operator">;</span><span class="comment">
// Ex<45>cution d'une s<>rie d'op<6F>rations avec la BDD (en utilisant l'op<6F>rateur += de la classe qx::QxSession et la connexion de la session)
</span>session<span class="operator"> +=</span> qx<span class="operator">::</span>dao<span class="operator">::</span>insert<span class="operator">(</span>my_object<span class="operator">,</span> session<span class="operator">.</span>database<span class="operator">());</span>
session<span class="operator"> +=</span> qx<span class="operator">::</span>dao<span class="operator">::</span>update<span class="operator">(</span>my_object<span class="operator">,</span> session<span class="operator">.</span>database<span class="operator">());</span>
session<span class="operator"> +=</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_id<span class="operator">(</span>my_object<span class="operator">,</span> session<span class="operator">.</span>database<span class="operator">());</span>
session<span class="operator"> +=</span> qx<span class="operator">::</span>dao<span class="operator">::</span>delete_by_id<span class="operator">(</span>my_object<span class="operator">,</span> session<span class="operator">.</span>database<span class="operator">());</span><span class="comment">
// Si la session n'est pas valide (donc une erreur s'est produite) =&gt; affichage de la 1<>re erreur de la session
</span><span class="flow">if</span><span class="operator"> (!</span> session<span class="operator">.</span>isValid<span class="operator">()) {</span> qDebug<span class="operator">(</span><span class="string">"[QxOrm] session error : '%s'"</span><span class="operator">,</span> qPrintable<span class="operator">(</span>session<span class="operator">.</span>firstError<span class="operator">().</span>text<span class="operator">())); }
}</span><span class="comment"> // Fermeture du scope : la session est d<>truite (transaction =&gt; commit ou rollback automatique)</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<b>Remarque :</b> une session peut d<>clencher une exception de type <a
href="../doxygen/html/classqx_1_1dao_1_1sql__error.html"
target="_blank"><i>qx::dao::sql_error</i></a> lorsqu'une erreur se produit (par d<>faut,
aucune exception n'est d<>clench<63>e). Il est possible de param<61>trer ce comportement en utilisant :
<ul>
<li>soit le constructeur de la classe <i>qx::QxSession</i> (pour une session en particulier)
;</li>
<li>soit le param<61>tre du singleton
<i>qx::QxSqlDatabase::getSingleton()->setSessionThrowable(bool b)</i> (pour toutes les
sessions).
</li>
</ul>
<b>Autre remarque :</b> il est important de ne pas oublier de passer la connexion <20> la base de
donn<6E>es de la session <20> chaque fonction <i>qx::dao::xxx</i> (en utilisant la m<>thode
<i>session.database()</i>).<br>
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 <i>qx::QxSession</i>.<br>
<br>
La classe <i>qx::QxSession</i> propose <20>galement des m<>thodes de persistance (CRUD), ce qui peut
simplifier l'<27>criture du code C++ suivant les habitudes de programmation.<br>
Voici le m<>me exemple en utilisant les m<>thodes de la classe <i>qx::QxSession</i> <20> la place des
fonctions du <i>namespace</i> <i>qx::dao</i> :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="operator">{</span><span class="comment"> // Ouverture d'un scope o<> une session sera instanci<63>e
// Cr<43>ation d'une session : une connexion valide <20> la BDD est assign<67>e <20> la session et une transaction est d<>marr<72>e
</span> qx<span class="operator">::</span>QxSession session<span class="operator">;</span><span class="comment">
// Ex<45>cution d'une s<>rie d'op<6F>rations avec la BDD
</span> session<span class="operator">.</span>insert<span class="operator">(</span>my_object<span class="operator">);</span>
session<span class="operator">.</span>update<span class="operator">(</span>my_object<span class="operator">);</span>
session<span class="operator">.</span>fetchById<span class="operator">(</span>my_object<span class="operator">);</span>
session<span class="operator">.</span>deleteById<span class="operator">(</span>my_object<span class="operator">);</span><span class="comment">
// Si la session n'est pas valide (donc une erreur s'est produite) =&gt; affichage de la 1<>re erreur de la session
</span><span class="flow"> if</span><span class="operator"> (!</span> session<span class="operator">.</span>isValid<span class="operator">()) {</span> qDebug<span class="operator">(</span><span class="string">"[QxOrm] session error : '%s'"</span><span class="operator">,</span> qPrintable<span class="operator">(</span>session<span class="operator">.</span>firstError<span class="operator">().</span>text<span class="operator">())); }
}</span><span class="comment"> // Fermeture du scope : la session est d<>truite (transaction =&gt; commit ou rollback automatique)</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_380">Moteur de relations</a>
</p>
<div class="manual_div_content">
La biblioth<74>que QxOrm fournit un puissant moteur de relations permettant de d<>finir facilement :
<ul>
<li>des relations <a href="#manual_3800">one-to-many (1-n)</a> ;</li>
<li>des relations <a href="#manual_3810">many-to-one (n-1)</a> ;</li>
<li>des relations <a href="#manual_3820">many-to-many (n-n)</a> ;</li>
<li>des relations <a href="#manual_3830">one-to-one (1-1)</a> ;</li>
</ul>
<b>Remarque :</b> <a href="./tutorial.html">un tutoriel complet sur les relations bas<61> sur le
projet de test <i>qxBlog</i></a> (dont les sources sont pr<70>sentes dans le package QxOrm) est
disponible.
<br><br>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3800">one-to-many (1-n)</a>
</p>
<div class="manual_div_content">
Une relation <i>one-to-many (1-n)</i> est d<>finie par la m<>thode : <a
href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::relationOneToMany()</a>.
Cette m<>thode renvoie une instance de la classe <a
href="../doxygen/html/classqx_1_1_ix_sql_relation.html"
target="_blank">qx::IxSqlRelation</a> (classe de base pour toutes les relations) et
n<>cessite 3 param<61>tres :
<ul>
<li><i>V U::* pData</i> : r<>f<EFBFBD>rence vers la donn<6E>e membre de la classe ;</li>
<li><i>const QString & sKey</i> : cl<63> unique associ<63>e <20> la relation ;</li>
<li><i>const QString & sForeignKey</i> : cl<63> <20>trang<6E>re d<>finie dans la classe/table li<6C>e.
</li>
</ul>
<br>
<b>Par exemple : </b> prenons l'exemple d'un <i>author</i> (une personne) qui peut r<>diger
plusieurs <i>blog</i>
: nous allons ainsi montrer comment d<>finir une relation de type
<b><i>one-to-many</i></b>. <br>
Au niveau base de donn<6E>es, voici les deux tables qui correspondent : <br>
<br>
<img alt="qxBlog.table.author" src="./resource/qxBlog.table.author.jpg" width="318"
height="118"><br>
<br>
Fichier <i>author.h</i> :<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="author.h">
<pre><span class="pre">#ifndef _QX_BLOG_AUTHOR_H_
#define _QX_BLOG_AUTHOR_H_
</span><span class="keyword">
class</span> blog<span class="operator">;</span><span class="keyword">
class</span> QX_BLOG_DLL_EXPORT author<span class="operator">
{</span><span class="keyword">
public</span><span class="operator">:</span><span class="comment">
// -- typedef
</span><span class="keyword"> typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>blog<span class="operator">&gt;</span> blog_ptr<span class="operator">;</span><span class="keyword">
typedef</span> std<span class="operator">::</span>vector<span class="operator">&lt;</span>blog_ptr<span class="operator">&gt;</span> list_blog<span class="operator">;</span><span class="comment">
// -- enum
</span><span class="keyword"> enum</span> enum_sex<span class="operator"> {</span> male<span class="operator">,</span> female<span class="operator">,</span> unknown<span class="operator"> };</span><span class="comment">
// -- propri<72>t<EFBFBD>s
</span> QString m_id<span class="operator">;</span>
QString m_name<span class="operator">;</span>
QDate m_birthdate<span class="operator">;</span>
enum_sex m_sex<span class="operator">;</span>
<font style="background-color:yellow">list_blog m_blogX<span class="operator">;</span></font><span class="comment">
// -- constructeur, destructeur virtuel
</span> author<span class="operator">() :</span> m_id<span class="operator">(</span><span class="int">0</span><span class="operator">),</span> m_sex<span class="operator">(</span>unknown<span class="operator">) { ; }</span><span class="keyword">
virtual</span><span class="operator"> ~</span>author<span class="operator">() { ; }</span><span class="comment">
// -- m<>thodes
</span><span class="type"> int</span> age<span class="operator">()</span><span class="keyword"> const</span><span class="operator">;
};</span>
QX_REGISTER_PRIMARY_KEY<span class="operator">(</span>author<span class="operator">,</span> QString<span class="operator">)</span>
QX_REGISTER_HPP_QX_BLOG<span class="operator">(</span>author<span class="operator">,</span> qx<span class="operator">::</span>trait<span class="operator">::</span>no_base_class_defined<span class="operator">,</span><span class="int"> 0</span><span class="operator">)</span><span class="keyword">
typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>author<span class="operator">&gt;</span> author_ptr<span class="operator">;</span><span class="keyword">
typedef</span> qx<span class="operator">::</span>QxCollection<span class="operator">&lt;</span>QString<span class="operator">,</span> author_ptr<span class="operator">&gt;</span> list_author<span class="operator">;</span><span class="pre">
#endif <span class="comment">// _QX_BLOG_AUTHOR_H_</span>
</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Fichier <i>author.cpp</i> :<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="author.cpp">
<pre><span class="pre">#include <span class="string">"../include/precompiled.h"</span>
#include <span class="string">"../include/author.h"</span>
#include <span class="string">"../include/blog.h"</span>
#include <span class="string">&lt;QxOrm_Impl.h&gt;</span>
</span>
QX_REGISTER_CPP_QX_BLOG<span class="operator">(</span>author<span class="operator">)</span><span class="keyword">
namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>author<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span>
t<span class="operator">.</span>id<span class="operator">(&amp;</span> author<span class="operator">::</span>m_id<span class="operator">,</span><span class="string"> "author_id"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> author<span class="operator">::</span>m_name<span class="operator">,</span><span class="string"> "name"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> author<span class="operator">::</span>m_birthdate<span class="operator">,</span><span class="string"> "birthdate"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> author<span class="operator">::</span>m_sex<span class="operator">,</span><span class="string"> "sex"</span><span class="operator">);</span>
<font style="background-color:yellow">t<span class="operator">.</span>relationOneToMany<span class="operator">(&amp;</span> author<span class="operator">::</span>m_blogX<span class="operator">,</span><span class="string"> "list_blog"</span><span class="operator">,</span><span class="string"> "author_id"</span><span class="operator">);</span></font>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">int</span><span class="operator">&gt;(&amp;</span> author<span class="operator">::</span>age<span class="operator">,</span><span class="string"> "age"</span><span class="operator">);
}}</span><span class="type">
int</span> author<span class="operator">::</span>age<span class="operator">()</span><span class="keyword"> const</span><span class="operator">
{</span><span class="flow">
if</span><span class="operator"> (!</span> m_birthdate<span class="operator">.</span>isValid<span class="operator">()) {</span><span class="flow"> return</span><span class="operator"> -</span><span class="int">1</span><span class="operator">; }</span><span class="flow">
return</span><span class="operator"> (</span>QDate<span class="operator">::</span>currentDate<span class="operator">().</span>year<span class="operator">() -</span> m_birthdate<span class="operator">.</span>year<span class="operator">());
}</span>
</pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3810">many-to-one (n-1)</a>
</p>
<div class="manual_div_content">
Une relation <i>many-to-one (n-1)</i> est d<>finie par la m<>thode : <a
href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::relationManyToOne()</a>.
Cette m<>thode renvoie une instance de la classe <a
href="../doxygen/html/classqx_1_1_ix_sql_relation.html"
target="_blank">qx::IxSqlRelation</a> (classe de base pour toutes les relations) et
n<>cessite 2 param<61>tres :
<ul>
<li><i>V U::* pData</i> : r<>f<EFBFBD>rence vers la donn<6E>e membre de la classe ;</li>
<li><i>const QString & sKey</i> : cl<63> unique associ<63>e <20> la relation (correspond <20> une
colonne de la table dans la base de donn<6E>es).</li>
</ul>
<br>
<b>Par exemple : </b> un <i>comment</i> est associ<63> <20> un <i>blog</i> et un <i>blog</i> peut
contenir plusieurs <i>comment</i> : nous allons ainsi montrer comment
d<>finir une relation de type <b><i>many-to-one</i></b>. <br>
Au niveau base de donn<6E>es, voici les deux tables qui correspondent : <br>
<br>
<img alt="qxBlog.table.comment" src="./resource/qxBlog.table.comment.jpg" width="335"
height="116"><br>
<br>
Fichier <i>comment.h</i> :<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="comment.h">
<pre><span class="pre">#ifndef _QX_BLOG_COMMENT_H_
#define _QX_BLOG_COMMENT_H_
</span><span class="keyword">
class</span> blog<span class="operator">;</span><span class="keyword">
class</span> QX_BLOG_DLL_EXPORT comment<span class="operator">
{</span><span class="keyword">
public</span><span class="operator">:</span><span class="comment">
// -- typedef
</span><span class="keyword"> typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>blog<span class="operator">&gt;</span> blog_ptr<span class="operator">;</span><span class="comment">
// -- propri<72>t<EFBFBD>s
</span><span class="type"> long</span> m_id<span class="operator">;</span>
QString m_text<span class="operator">;</span>
QDateTime m_dt_create<span class="operator">;</span>
<font style="background-color:yellow">blog_ptr m_blog<span class="operator">;</span></font><span class="comment">
// -- constructeur, destructeur virtuel
</span> comment<span class="operator">() :</span> m_id<span class="operator">(</span><span class="int">0</span><span class="operator">) { ; }</span><span class="keyword">
virtual</span><span class="operator"> ~</span>comment<span class="operator">() { ; }
};</span>
QX_REGISTER_HPP_QX_BLOG<span class="operator">(</span>comment<span class="operator">,</span> qx<span class="operator">::</span>trait<span class="operator">::</span>no_base_class_defined<span class="operator">,</span><span class="int"> 0</span><span class="operator">)</span><span class="keyword">
typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>comment<span class="operator">&gt;</span> comment_ptr<span class="operator">;</span><span class="keyword">
typedef</span> QList<span class="operator">&lt;</span>comment_ptr<span class="operator">&gt;</span> list_comment<span class="operator">;</span><span class="pre">
#endif <span class="comment">// _QX_BLOG_COMMENT_H_</span>
</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Fichier <i>comment.cpp</i> :<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="comment.cpp">
<pre><span class="pre">#include <span class="string">"../include/precompiled.h"</span>
#include <span class="string">"../include/comment.h"</span>
#include <span class="string">"../include/blog.h"</span>
#include <span class="string">&lt;QxOrm_Impl.h&gt;</span>
</span>
QX_REGISTER_CPP_QX_BLOG<span class="operator">(</span>comment<span class="operator">)</span><span class="keyword">
namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>comment<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span>
t<span class="operator">.</span>id<span class="operator">(&amp;</span> comment<span class="operator">::</span>m_id<span class="operator">,</span><span class="string"> "comment_id"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> comment<span class="operator">::</span>m_text<span class="operator">,</span><span class="string"> "comment_text"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> comment<span class="operator">::</span>m_dt_create<span class="operator">,</span><span class="string"> "date_creation"</span><span class="operator">);</span>
<font style="background-color:yellow">t<span class="operator">.</span>relationManyToOne<span class="operator">(&amp;</span> comment<span class="operator">::</span>m_blog<span class="operator">,</span><span class="string"> "blog_id"</span><span class="operator">);</font>
}}</span>
</pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3820">many-to-many
(n-n)</a></p>
<div class="manual_div_content">
Une relation <i>many-to-many (n-n)</i> est d<>finie par la m<>thode : <a
href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::relationManyToMany()</a>.
Cette m<>thode renvoie une instance de la classe <a
href="../doxygen/html/classqx_1_1_ix_sql_relation.html"
target="_blank">qx::IxSqlRelation</a> (classe de base pour toutes les relations) et
n<>cessite 5 param<61>tres :
<ul>
<li><i>V U::* pData</i> : r<>f<EFBFBD>rence vers la donn<6E>e membre de la classe ;</li>
<li><i>const QString & sKey</i> : cl<63> unique associ<63>e <20> la relation ;</li>
<li><i>const QString & sExtraTable</i> : nom de la table suppl<70>mentaire permettant de
stocker les <i>id</i> de chaque c<>t<EFBFBD> des relations ;</li>
<li><i>const QString & sForeignKeyOwner</i> : cl<63> <20>trang<6E>re d<>finie dans la table
suppl<70>mentaire pour repr<70>senter la classe/table courante ;</li>
<li><i>const QString & sForeignKeyDataType</i> : cl<63> <20>trang<6E>re d<>finie dans la table
suppl<70>mentaire pour repr<70>senter la classe/table associ<63>e <20> la relation.</li>
</ul>
<br>
<b>Par exemple : </b> une <i>category</i> r<>f<EFBFBD>rence plusieurs <i>blog</i> et un <i>blog</i>
peut
appartenir <20> plusieurs <i>category</i> : nous allons ainsi montrer
comment d<>finir une relation de type <b><i>many-to-many</i></b>.
Ce type de relation implique une table suppl<70>mentaire dans la base de
donn<6E>es pour stocker la liste des <i>id</i> de chaque c<>t<EFBFBD> des
relations. <br>
Au niveau base de donn<6E>es, voici les trois tables qui correspondent : <br>
<br>
<img alt="qxBlog.table.category" src="./resource/qxBlog.table.category.jpg" width="464"
height="115"><br>
<br>
Fichier <i>category.h</i> :<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="category.h">
<pre><span class="pre">#ifndef _QX_BLOG_CATEGORY_H_
#define _QX_BLOG_CATEGORY_H_
</span><span class="keyword">
class</span> blog<span class="operator">;</span><span class="keyword">
class</span> QX_BLOG_DLL_EXPORT category<span class="operator">
{</span><span class="keyword">
public</span><span class="operator">:</span><span class="comment">
// -- typedef
</span><span class="keyword"> typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>blog<span class="operator">&gt;</span> blog_ptr<span class="operator">;</span><span class="keyword">
typedef</span> qx<span class="operator">::</span>QxCollection<span class="operator">&lt;</span><span class="type">long</span><span class="operator">,</span> blog_ptr<span class="operator">&gt;</span> list_blog<span class="operator">;</span><span class="comment">
// -- propri<72>t<EFBFBD>s
</span><span class="type"> long</span> m_id<span class="operator">;</span>
QString m_name<span class="operator">;</span>
QString m_desc<span class="operator">;</span>
<font style="background-color:yellow">list_blog m_blogX<span class="operator">;</span></font><span class="comment">
// -- constructeur, destructeur virtuel
</span> category<span class="operator">() :</span> m_id<span class="operator">(</span><span class="int">0</span><span class="operator">) { ; }</span><span class="keyword">
virtual</span><span class="operator"> ~</span>category<span class="operator">() { ; }
};</span>
QX_REGISTER_HPP_QX_BLOG<span class="operator">(</span>category<span class="operator">,</span> qx<span class="operator">::</span>trait<span class="operator">::</span>no_base_class_defined<span class="operator">,</span><span class="int"> 0</span><span class="operator">)</span><span class="keyword">
typedef</span> QSharedPointer<span class="operator">&lt;</span>category<span class="operator">&gt;</span> category_ptr<span class="operator">;</span><span class="keyword">
typedef</span> qx<span class="operator">::</span>QxCollection<span class="operator">&lt;</span><span class="type">long</span><span class="operator">,</span> category_ptr<span class="operator">&gt;</span> list_category<span class="operator">;</span><span class="pre">
#endif <span class="comment">// _QX_BLOG_CATEGORY_H_</span>
</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Fichier <i>category.cpp</i> :<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="category.cpp">
<pre><span class="pre">#include <span class="string">"../include/precompiled.h"</span>
#include <span class="string">"../include/category.h"</span>
#include <span class="string">"../include/blog.h"</span>
#include <span class="string">&lt;QxOrm_Impl.h&gt;</span>
</span>
QX_REGISTER_CPP_QX_BLOG<span class="operator">(</span>category<span class="operator">)</span><span class="keyword">
namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>category<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span>
t<span class="operator">.</span>id<span class="operator">(&amp;</span> category<span class="operator">::</span>m_id<span class="operator">,</span><span class="string"> "category_id"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> category<span class="operator">::</span>m_name<span class="operator">,</span><span class="string"> "name"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> category<span class="operator">::</span>m_desc<span class="operator">,</span><span class="string"> "description"</span><span class="operator">);</span>
<font style="background-color:yellow">t<span class="operator">.</span>relationManyToMany<span class="operator">(&amp;</span> category<span class="operator">::</span>m_blogX<span class="operator">,</span><span class="string"> "list_blog"</span><span class="operator">,</span><span class="string"> "category_blog"</span><span class="operator">,</span><span class="string"> "category_id"</span><span class="operator">,</span><span class="string"> "blog_id"</span><span class="operator">);</font>
}}</span>
</pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3830">one-to-one (1-1)</a>
</p>
<div class="manual_div_content">
Une relation <i>one-to-one (1-1)</i> permet de repr<70>senter 2 entit<69>s distinctes qui partagent
le m<>me identifiant en base de donn<6E>es.
Une relation <i>one-to-one (1-1)</i> est d<>finie par la m<>thode : <a
href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::relationOneToOne()</a>.
Cette m<>thode renvoie une instance de la classe <a
href="../doxygen/html/classqx_1_1_ix_sql_relation.html"
target="_blank">qx::IxSqlRelation</a> (classe de base pour toutes les relations) et
n<>cessite 2 param<61>tres :
<ul>
<li><i>V U::* pData</i> : r<>f<EFBFBD>rence vers la donn<6E>e membre de la classe ;</li>
<li><i>const QString & sKey</i> : cl<63> unique associ<63>e <20> la relation.</li>
</ul>
<br>
<b>Par exemple :</b> prenons l'exemple d'une table <i>person</i> et d'une autre table
<i>author</i> : un <i>author</i> est <20>galement une <i>person</i>, les 2 tables pourraient
partager le m<>me identifiant en base de donn<6E>es.
Au niveau base de donn<6E>es, voici les 2 tables qui correspondent (<i>person_id ==
author_id</i>) :<br>
<br>
<img alt="qxBlog.table.person" src="resource/qxBlog.table.person.jpg" width="279"
height="118">
<br><br><br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3840">Requ<EFBFBD>te SQL avec
relations</a></p>
<div class="manual_div_content">
La biblioth<74>que QxOrm supporte quatre types de relations pour lier les classes C++
enregistr<74>es dans le contexte QxOrm : <i>one-to-one</i>, <i>one-to-many</i>,
<i>many-to-one</i> et <i>many-to-many</i>.<br>
Pour plus de d<>tails sur la d<>finition de ces relations, il est conseill<6C> de lire <a
href="./tutorial.html" target="_blank">le tutoriel qxBlog</a>.<br>
Nous allons d<>tailler dans cette Q&R les diff<66>rentes m<>thodes de r<>cup<75>ration des donn<6E>es
(module <a href="../doxygen/html/group___qx_dao.html" target="_blank">QxDao</a>, fonctions du
namespace <a href="../doxygen/html/namespaceqx_1_1dao.html" target="_blank">qx::dao</a>) :
<ul>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html" target="_blank">fetch_by_id</a>, <a
href="../doxygen/html/namespaceqx_1_1dao.html" target="_blank">fetch_all</a> et <a
href="../doxygen/html/namespaceqx_1_1dao.html" target="_blank">fetch_by_query</a> :
r<>cup<75>re les donn<6E>es en requ<71>tant une seule table de la base de donn<6E>es (on parle alors
de mode <i>lazy fetch</i>) ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank">fetch_by_id_with_all_relation</a>, <a
href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank">fetch_all_with_all_relation</a> et <a
href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank">fetch_by_query_with_all_relation</a> : r<>cup<75>re les donn<6E>es en
requ<71>tant une table + toutes ses tables li<6C>es (soit une requ<71>te sur plusieurs tables de
la base de donn<6E>es, on parle alors de mode <i>eager fetch</i>) ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank">fetch_by_id_with_relation</a>, <a
href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank">fetch_all_with_relation</a> et <a
href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank">fetch_by_query_with_relation</a> : <20>quivalent aux fonctions
ci-dessus (mode <i>eager fetch</i>) avec possibilit<69> de pr<70>ciser les relations <20>
r<>cup<75>rer sur plusieurs niveaux.</li>
</ul>
Le premier param<61>tre des fonctions <a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank">fetch_by_id_with_relation</a>, <a
href="../doxygen/html/namespaceqx_1_1dao.html" target="_blank">fetch_all_with_relation</a>
et <a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank">fetch_by_query_with_relation</a> correspond <20> la liste des relations <20>
requ<71>ter.<br>
Cette liste de relations peut contenir les <20>l<EFBFBD>ments suivants :
<ul>
<li>identifiant d'une relation : chaque relation poss<73>de une cl<63> d<>finie au niveau de la
fonction de param<61>trage <i>qx::register_class&lt;T&gt;</i> ;</li>
<li>le mot-cl<63> "<i>*</i>" signifie "<i>r<EFBFBD>cup<EFBFBD>rer toutes les relations d<>finies dans la
fonction de param<61>trage <i>qx::register_class&lt;T&gt;</i> sur un niveau</i>" ;</li>
<li>le mot-cl<63> "<i>-></i>" signifie jointure de type "<i>LEFT OUTER JOIN</i>" (jointure
par d<>faut de la biblioth<74>que QxOrm) ;</li>
<li>le mot-cl<63> "<i>>></i>" signifie jointure de type "<i>INNER JOIN</i>" entre deux
tables.</li>
</ul>
<b>Remarque :</b> en utilisant le mot-cl<63> "*" pour indiquer "<i>toutes les relations sur un
niveau</i>", les appels suivants sont <20>quivalents :
<ul>
<li><i>qx::dao::fetch_by_id_with_relation(<b>"*"</b>, ...)</i> ==
<i>qx::dao::fetch_by_id_with_all_relation(...)</i> ;
</li>
<li><i>qx::dao::fetch_by_query_with_relation(<b>"*"</b>, ...)</i> ==
<i>qx::dao::fetch_by_query_with_all_relation(...)</i> ;
</li>
<li><i>qx::dao::fetch_all_with_relation(<b>"*"</b>, ...)</i> ==
<i>qx::dao::fetch_all_with_all_relation(...)</i>.
</li>
</ul>
<br>
<b>Exemple :</b> <20> partir du tutoriel qxBlog, il est possible de r<>cup<75>rer les donn<6E>es
suivantes avec une seule requ<71>te :<br>
<br>
<b>1-</b> r<>cup<75>rer un <i>blog</i> et son <i>author</i> ;<br>
<b>2-</b> pour l'<i>author</i> valoris<69>, r<>cup<75>rer tous les <i>blog</i> qu'il a <20>crit ;<br>
<b>3-</b> pour chaque <i>blog</i> que l'<i>author</i> a <20>crit, r<>cup<75>rer tous les
<i>comment</i> associ<63>s.<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="complex fetch with relationships">
<pre>blog_ptr my_blog<span class="operator"> =</span> blog_ptr<span class="operator">(</span><span class="keyword">new</span> blog<span class="operator">(</span><span class="int">10</span><span class="operator">));</span>
QSqlError daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_id_with_relation<span class="operator">(</span><span class="string">"author_id-&gt;list_blog-&gt;list_comment"</span><span class="operator">,</span> my_blog<span class="operator">);</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Ce qui g<>n<EFBFBD>re la requ<71>te SQL suivante :
<div style="width:900px; height:180px; overflow:auto; background-color:white">
<pre><span class="int">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</span></pre>
</div>
<br><br>
<b>Autre exemple :</b> il est <20>galement possible de cr<63>er une liste de relations <20> r<>cup<75>rer,
comme ceci par exemple :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="complex fetch with relationships">
<pre>blog_ptr my_blog<span class="operator"> =</span> blog_ptr<span class="operator">(</span><span class="keyword">new</span> blog<span class="operator">(</span><span class="int">10</span><span class="operator">));</span>
QStringList relation<span class="operator">;</span>
relation<span class="operator"> &lt;&lt;</span><span class="string"> "author_id-&gt;list_blog-&gt;list_comment"</span><span class="operator">;</span>
relation<span class="operator"> &lt;&lt;</span><span class="string"> "author_id-&gt;list_blog-&gt;list_category"</span><span class="operator">;</span>
relation<span class="operator"> &lt;&lt;</span><span class="string"> "list_comment"</span><span class="operator">;</span>
relation<span class="operator"> &lt;&lt;</span><span class="string"> "list_category"</span><span class="operator">;</span>
QSqlError daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_id_with_relation<span class="operator">(</span>relation<span class="operator">,</span> my_blog<span class="operator">);</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Ce qui g<>n<EFBFBD>re la requ<71>te SQL suivante :
<div style="width:900px; height:270px; overflow:auto; background-color:white">
<pre><span class="int">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</span></pre>
</div>
<br><br>
<b>Autre exemple :</b> pour r<>cup<75>rer toutes les relations pour un niveau donn<6E>, il faut
utiliser le mot-cl<63> "*".<br>
Pour r<>cup<75>rer toutes les donn<6E>es de toutes les relations sur trois niveaux, il faut <20>crire
:<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="complex fetch with relationships">
<pre>blog_ptr my_blog<span class="operator"> =</span> blog_ptr<span class="operator">(</span><span class="keyword">new</span> blog<span class="operator">(</span><span class="int">10</span><span class="operator">));</span>
QSqlError daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_id_with_relation<span class="operator">(</span><span class="string">"*-&gt;*-&gt;*"</span><span class="operator">,</span> my_blog<span class="operator">);</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Ce qui g<>n<EFBFBD>re la requ<71>te SQL suivante :
<div style="width:900px; height:620px; overflow:auto; background-color:white">
<pre><span class="int">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</span></pre>
</div>
<br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3850">S<EFBFBD>lectionner les
colonnes des relations <20> r<>cup<75>rer et d<>finition des alias SQL</a></p>
<div class="manual_div_content">
Il est parfois n<>cessaire de ne pas requ<71>ter toutes les colonnes d'une table par soucis
d'optimisation : en effet, s<>lectionner les colonnes r<>ellement utilis<69>es par un traitement
permet de limiter les flux r<>seau entre la base de donn<6E>es et l'application C++, ce qui
am<61>liore les performances.<br>
<br>
Concernant les relations, la biblioth<74>que QxOrm fournit une syntaxe sp<73>cifique pour
s<>lectionner les colonnes <20> r<>cup<75>rer, sous la forme : <b>my_relation { col_1, col_2, etc...
}</b>.
Si cette syntaxe n'est pas utilis<69>e, par d<>faut, QxOrm r<>cup<75>re toutes les colonnes.<br>
<br>
<b>Par exemple</b> : imaginons la requ<71>te suivante qui permet de r<>cup<75>rer :
<ul>
<li>uniquement la colonne <i>blog_text</i> de la table <i>blog</i> ;</li>
<li>uniquement les colonnes <i>name</i> et <i>birthdate</i> de la table <i>author</i> ;
</li>
<li>uniquement la colonne <i>comment_text</i> de la table <i>comment</i>.</li>
</ul>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="select relationships columns">
<pre><span class="comment"> // Fetch relations defining columns to fetch with syntax { col_1, col_2, etc... }
</span> list_blog lstBlogComplexRelation<span class="operator">;</span>
<font style="background-color:yellow">QStringList relations<span class="operator"> =</span> QStringList<span class="operator">() &lt;&lt;</span><span class="string"> "{ blog_text }"</span><span class="operator"> &lt;&lt;</span><span class="string"> "author_id { name, birthdate }"</span><span class="operator"> &lt;&lt;</span><span class="string"> "list_comment { comment_text }"</span><span class="operator">;</span></font>
QSqlError daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_all_with_relation<span class="operator">(</span>relations<span class="operator">,</span> lstBlogComplexRelation<span class="operator">);</span>
qx<span class="operator">::</span>dump<span class="operator">(</span>lstBlogComplexRelation<span class="operator">);</span>
qAssert<span class="operator">(</span>lstBlogComplexRelation<span class="operator">.</span>size<span class="operator">() &gt;</span><span class="int"> 0</span><span class="operator">);</span>
qAssert<span class="operator">(</span>lstBlogComplexRelation<span class="operator">[</span><span class="int">0</span><span class="operator">]-&gt;</span>m_text<span class="operator"> !=</span><span class="string"> ""</span><span class="operator">);</span><span class="comment"> // Fetched
</span> qAssert<span class="operator">(</span>lstBlogComplexRelation<span class="operator">[</span><span class="int">0</span><span class="operator">]-&gt;</span>m_dt_creation<span class="operator">.</span>isNull<span class="operator">());</span><span class="comment"> // Not fetched
</span> qAssert<span class="operator">(</span>lstBlogComplexRelation<span class="operator">[</span><span class="int">0</span><span class="operator">]-&gt;</span>m_author<span class="operator">-&gt;</span>m_sex<span class="operator"> ==</span> author<span class="operator">::</span>unknown<span class="operator">);</span><span class="comment"> // Not fetched
</span> qAssert<span class="operator">(</span>lstBlogComplexRelation<span class="operator">[</span><span class="int">0</span><span class="operator">]-&gt;</span>m_author<span class="operator">-&gt;</span>m_name<span class="operator"> !=</span><span class="string"> ""</span><span class="operator">);</span><span class="comment"> // Fetched
</span> qAssert<span class="operator">(</span>lstBlogComplexRelation<span class="operator">[</span><span class="int">0</span><span class="operator">]-&gt;</span>m_commentX<span class="operator">.</span>size<span class="operator">() &gt;</span><span class="int"> 0</span><span class="operator">);</span>
qAssert<span class="operator">(</span>lstBlogComplexRelation<span class="operator">[</span><span class="int">0</span><span class="operator">]-&gt;</span>m_commentX<span class="operator">[</span><span class="int">0</span><span class="operator">]-&gt;</span>m_dt_create<span class="operator">.</span>isNull<span class="operator">());</span><span class="comment"> // Not fetched
</span> qAssert<span class="operator">(</span>lstBlogComplexRelation<span class="operator">[</span><span class="int">0</span><span class="operator">]-&gt;</span>m_commentX<span class="operator">[</span><span class="int">0</span><span class="operator">]-&gt;</span>m_text<span class="operator"> !=</span><span class="string"> ""</span><span class="operator">);</span><span class="comment"> // Fetched</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<b>Remarque :</b> une autre syntaxe est disponible afin de renseigner les colonnes <20> ne pas
r<>cup<75>rer : <b>my_relation -{ col_1, col_2, etc... }</b>.
<br><br>
<b>Autre remarque :</b> il est <20>galement possible de d<>finir <b>un alias par relation</b> <20>
utiliser dans la requ<71>te SQL.
Ceci est utile pour l'<27>criture des conditions dans la clause <i>WHERE</i>.
Un alias SQL peut <20>tre d<>fini entre les caract<63>res <b>&lt;</b> <b>&gt;</b>.
<br><br>
<b>Exemple :</b> voici un exemple de fetch avec relations en d<>finissant des alias SQL par
relation :
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="select relationships columns">
<pre>list_blog lstBlogComplexRelation3;
QStringList relations;
relations &lt;&lt; <span class="string">"<font style="background-color:yellow">&lt;blog_alias&gt;</font> { blog_text }"</span>;
relations &lt;&lt; <span class="string">"author_id <font style="background-color:yellow">&lt;author_alias&gt;</font> { name, birthdate }"</span>;
relations &lt;&lt; <span class="string">"list_comment <font style="background-color:yellow">&lt;list_comment_alias&gt;</font> { comment_text } -&gt; blog_id <font style="background-color:yellow">&lt;blog_alias_2&gt;</font> -&gt; * <font style="background-color:yellow">&lt;..._my_alias_suffix&gt;</font>"</span>;
QSqlError daoError = qx::dao::fetch_all_with_relation(relations, lstBlogComplexRelation3);
qx::dump(lstBlogComplexRelation3);</pre>
</td>
</tr>
</tbody>
</table>
<br><br>
Ce qui g<>n<EFBFBD>re la requ<71>te SQL suivante :
<br>
<div style="width:900px; height:230px; overflow:auto; background-color:white">
<pre><span class="int">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 <font style="background-color:yellow">blog_alias</font>
LEFT OUTER JOIN author <font style="background-color:yellow">author_alias</font> ON author_alias.author_id = blog_alias.author_id
LEFT OUTER JOIN comment <font style="background-color:yellow">list_comment_alias</font> ON list_comment_alias.blog_id = blog_alias.blog_id
LEFT OUTER JOIN blog <font style="background-color:yellow">blog_alias_2</font> ON blog_alias_2.blog_id = list_comment_alias.blog_id
LEFT OUTER JOIN author <font style="background-color:yellow">author_my_alias_suffix</font> ON author_my_alias_suffix.author_id = blog_alias_2.author_id
LEFT OUTER JOIN comment <font style="background-color:yellow">comment_my_alias_suffix</font> 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 <font style="background-color:yellow">category_my_alias_suffix</font> ON category_blog_6.category_id = category_my_alias_suffix.category_id</span></pre>
</div>
<br><br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3855">Ajout SQL dans les
clauses LEFT OUTER JOIN / INNER JOIN</a></p>
<div class="manual_div_content">
La classe <a href="../doxygen/html/classqx_1_1_qx_sql_query.html"
target="_blank">qx::QxSqlQuery</a> (ou son alias <a
href="../doxygen/html/classqx_1_1_qx_sql_query.html" target="_blank">qx_query</a>) dispose
de la m<>thode suivante :
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="qx::QxSqlQuery::addJoinQuery()">
<pre>QxSqlQuery & QxSqlQuery::<font style="background-color:yellow">addJoinQuery</font>(const QString & relationKeyOrAlias, const QxSqlQuery & joinQuery);</pre>
</td>
</tr>
</tbody>
</table>
<br><br>
La m<>thode <i>qx::QxSqlQuery::addJoinQuery()</i> permet d'ins<6E>rer des sous-requ<71>tes SQL dans
les clauses <i>LEFT OUTER JOIN</i> / <i>INNER JOIN</i>.<br>
Par exemple :
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="qx::QxSqlQuery::addJoinQuery()">
<pre><span class="comment">// Test to add join SQL sub-queries (inside LEFT OUTER JOIN or INNER JOIN)</span>
list_blog lstBlogWithJoinQueries;
qx_query query = qx_query().where(<span class="string">"blog_alias.blog_text"</span>).isEqualTo(<span class="string">"update blog_text_1"</span>);
<font style="background-color:yellow">query.addJoinQuery</font>(<span class="string">"list_comment_alias"</span>, <span class="string">"AND list_comment_alias.comment_text IS NOT NULL"</span>);
<font style="background-color:yellow">query.addJoinQuery</font>(<span class="string">"author_alias"</span>, qx_query().freeText(<span class="string">"AND author_alias.sex = :sex"</span>, QVariantList() &lt;&lt; author::female));
daoError = qx::dao::fetch_by_query_with_relation(QStringList() &lt;&lt; <span class="string">"&lt;blog_alias&gt; { blog_text }"</span> &lt;&lt; <span class="string">"author_id &lt;author_alias&gt; { name, birthdate, sex }"</span>
&lt;&lt; <span class="string">"list_comment &lt;list_comment_alias&gt; { comment_text }"</span>, query, lstBlogWithJoinQueries);
qx::dump(lstBlogWithJoinQueries);
qAssert(lstBlogWithJoinQueries.size() &gt; 0);
qAssert(lstBlogWithJoinQueries[0]-&gt;m_text == <span class="string">"update blog_text_1"</span>);
qAssert(lstBlogWithJoinQueries[0]-&gt;m_author-&gt;m_sex == author::female);</pre>
</td>
</tr>
</tbody>
</table>
<br><br>
Le code C++ ci-dessus va construire la requ<71>te SQL suivante :
<br><br>
<div style="width:900px; height:200px; overflow:auto; background-color:white">
<pre><span class="int">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
<font style="background-color:yellow">AND author_alias.sex = :sex</font>)
LEFT OUTER JOIN comment list_comment_alias ON (list_comment_alias.blog_id = blog_alias.blog_id
<font style="background-color:yellow">AND list_comment_alias.comment_text IS NOT NULL</font>)
WHERE blog_alias.blog_text = :blog_alias_blog_text_1_0</span></pre>
</div>
<br><br>
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_390">Collections support<72>es
par QxOrm</a></p>
<div class="manual_div_content">
QxOrm supporte de nombreux conteneurs livr<76>s avec <a href="#manual_3900">Qt</a>, <a
href="#manual_3910">boost</a> ou la <a href="#manual_3920">biblioth<EFBFBD>que standard std</a>.
La biblioth<74>que QxOrm fournit <20>galement son propre conteneur, nomm<6D> <a
href="#manual_3930">qx::QxCollection</a>, particuli<6C>rement adapt<70> pour stocker les donn<6E>es
issues d'une base de donn<6E>es.
Le d<>veloppeur a donc <20> sa disposition un large choix : QxOrm n'impose aucune contrainte sur
l'utilisation des collections.
<br><br>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3900">Collections de Qt</a>
</p>
<div class="manual_div_content">
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="Qt collections">
<pre>
QList&lt;T&gt;
QVector&lt;T&gt;
QSet&lt;T&gt;
QLinkedList&lt;T&gt;
QHash&lt;Key, Value&gt;
QMap&lt;Key, Value&gt;
QMultiHash&lt;Key, Value&gt;
QMultiMap&lt;Key, Value&gt;
</pre>
</td>
</tr>
</tbody>
</table>
<br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3910">Collections de
boost</a></p>
<div class="manual_div_content">
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="boost collections">
<pre>
boost::unordered_map&lt;Key, Value&gt;
boost::unordered_set&lt;T&gt;
boost::unordered_multimap&lt;Key, Value&gt;
boost::unordered_multiset&lt;T&gt;
</pre>
</td>
</tr>
</tbody>
</table>
<br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3920">Collections fournies
par l'espace de nom standard std</a></p>
<div class="manual_div_content">
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="std collections">
<pre>
std::list&lt;T&gt;
std::vector&lt;T&gt;
std::set&lt;T&gt;
std::map&lt;Key, Value&gt;
std::unordered_map&lt;Key, Value&gt;
std::unordered_set&lt;T&gt;
std::unordered_multimap&lt;Key, Value&gt;
std::unordered_multiset&lt;T&gt;
</pre>
</td>
</tr>
</tbody>
</table>
<br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_3930">qx::QxCollection</a>
</p>
<div class="manual_div_content">
Il existe de nombreux <i>container</i> dans les biblioth<74>ques <b>stl</b>, <b>boost</b> et
<b>Qt</b>.<br>
Il est donc l<>gitime de se poser cette question : <20> quoi sert <i>qx::QxCollection&lt;Key,
Value&gt;</i> ?<br>
<i><a href="../doxygen/html/classqx_1_1_qx_collection.html"
target="_blank">qx::QxCollection&lt;Key, Value&gt;</a></i> est un nouveau
<i>container</i> (bas<61> sur l'excellente biblioth<74>que <a
href="http://www.boost.org/doc/libs/release/libs/multi_index/doc/index.html"
target="_blank"><i>boost::multi_index_container</i></a>) qui poss<73>de les fonctionnalit<69>s
suivantes :
<ul>
<li>conserve l'ordre d'insertion des <20>l<EFBFBD>ments dans la liste ;
</li>
<li>acc<EFBFBD>s rapide <20> un <20>l<EFBFBD>ment par son index : <20>quivaut <20> <i>std::vector&lt;T&gt;</i> ou
<i>QList&lt;T&gt;</i> par exemple ;
</li>
<li>acc<EFBFBD>s rapide <20> un <20>l<EFBFBD>ment par une cl<63> (<i>hash-map</i>) : <20>quivaut <20> <i>QHash&lt;Key,
Value&gt;</i> ou <i>boost::unordered_map&lt;Key, Value&gt;</i> par exemple ;
</li>
<li>fonctions de tri sur le type <i>Key</i> et sur le type <i>Value</i> ;
</li>
<li>thread-safe.
</li>
</ul>
<b>Remarque :</b>
<i>qx::QxCollection&lt;Key, Value&gt;</i> est compatible avec la macro <i>foreach</i> fournie
par la biblioth<74>que <b>Qt</b> ainsi que par la macro <a
href="http://www.boost.org/doc/libs/release/doc/html/foreach.html"
target="_blank"><i>BOOST_FOREACH</i></a> fournie par la biblioth<74>que <b>boost</b>.<br>
Cependant, chaque <20>l<EFBFBD>ment renvoy<6F> par ces deux macros correspond <20> un objet de type
<i>std::pair&lt;Key, Value&gt;</i>.<br>
Pour obtenir un r<>sultat 'plus naturel' et plus lisible, il est conseill<6C> d'utiliser la macro
<i>_foreach</i> : cette macro utilise <i>BOOST_FOREACH</i> pour tous les <i>container</i>
sauf pour <i>qx::QxCollection&lt;Key, Value&gt;</i>.<br>
Dans ce cas, l'<27>l<EFBFBD>ment renvoy<6F> correspond au type <i>Value</i> (voir par la suite l'exemple
d'utilisation).<br>
La macro <i>_foreach</i> est donc compatible avec tous les <i>container</i> (<b>stl</b>,
<b>Qt</b>, <b>boost</b>, etc.) puisqu'elle utilise la macro <i>BOOST_FOREACH</i>.<br><br>
<b>Autre Remarque :</b>
<i>qx::QxCollection&lt;Key, Value&gt;</i> est particuli<6C>rement adapt<70> pour recevoir des
donn<6E>es issues d'une base de donn<6E>es.<br>
En effet, ces donn<6E>es peuvent <20>tre tri<72>es (en utilisant <i>ORDER BY</i> dans une requ<71>te SQL
par exemple), il est donc important de conserver l'ordre d'insertion des <20>l<EFBFBD>ments dans la
liste.<br>
De plus, chaque donn<6E>e issue d'une base de donn<6E>es poss<73>de un identifiant unique. Il est donc
int<6E>ressant de pouvoir acc<63>der <20> un <20>l<EFBFBD>ment en fonction de cet identifiant unique de mani<6E>re
extr<74>mement rapide (<i>hash-map</i>).<br><br>
<b>Exemple d'utilisation de la collection <i>qx::QxCollection&lt;Key, Value&gt;</i> :</b><br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">/* d<>finition d'une classe drug avec 3 propri<72>t<EFBFBD>s : 'code', 'name', 'description' */</span><span class="keyword">
class</span> drug<span class="operator"> {</span><span class="keyword"> public</span><span class="operator">:</span> QString code<span class="operator">;</span> QString name<span class="operator">;</span> QString desc<span class="operator">; };</span><span class="comment">
/* pointeur intelligent associ<63> <20> la classe drug */</span><span class="keyword">
typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>drug<span class="operator">&gt;</span> drug_ptr<span class="operator">;</span><span class="comment">
/* collection de drugs (acc<63>s rapide <20> un <20>l<EFBFBD>ment de la collection par la propri<72>t<EFBFBD> 'code') */</span>
qx<span class="operator">::</span>QxCollection<span class="operator">&lt;</span>QString<span class="operator">,</span> drug_ptr<span class="operator">&gt;</span> lstDrugs<span class="operator">;</span><span class="comment">
/* cr<63>ation de 3 nouveaux drugs */</span>
drug_ptr d1<span class="operator">;</span> d1<span class="operator">.</span>reset<span class="operator">(</span><span class="keyword">new</span> drug<span class="operator">());</span> d1<span class="operator">-&gt;</span>code<span class="operator"> =</span><span class="string"> "code1"</span><span class="operator">;</span> d1<span class="operator">-&gt;</span>name<span class="operator"> =</span><span class="string"> "name1"</span><span class="operator">;</span> d1<span class="operator">-&gt;</span>desc<span class="operator"> =</span><span class="string"> "desc1"</span><span class="operator">;</span>
drug_ptr d2<span class="operator">;</span> d2<span class="operator">.</span>reset<span class="operator">(</span><span class="keyword">new</span> drug<span class="operator">());</span> d2<span class="operator">-&gt;</span>code<span class="operator"> =</span><span class="string"> "code2"</span><span class="operator">;</span> d2<span class="operator">-&gt;</span>name<span class="operator"> =</span><span class="string"> "name2"</span><span class="operator">;</span> d2<span class="operator">-&gt;</span>desc<span class="operator"> =</span><span class="string"> "desc2"</span><span class="operator">;</span>
drug_ptr d3<span class="operator">;</span> d3<span class="operator">.</span>reset<span class="operator">(</span><span class="keyword">new</span> drug<span class="operator">());</span> d3<span class="operator">-&gt;</span>code<span class="operator"> =</span><span class="string"> "code3"</span><span class="operator">;</span> d3<span class="operator">-&gt;</span>name<span class="operator"> =</span><span class="string"> "name3"</span><span class="operator">;</span> d3<span class="operator">-&gt;</span>desc<span class="operator"> =</span><span class="string"> "desc3"</span><span class="operator">;</span><span class="comment">
/* insertion des 3 drugs dans la collection */</span>
lstDrugs<span class="operator">.</span>insert<span class="operator">(</span>d1<span class="operator">-&gt;</span>code<span class="operator">,</span> d1<span class="operator">);</span>
lstDrugs<span class="operator">.</span>insert<span class="operator">(</span>d2<span class="operator">-&gt;</span>code<span class="operator">,</span> d2<span class="operator">);</span>
lstDrugs<span class="operator">.</span>insert<span class="operator">(</span>d3<span class="operator">-&gt;</span>code<span class="operator">,</span> d3<span class="operator">);</span><span class="comment">
/* parcours la collection en utilisant le mot-cl<63> '_foreach' */</span>
_foreach<span class="operator">(</span>drug_ptr p<span class="operator">,</span> lstDrugs<span class="operator">)
{</span> qDebug<span class="operator">() &lt;&lt;</span> qPrintable<span class="operator">(</span>p<span class="operator">-&gt;</span>name<span class="operator">) &lt;&lt;</span><span class="string"> " "</span><span class="operator"> &lt;&lt;</span> qPrintable<span class="operator">(</span>p<span class="operator">-&gt;</span>desc<span class="operator">); }</span><span class="comment">
/* parcours la collection en utilisant une boucle 'for' */</span><span class="flow">
for</span><span class="operator"> (</span><span class="type">long</span> l<span class="operator"> =</span><span class="int"> 0</span><span class="operator">;</span> l<span class="operator"> &lt;</span> lstDrugs<span class="operator">.</span>count<span class="operator">(); ++</span>l<span class="operator">)
{</span>
drug_ptr p<span class="operator"> =</span> lstDrugs<span class="operator">.</span>getByIndex<span class="operator">(</span>l<span class="operator">);</span>
QString code<span class="operator"> =</span> lstDrugs<span class="operator">.</span>getKeyByIndex<span class="operator">(</span>l<span class="operator">);</span>
qDebug<span class="operator">() &lt;&lt;</span> qPrintable<span class="operator">(</span>p<span class="operator">-&gt;</span>name<span class="operator">) &lt;&lt;</span><span class="string"> " "</span><span class="operator"> &lt;&lt;</span> qPrintable<span class="operator">(</span>p<span class="operator">-&gt;</span>desc<span class="operator">);
}</span><span class="comment">
/* parcours la collection en utilisant le style Java avec 'QxCollectionIterator' */</span>
qx<span class="operator">::</span>QxCollectionIterator<span class="operator">&lt;</span>QString<span class="operator">,</span> drug_ptr<span class="operator">&gt;</span> itr<span class="operator">(</span>lstDrugs<span class="operator">);</span><span class="flow">
while</span><span class="operator"> (</span>itr<span class="operator">.</span>next<span class="operator">())
{</span>
QString code<span class="operator"> =</span> itr<span class="operator">.</span>key<span class="operator">();</span>
qDebug<span class="operator">() &lt;&lt;</span> qPrintable<span class="operator">(</span>itr<span class="operator">.</span>value<span class="operator">()-&gt;</span>name<span class="operator">) &lt;&lt;</span><span class="string"> " "</span><span class="operator"> &lt;&lt;</span> qPrintable<span class="operator">(</span>itr<span class="operator">.</span>value<span class="operator">()-&gt;</span>desc<span class="operator">);
}</span><span class="comment">
/* effectue un tri croissant par cl<63> (propri<72>t<EFBFBD> 'code') et d<>croissant par valeur */</span>
lstDrugs<span class="operator">.</span>sortByKey<span class="operator">(</span><span class="bool">true</span><span class="operator">);</span>
lstDrugs<span class="operator">.</span>sortByValue<span class="operator">(</span><span class="bool">false</span><span class="operator">);</span><span class="comment">
/* acc<63>s rapide <20> un drug par son 'code' */</span>
drug_ptr p<span class="operator"> =</span> lstDrugs<span class="operator">.</span>getByKey<span class="operator">(</span><span class="string">"code2"</span><span class="operator">);</span><span class="comment">
/* acc<63>s rapide <20> un drug par son index (position) dans la collection */</span>
drug_ptr p<span class="operator"> =</span> lstDrugs<span class="operator">.</span>getByIndex<span class="operator">(</span><span class="int">2</span><span class="operator">);</span><span class="comment">
/* teste si un drug existe dans la collection et si la liste est vide */</span><span class="type">
bool</span> bExist<span class="operator"> =</span> lstDrugs<span class="operator">.</span>exist<span class="operator">(</span><span class="string">"code3"</span><span class="operator">);</span><span class="type">
bool</span> bEmpty<span class="operator"> =</span> lstDrugs<span class="operator">.</span>empty<span class="operator">();</span><span class="comment">
/* supprime de la collection le 2<>me <20>l<EFBFBD>ment */</span>
lstDrugs<span class="operator">.</span>removeByIndex<span class="operator">(</span><span class="int">2</span><span class="operator">);</span><span class="comment">
/* supprime de la collection l'<27>l<EFBFBD>ment avec le code 'code3' */</span>
lstDrugs<span class="operator">.</span>removeByKey<span class="operator">(</span><span class="string">"code3"</span><span class="operator">);</span><span class="comment">
/* efface tous les <20>l<EFBFBD>ments de la collection */</span>
lstDrugs<span class="operator">.</span>clear<span class="operator">();</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_400">Pointeurs intelligents
support<72>s par QxOrm (smart-pointers)</a></p>
<div class="manual_div_content">
QxOrm supporte de nombreux pointeurs intelligents livr<76>s avec <a href="#manual_4000">Qt</a>, <a
href="#manual_4010">boost</a> ou la <a href="#manual_4020">biblioth<EFBFBD>que standard std</a>.
La biblioth<74>que QxOrm fournit <20>galement son propre pointeur intelligent, nomm<6D> <a
href="#manual_4030">qx::dao::ptr</a>, apportant de nouvelles fonctionnalit<69>s lorsqu'il est
utilis<69> avec les fonctions de <a href="../doxygen/html/namespaceqx_1_1dao.html"
target="_blank">l'espace de nom qx::dao</a>.
Le d<>veloppeur a donc <20> sa disposition un large choix : QxOrm n'impose aucune contrainte sur
l'utilisation des pointeurs intelligents.
<br><br>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_4000">Pointeurs
intelligents de Qt</a></p>
<div class="manual_div_content">
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="Qt smart pointers">
<pre>
QSharedPointer&lt;T&gt;
QScopedPointer&lt;T&gt;
QWeakPointer&lt;T&gt;
QSharedDataPointer&lt;T&gt;
</pre>
</td>
</tr>
</tbody>
</table>
<br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_4010">Pointeurs
intelligents de boost</a></p>
<div class="manual_div_content">
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="boost smart pointers">
<pre>
boost::shared_ptr&lt;T&gt;
boost::intrusive_ptr&lt;T&gt;
boost::scoped_ptr&lt;T&gt;
boost::weak_ptr&lt;T&gt;
</pre>
</td>
</tr>
</tbody>
</table>
<br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_4020">Pointeurs
intelligents fournis par l'espace de nom standard std</a></p>
<div class="manual_div_content">
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="std smart pointers">
<pre>
std::shared_ptr&lt;T&gt;
std::unique_ptr&lt;T&gt;
std::weak_ptr&lt;T&gt;
</pre>
</td>
</tr>
</tbody>
</table>
<br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_4030">qx::dao::ptr</a></p>
<div class="manual_div_content">
<b>QxOrm</b> est compatible avec les pointeurs intelligents des biblioth<74>ques <b>boost</b> et
<b>Qt</b>.<br>
Le pointeur intelligent d<>velopp<70> par <b>QxOrm</b> est bas<61> sur <i>QSharedPointer</i> et
apporte de nouvelles fonctionnalit<69>s s'il est utilis<69> avec les fonctions
'<i>qx::dao::...</i>'.<br>
<i><a href="../doxygen/html/classqx_1_1dao_1_1ptr.html"
target="_blank">qx::dao::ptr&lt;T&gt;</a></i> conserve automatiquement les valeurs
issues de la base de donn<6E>es.<br>
Il est ainsi possible de v<>rifier <20> tout moment si une instance d'objet a subi des
modifications gr<67>ce <20> la m<>thode '<i>isDirty()</i>' : cette m<>thode peut renvoyer la liste de
toutes les propri<72>t<EFBFBD>s ayant <20>t<EFBFBD> modifi<66>es.<br>
<i>qx::dao::ptr&lt;T&gt;</i> peut <20>galement <20>tre utilis<69> par la fonction
'<i>qx::dao::update_optimized()</i>' pour mettre <20> jour en base de donn<6E>es uniquement les
champs modifi<66>s.<br>
<i>qx::dao::ptr&lt;T&gt;</i> peut <20>tre utilis<69> avec un objet simple ou bien avec la plupart
des containers : <i>stl</i>, <i>boost</i>, <i>Qt</i> et <i>qx::QxCollection&lt;Key,
Value&gt;</i>.<br>
<br>
<b>Exemple d'utilisation du pointeur intelligent <i>qx::dao::ptr&lt;T&gt;</i> :</b><br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// exemple d'utilisation de la m<>thode 'isDirty()'
</span>qx<span class="operator">::</span>dao<span class="operator">::</span>ptr<span class="operator">&lt;</span>blog<span class="operator">&gt;</span> blog_isdirty<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>ptr<span class="operator">&lt;</span>blog<span class="operator">&gt;(</span><span class="keyword">new</span> blog<span class="operator">());</span>
blog_isdirty<span class="operator">-&gt;</span>m_id<span class="operator"> =</span> blog_1<span class="operator">-&gt;</span>m_id<span class="operator">;</span>
daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_id<span class="operator">(</span>blog_isdirty<span class="operator">);</span>
qAssert<span class="operator">(!</span> daoError<span class="operator">.</span>isValid<span class="operator">() &amp;&amp; !</span> blog_isdirty<span class="operator">.</span>isDirty<span class="operator">());</span>
blog_isdirty<span class="operator">-&gt;</span>m_text<span class="operator"> =</span><span class="string"> "blog property 'text' modified =&gt; blog is dirty !!!"</span><span class="operator">;</span>
QStringList lstDiff<span class="operator">;</span><span class="type"> bool</span> bDirty<span class="operator"> =</span> blog_isdirty<span class="operator">.</span>isDirty<span class="operator">(</span>lstDiff<span class="operator">);</span>
qAssert<span class="operator">(</span>bDirty<span class="operator"> &amp;&amp; (</span>lstDiff<span class="operator">.</span>count<span class="operator">() ==</span><span class="int"> 1</span><span class="operator">) &amp;&amp; (</span>lstDiff<span class="operator">.</span>at<span class="operator">(</span><span class="int">0</span><span class="operator">) ==</span><span class="string"> "blog_text"</span><span class="operator">));</span><span class="flow">
if</span><span class="operator"> (</span>bDirty<span class="operator">) {</span> qDebug<span class="operator">(</span><span class="string">"[QxOrm] test dirty 1 : blog is dirty =&gt; '%s'"</span><span class="operator">,</span> qPrintable<span class="operator">(</span>lstDiff<span class="operator">.</span>join<span class="operator">(</span><span class="string">"|"</span><span class="operator">))); }</span><span class="comment">
// met <20> jour uniquement la propri<72>t<EFBFBD> 'm_text' de l'instance 'blog_isdirty'
</span>daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>update_optimized<span class="operator">(</span>blog_isdirty<span class="operator">);</span>
qAssert<span class="operator">(!</span> daoError<span class="operator">.</span>isValid<span class="operator">() &amp;&amp; !</span> blog_isdirty<span class="operator">.</span>isDirty<span class="operator">());</span>
qx<span class="operator">::</span>dump<span class="operator">(</span>blog_isdirty<span class="operator">);</span><span class="comment">
// exemple d'utilisation de la m<>thode 'isDirty()' avec une liste d'objets
</span><span class="keyword">typedef</span> qx<span class="operator">::</span>dao<span class="operator">::</span>ptr<span class="operator">&lt;</span> QList<span class="operator">&lt;</span>author_ptr<span class="operator">&gt; &gt;</span> type_lst_author_test_is_dirty<span class="operator">;</span>
type_lst_author_test_is_dirty container_isdirty<span class="operator"> =</span> type_lst_author_test_is_dirty<span class="operator">(</span><span class="keyword">new</span> QList<span class="operator">&lt;</span>author_ptr<span class="operator">&gt;());</span>
daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_all<span class="operator">(</span>container_isdirty<span class="operator">);</span>
qAssert<span class="operator">(!</span> daoError<span class="operator">.</span>isValid<span class="operator">() &amp;&amp; !</span> container_isdirty<span class="operator">.</span>isDirty<span class="operator">() &amp;&amp; (</span>container_isdirty<span class="operator">-&gt;</span>count<span class="operator">() ==</span><span class="int"> 3</span><span class="operator">));</span>
author_ptr author_ptr_dirty<span class="operator"> =</span> container_isdirty<span class="operator">-&gt;</span>at<span class="operator">(</span><span class="int">1</span><span class="operator">);</span>
author_ptr_dirty<span class="operator">-&gt;</span>m_name<span class="operator"> =</span><span class="string"> "author name modified at index 1 =&gt; container is dirty !!!"</span><span class="operator">;</span>
bDirty<span class="operator"> =</span> container_isdirty<span class="operator">.</span>isDirty<span class="operator">(</span>lstDiff<span class="operator">);</span>
qAssert<span class="operator">(</span>bDirty<span class="operator"> &amp;&amp; (</span>lstDiff<span class="operator">.</span>count<span class="operator">() ==</span><span class="int"> 1</span><span class="operator">));</span><span class="flow">
if</span><span class="operator"> (</span>bDirty<span class="operator">) {</span> qDebug<span class="operator">(</span><span class="string">"[QxOrm] test dirty 2 : container is dirty =&gt; '%s'"</span><span class="operator">,</span> qPrintable<span class="operator">(</span>lstDiff<span class="operator">.</span>join<span class="operator">(</span><span class="string">"|"</span><span class="operator">))); }</span>
author_ptr_dirty<span class="operator"> =</span> container_isdirty<span class="operator">-&gt;</span>at<span class="operator">(</span><span class="int">2</span><span class="operator">);</span>
author_ptr_dirty<span class="operator">-&gt;</span>m_birthdate<span class="operator"> =</span> QDate<span class="operator">(</span><span class="int">1998</span><span class="operator">,</span><span class="int"> 03</span><span class="operator">,</span><span class="int"> 06</span><span class="operator">);</span>
bDirty<span class="operator"> =</span> container_isdirty<span class="operator">.</span>isDirty<span class="operator">(</span>lstDiff<span class="operator">);</span>
qAssert<span class="operator">(</span>bDirty<span class="operator"> &amp;&amp; (</span>lstDiff<span class="operator">.</span>count<span class="operator">() ==</span><span class="int"> 2</span><span class="operator">));</span><span class="flow">
if</span><span class="operator"> (</span>bDirty<span class="operator">) {</span> qDebug<span class="operator">(</span><span class="string">"[QxOrm] test dirty 3 : container is dirty =&gt; '%s'"</span><span class="operator">,</span> qPrintable<span class="operator">(</span>lstDiff<span class="operator">.</span>join<span class="operator">(</span><span class="string">"|"</span><span class="operator">))); }</span><span class="comment">
// met <20> jour la propri<72>t<EFBFBD> 'm_name' en position 1, la propri<72>t<EFBFBD> 'm_birthdate' en position 2 et ne change rien en position 0
</span>daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>update_optimized<span class="operator">(</span>container_isdirty<span class="operator">);</span>
qAssert<span class="operator">(!</span> daoError<span class="operator">.</span>isValid<span class="operator">() &amp;&amp; !</span> container_isdirty<span class="operator">.</span>isDirty<span class="operator">());</span>
qx<span class="operator">::</span>dump<span class="operator">(</span>container_isdirty<span class="operator">);</span><span class="comment">
// r<>cup<75>re uniquement la propri<72>t<EFBFBD> 'm_dt_creation' du blog
</span>QStringList lstColumns<span class="operator"> =</span> QStringList<span class="operator">() &lt;&lt;</span><span class="string"> "date_creation"</span><span class="operator">;</span>
list_blog lst_blog_with_only_date_creation<span class="operator">;</span>
daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_all<span class="operator">(</span>lst_blog_with_only_date_creation<span class="operator">,</span> NULL<span class="operator">,</span> lstColumns<span class="operator">);</span>
qAssert<span class="operator">(!</span> daoError<span class="operator">.</span>isValid<span class="operator">() &amp;&amp; (</span>lst_blog_with_only_date_creation<span class="operator">.</span>size<span class="operator">() &gt;</span><span class="int"> 0</span><span class="operator">));</span><span class="flow">
if</span><span class="operator"> ((</span>lst_blog_with_only_date_creation<span class="operator">.</span>size<span class="operator">() &gt;</span><span class="int"> 0</span><span class="operator">) &amp;&amp; (</span>lst_blog_with_only_date_creation<span class="operator">[</span><span class="int">0</span><span class="operator">] !=</span> NULL<span class="operator">))
{</span> qAssert<span class="operator">(</span>lst_blog_with_only_date_creation<span class="operator">[</span><span class="int">0</span><span class="operator">]-&gt;</span>m_text<span class="operator">.</span>isEmpty<span class="operator">()); }</span>
qx<span class="operator">::</span>dump<span class="operator">(</span>lst_blog_with_only_date_creation<span class="operator">);</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_410">D<EFBFBD>clencheurs
(triggers)</a></p>
<div class="manual_div_content">
Les <i>Trigger</i> de <b>QxOrm</b> permettent d'effectuer divers traitements avant et/ou apr<70>s
une insertion, une mise <20> jour ou bien une suppression dans la base de donn<6E>es.<br>
Un exemple d'utilisation se trouve dans le dossier <i>./test/qxDllSample/dll2/</i> avec la
classe <i>BaseClassTrigger</i>.<br>
Cette classe contient cinq propri<72>t<EFBFBD>s : <i>m_id</i>, <i>m_dateCreation</i>,
<i>m_dateModification</i>, <i>m_userCreation</i> et <i>m_userModification</i>.<br>
Ces propri<72>t<EFBFBD>s se mettront <20> jour automatiquement pour chaque classe h<>ritant de
<i>BaseClassTrigger</i> (cf. les classes <i>Foo</i> et <i>Bar</i> du m<>me projet).<br>
Il est n<>cessaire de sp<73>cialiser le template '<i>qx::dao::detail::QxDao_Trigger&lt;T&gt;</i>'
pour profiter de cette fonctionnalit<69>.<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="pre">#ifndef _QX_BASE_CLASS_TRIGGER_H_
#define _QX_BASE_CLASS_TRIGGER_H_
</span><span class="keyword">
class</span> QX_DLL2_EXPORT BaseClassTrigger<span class="operator">
{</span>
QX_REGISTER_FRIEND_CLASS<span class="operator">(</span>BaseClassTrigger<span class="operator">)</span><span class="keyword">
protected</span><span class="operator">:</span><span class="type">
long</span> m_id<span class="operator">;</span>
QDateTime m_dateCreation<span class="operator">;</span>
QDateTime m_dateModification<span class="operator">;</span>
QString m_userCreation<span class="operator">;</span>
QString m_userModification<span class="operator">;</span><span class="keyword">
public</span><span class="operator">:</span>
BaseClassTrigger<span class="operator">() :</span> m_id<span class="operator">(</span><span class="int">0</span><span class="operator">) { ; }</span><span class="keyword">
virtual</span><span class="operator"> ~</span>BaseClassTrigger<span class="operator">() { ; }</span><span class="type">
long</span> getId<span class="operator">()</span><span class="keyword"> const</span><span class="operator"> {</span><span class="flow"> return</span> m_id<span class="operator">; }</span>
QDateTime getDateCreation<span class="operator">()</span><span class="keyword"> const</span><span class="operator"> {</span><span class="flow"> return</span> m_dateCreation<span class="operator">; }</span>
QDateTime getDateModification<span class="operator">()</span><span class="keyword"> const</span><span class="operator"> {</span><span class="flow"> return</span> m_dateModification<span class="operator">; }</span>
QString getUserCreation<span class="operator">()</span><span class="keyword"> const</span><span class="operator"> {</span><span class="flow"> return</span> m_userCreation<span class="operator">; }</span>
QString getUserModification<span class="operator">()</span><span class="keyword"> const</span><span class="operator"> {</span><span class="flow"> return</span> m_userModification<span class="operator">; }</span><span class="type">
void</span> setId<span class="operator">(</span><span class="type">long</span> l<span class="operator">) {</span> m_id<span class="operator"> =</span> l<span class="operator">; }</span><span class="type">
void</span> setDateCreation<span class="operator">(</span><span class="keyword">const</span> QDateTime<span class="operator"> &amp;</span> dt<span class="operator">) {</span> m_dateCreation<span class="operator"> =</span> dt<span class="operator">; }</span><span class="type">
void</span> setDateModification<span class="operator">(</span><span class="keyword">const</span> QDateTime<span class="operator"> &amp;</span> dt<span class="operator">) {</span> m_dateModification<span class="operator"> =</span> dt<span class="operator">; }</span><span class="type">
void</span> setUserCreation<span class="operator">(</span><span class="keyword">const</span> QString<span class="operator"> &amp;</span> s<span class="operator">) {</span> m_userCreation<span class="operator"> =</span> s<span class="operator">; }</span><span class="type">
void</span> setUserModification<span class="operator">(</span><span class="keyword">const</span> QString<span class="operator"> &amp;</span> s<span class="operator">) {</span> m_userModification<span class="operator"> =</span> s<span class="operator">; }</span>
<font style="background-color:yellow"><span class="type">void</span> onBeforeInsert<span class="operator">(</span>qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>IxDao_Helper<span class="operator"> *</span> dao<span class="operator">);</span></font>
<font style="background-color:yellow"><span class="type">void</span> onBeforeUpdate<span class="operator">(</span>qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>IxDao_Helper<span class="operator"> *</span> dao<span class="operator">);</span></font>
<span class="operator">};</span>
QX_REGISTER_HPP_QX_DLL2<span class="operator">(</span>BaseClassTrigger<span class="operator">,</span> qx<span class="operator">::</span>trait<span class="operator">::</span>no_base_class_defined<span class="operator">,</span><span class="int"> 0</span><span class="operator">)</span><span class="keyword">
namespace</span> qx<span class="operator"> {</span><span class="keyword">
namespace</span> dao<span class="operator"> {</span><span class="keyword">
namespace</span> detail<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span>
<font style="background-color:yellow"><span class="keyword">struct</span> QxDao_Trigger<span class="operator">&lt;</span>BaseClassTrigger<span class="operator">&gt;</span></font>
<span class="operator">{</span><span class="keyword">
static inline</span><span class="type"> void</span> onBeforeInsert<span class="operator">(</span>BaseClassTrigger<span class="operator"> *</span> t<span class="operator">,</span> qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>IxDao_Helper<span class="operator"> *</span> dao<span class="operator">)
{</span><span class="flow"> if</span><span class="operator"> (</span>t<span class="operator">) {</span> t<span class="operator">-&gt;</span>onBeforeInsert<span class="operator">(</span>dao<span class="operator">); } }</span><span class="keyword">
static inline</span><span class="type"> void</span> onBeforeUpdate<span class="operator">(</span>BaseClassTrigger<span class="operator"> *</span> t<span class="operator">,</span> qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>IxDao_Helper<span class="operator"> *</span> dao<span class="operator">)
{</span><span class="flow"> if</span><span class="operator"> (</span>t<span class="operator">) {</span> t<span class="operator">-&gt;</span>onBeforeUpdate<span class="operator">(</span>dao<span class="operator">); } }</span><span class="keyword">
static inline</span><span class="type"> void</span> onBeforeDelete<span class="operator">(</span>BaseClassTrigger<span class="operator"> *</span> t<span class="operator">,</span> qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>IxDao_Helper<span class="operator"> *</span> dao<span class="operator">)
{</span> Q_UNUSED<span class="operator">(</span>t<span class="operator">);</span> Q_UNUSED<span class="operator">(</span>dao<span class="operator">); }</span><span class="keyword">
static inline</span><span class="type"> void</span> onBeforeFetch<span class="operator">(</span>BaseClassTrigger<span class="operator"> *</span> t<span class="operator">,</span> qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>IxDao_Helper<span class="operator"> *</span> dao<span class="operator">)
{</span> Q_UNUSED<span class="operator">(</span>t<span class="operator">);</span> Q_UNUSED<span class="operator">(</span>dao<span class="operator">); }</span><span class="keyword">
static inline</span><span class="type"> void</span> onAfterInsert<span class="operator">(</span>BaseClassTrigger<span class="operator"> *</span> t<span class="operator">,</span> qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>IxDao_Helper<span class="operator"> *</span> dao<span class="operator">)
{</span> Q_UNUSED<span class="operator">(</span>t<span class="operator">);</span> Q_UNUSED<span class="operator">(</span>dao<span class="operator">); }</span><span class="keyword">
static inline</span><span class="type"> void</span> onAfterUpdate<span class="operator">(</span>BaseClassTrigger<span class="operator"> *</span> t<span class="operator">,</span> qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>IxDao_Helper<span class="operator"> *</span> dao<span class="operator">)
{</span> Q_UNUSED<span class="operator">(</span>t<span class="operator">);</span> Q_UNUSED<span class="operator">(</span>dao<span class="operator">); }</span><span class="keyword">
static inline</span><span class="type"> void</span> onAfterDelete<span class="operator">(</span>BaseClassTrigger<span class="operator"> *</span> t<span class="operator">,</span> qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>IxDao_Helper<span class="operator"> *</span> dao<span class="operator">)
{</span> Q_UNUSED<span class="operator">(</span>t<span class="operator">);</span> Q_UNUSED<span class="operator">(</span>dao<span class="operator">); }
static inline</span><span class="type"> void</span> onAfterFetch<span class="operator">(</span>BaseClassTrigger<span class="operator"> *</span> t<span class="operator">,</span> qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>IxDao_Helper<span class="operator"> *</span> dao<span class="operator">)
{</span> Q_UNUSED<span class="operator">(</span>t<span class="operator">);</span> Q_UNUSED<span class="operator">(</span>dao<span class="operator">); }
};
}</span><span class="comment"> // namespace detail
</span><span class="operator">}</span><span class="comment"> // namespace dao
</span><span class="operator">}</span><span class="comment"> // namespace qx
</span><span class="pre">
#endif // _QX_BASE_CLASS_TRIGGER_H_
</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="pre">#include "../include/precompiled.h"
#include "../include/BaseClassTrigger.h"
#include &lt;QxOrm_Impl.h&gt;
</span>
QX_REGISTER_CPP_QX_DLL2<span class="operator">(</span>BaseClassTrigger<span class="operator">)</span><span class="keyword">
namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>BaseClassTrigger<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span>
IxDataMember<span class="operator"> *</span> pData<span class="operator"> =</span> NULL<span class="operator">;</span>
pData<span class="operator"> =</span> t<span class="operator">.</span>id<span class="operator">(&amp;</span> BaseClassTrigger<span class="operator">::</span>m_id<span class="operator">,</span><span class="string"> "id"</span><span class="operator">);</span>
pData<span class="operator"> =</span> t<span class="operator">.</span>data<span class="operator">(&amp;</span> BaseClassTrigger<span class="operator">::</span>m_dateCreation<span class="operator">,</span><span class="string"> "date_creation"</span><span class="operator">);</span>
pData<span class="operator"> =</span> t<span class="operator">.</span>data<span class="operator">(&amp;</span> BaseClassTrigger<span class="operator">::</span>m_dateModification<span class="operator">,</span><span class="string"> "date_modification"</span><span class="operator">);</span>
pData<span class="operator"> =</span> t<span class="operator">.</span>data<span class="operator">(&amp;</span> BaseClassTrigger<span class="operator">::</span>m_userCreation<span class="operator">,</span><span class="string"> "user_creation"</span><span class="operator">);</span>
pData<span class="operator"> =</span> t<span class="operator">.</span>data<span class="operator">(&amp;</span> BaseClassTrigger<span class="operator">::</span>m_userModification<span class="operator">,</span><span class="string"> "user_modification"</span><span class="operator">);
}}</span>
<font style="background-color:yellow"><span class="type">void</span> BaseClassTrigger<span class="operator">::</span>onBeforeInsert<span class="operator">(</span>qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>IxDao_Helper<span class="operator"> *</span> dao<span class="operator">)</span></font>
<span class="operator">{</span>
Q_UNUSED<span class="operator">(</span>dao<span class="operator">);</span>
m_dateCreation<span class="operator"> =</span> QDateTime<span class="operator">::</span>currentDateTime<span class="operator">();</span>
m_dateModification<span class="operator"> =</span> QDateTime<span class="operator">::</span>currentDateTime<span class="operator">();</span>
m_userCreation<span class="operator"> =</span><span class="string"> "current_user_1"</span><span class="operator">;</span>
m_userModification<span class="operator"> =</span><span class="string"> "current_user_1"</span><span class="operator">;
}</span>
<font style="background-color:yellow"><span class="type">void</span> BaseClassTrigger<span class="operator">::</span>onBeforeUpdate<span class="operator">(</span>qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>IxDao_Helper<span class="operator"> *</span> dao<span class="operator">)</span></font>
<span class="operator">{</span>
Q_UNUSED<span class="operator">(</span>dao<span class="operator">);</span>
m_dateModification<span class="operator"> =</span> QDateTime<span class="operator">::</span>currentDateTime<span class="operator">();</span>
m_userModification<span class="operator"> =</span><span class="string"> "current_user_2"</span><span class="operator">;
}</span>
</pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_420">Validation d'une instance
C++ (validators)</a></p>
<div class="manual_div_content">
Le module <b><a href="../doxygen/html/group___qx_validator.html"
target="_blank">QxValidator</a></b> de la biblioth<74>que <b>QxOrm</b> permet d'ajouter des
contraintes sur les propri<72>t<EFBFBD>s enregistr<74>es dans le contexte QxOrm.<br>
Ces contraintes sont d<>finies dans la m<>thode de mapping : <i>void
qx::register_class&lt;T&gt;</i>.<br>
Si pour une instance de classe donn<6E>e, au moins une contrainte n'est pas respect<63>e, alors
l'instance est consid<69>r<EFBFBD>e comme invalide : l'objet ne peut alors pas <20>tre sauvegard<72> en base de
donn<6E>es (<i>INSERT</i> ou <i>UPDATE</i>).<br>
<br>
Il est <20>galement possible d'utiliser le module <b>QxValidator</b> pour valider les donn<6E>es au
niveau de la couche pr<70>sentation de l'application : si les donn<6E>es saisies par un utilisateur ne
sont pas valides, un message d'erreur peut <20>tre signal<61>, il n'est alors pas n<>cessaire d'essayer
d'enregistrer l'instance courante en base de donn<6E>es.<br>
Les r<>gles de validation n'ont pas besoin d'<27>tre dupliqu<71>es : elles peuvent <20>tre utilis<69>es aussi
bien par la couche pr<70>sentation que par la couche d'acc<63>s aux donn<6E>es de l'application.<br>
<br>
Voici la description de quelques classes du module <b>QxValidator</b> :
<ul>
<li><a href="../doxygen/html/classqx_1_1_ix_validator.html"
target="_blank">qx::IxValidator</a> : chaque contrainte d<>finie dans la fonction de
mapping <i>void qx::register_class&lt;T&gt;</i> est associ<63>e <20> une interface de type
<i>qx::IxValidator</i> ;
</li>
<li><a href="../doxygen/html/classqx_1_1_ix_validator_x.html"
target="_blank">qx::IxValidatorX</a> : pour une classe donn<6E>e, la liste des contraintes
est associ<63>e <20> une interface de type <i>qx::IxValidatorX</i>. Cette collection peut <20>tre
parcourue <20> l'ex<65>cution du programme : <20>a peut <20>tre int<6E>ressant par exemple pour g<>n<EFBFBD>rer
le sch<63>ma DDL SQL et prendre en compte les contraintes au niveau de la base de donn<6E>es
(voir le chapitre suivant du manuel utilisateur : <a href="#manual_470">G<EFBFBD>n<EFBFBD>rer le sch<63>ma
DDL SQL de la base de donn<6E>es</a>) ;</li>
<li><a href="../doxygen/html/classqx_1_1_qx_invalid_value_x.html"
target="_blank">qx::QxInvalidValueX</a> : au moment du processus de validation,
lorsqu'une instance n'est pas valide, la liste des contraintes non respect<63>es est
repr<70>sent<6E>e par une collection de type <i>qx::QxInvalidValueX</i> ;</li>
<li><a href="../doxygen/html/classqx_1_1_qx_invalid_value.html"
target="_blank">qx::QxInvalidValue</a> : chaque <20>l<EFBFBD>ment de cette collection est de type
<i>qx::QxInvalidValue</i> et contient un message d'erreur (description expliquant pourquoi
l'instance est invalide).
</li>
</ul>
Le module <b>QxValidator</b> g<>re automatiquement la notion d'h<>ritage de classe : si des
contraintes sont d<>finies au niveau de la classe de base, alors elles seront automatiquement
v<>rifi<66>es pour chaque validation d'une classe d<>riv<69>e.<br>
<br>
Voici un exemple d'utilisation du module <b>QxValidator</b> avec une classe '<i>person</i>'
:<br>
<br>
* fichier '<i>person.h</i>' :<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="pre">#ifndef _CLASS_PERSON_H_
#define _CLASS_PERSON_H_
</span><span class="keyword">
class</span> person<span class="operator">
{</span><span class="keyword">
public</span><span class="operator">:</span><span class="keyword">
enum</span> sex<span class="operator"> {</span> male<span class="operator">,</span> female<span class="operator">,</span> unknown<span class="operator"> };</span><span class="type">
long</span> _id<span class="operator">;</span>
QString _firstName<span class="operator">;</span>
QString _lastName<span class="operator">;</span>
QDateTime _birthDate<span class="operator">;</span>
sex _sex<span class="operator">;</span>
person<span class="operator">() :</span> _id<span class="operator">(</span><span class="int">0</span><span class="operator">),</span> _sex<span class="operator">(</span>unknown<span class="operator">) { ; }</span>
person<span class="operator">(</span><span class="type">long</span> id<span class="operator">) :</span> _id<span class="operator">(</span>id<span class="operator">),</span> _sex<span class="operator">(</span>unknown<span class="operator">) { ; }</span><span class="keyword">
virtual</span><span class="operator"> ~</span>person<span class="operator">() { ; }</span><span class="keyword">
private</span><span class="operator">:</span>
<font style="background-color:yellow"><span class="type">void</span> isValid<span class="operator">(</span>qx<span class="operator">::</span>QxInvalidValueX<span class="operator"> &amp;</span> invalidValues<span class="operator">);</span></font>
<span class="operator">};</span>
QX_REGISTER_HPP_MY_EXE<span class="operator">(</span>person<span class="operator">,</span> qx<span class="operator">::</span>trait<span class="operator">::</span>no_base_class_defined<span class="operator">,</span><span class="int"> 0</span><span class="operator">)</span><span class="pre">
#endif // _CLASS_PERSON_H_</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
* fichier '<i>person.cpp</i>' :<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="pre">#include "../include/precompiled.h"
#include "../include/person.h"
#include "../include/global_validator.h"
#include &lt;QxOrm_Impl.h&gt;
</span>
QX_REGISTER_CPP_MY_EXE<span class="operator">(</span>person<span class="operator">)</span><span class="keyword">
namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>person<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span>
t<span class="operator">.</span>id<span class="operator">(&amp;</span> person<span class="operator">::</span>_id<span class="operator">,</span><span class="string"> "id"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>_firstName<span class="operator">,</span><span class="string"> "firstName"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>_lastName<span class="operator">,</span><span class="string"> "lastName"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>_birthDate<span class="operator">,</span><span class="string"> "birthDate"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>_sex<span class="operator">,</span><span class="string"> "sex"</span><span class="operator">);</span>
<font style="background-color:yellow">QxValidatorX<span class="operator">&lt;</span>person<span class="operator">&gt; *</span> pAllValidator<span class="operator"> =</span> t<span class="operator">.</span>getAllValidator<span class="operator">();</span>
pAllValidator<span class="operator">-&gt;</span>add_NotEmpty<span class="operator">(</span><span class="string">"firstName"</span><span class="operator">);</span>
pAllValidator<span class="operator">-&gt;</span>add_NotEmpty<span class="operator">(</span><span class="string">"lastName"</span><span class="operator">,</span><span class="string"> "a person must have a lastname"</span><span class="operator">);</span>
pAllValidator<span class="operator">-&gt;</span>add_CustomValidator<span class="operator">(&amp;</span> person<span class="operator">::</span>isValid<span class="operator">);</span>
pAllValidator<span class="operator">-&gt;</span>add_CustomValidator_QVariant<span class="operator">(&amp;</span> validateFirstName<span class="operator">,</span><span class="string"> "firstName"</span><span class="operator">);</span>
pAllValidator<span class="operator">-&gt;</span>add_CustomValidator_DataType<span class="operator">&lt;</span>QDateTime<span class="operator">&gt;(&amp;</span> validateDateTime<span class="operator">,</span><span class="string"> "birthDate"</span><span class="operator">);</span></font>
<span class="operator">}}</span>
<font style="background-color:yellow"><span class="type">void</span> person<span class="operator">::</span>isValid<span class="operator">(</span>qx<span class="operator">::</span>QxInvalidValueX<span class="operator"> &amp;</span> invalidValues<span class="operator">)</span></font>
<span class="operator">{</span><span class="comment">
// Cette m<>thode est appel<65>e automatiquement par le module 'QxValidator' :
// - avant d'ins<6E>rer ou mettre <20> jour une instance de type 'person' par les fonctions du namespace 'qx::dao' ;
// - en utilisant la fonction 'qx::validate()' avec pour param<61>tre une instance de type 'person'.
// L'enregistrement de la m<>thode 'person::isValid()' est effectu<74> dans la fonction de mapping :
// pAllValidator-&gt;add_CustomValidator(&amp; person::isValid);
// Dans cette m<>thode, il est possible de v<>rifier n'importe quelle valeur de l'instance courante
// Si une propri<72>t<EFBFBD> est non valide, il suffit d'ins<6E>rer un <20>l<EFBFBD>ment dans la collection 'invalidValues'
// Remarque : cette m<>thode est d<>clar<61>e 'private' pour forcer l'utilisateur <20> utiliser la fonction 'qx::validate()'
// Mais ce n'est pas une obligation : cette m<>thode peut <20>tre d<>clar<61>e 'public' ou 'protected'
// Par exemple, si on souhaite v<>rifier la propri<72>t<EFBFBD> '_sex' d'une personne :
</span><span class="flow"> if</span><span class="operator"> ((</span>_sex<span class="operator"> !=</span> male<span class="operator">) &amp;&amp; (</span>_sex<span class="operator"> !=</span> female<span class="operator">))
{</span> invalidValues<span class="operator">.</span>insert<span class="operator">(</span><span class="string">"le sexe de la personne doit <20>tre d<>fini : masculin ou f<>minin"</span><span class="operator">); }
}</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
* fichier '<i>global_validator.h</i>' :<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Les fonctions suivantes ('validateFirstName()' et 'validateDateTime()') sont globales (non li<6C>es <20> une classe)
// Elles peuvent ainsi <20>tre utilis<69>es par plusieurs classes pour valider une propri<72>t<EFBFBD> (par exemple : valider la saisie d'une adresse IP).
// Ces fonctions seront appel<65>es automatiquement par le module 'QxValidator' :
// - avant d'ins<6E>rer ou mettre <20> jour une instance de classe par les fonctions du namespace 'qx::dao' ;
// - en utilisant la fonction 'qx::validate()'.
</span><span class="type">
void</span> validateFirstName<span class="operator">(</span><span class="keyword">const</span> QVariant<span class="operator"> &amp;</span> value<span class="operator">,</span><span class="keyword"> const</span> qx<span class="operator">::</span>IxValidator<span class="operator"> *</span> validator<span class="operator">,</span> qx<span class="operator">::</span>QxInvalidValueX<span class="operator"> &amp;</span> invalidValues<span class="operator">)
{</span><span class="comment">
// Ici, on peut tester la valeur d'une propri<72>t<EFBFBD> (convertie en type QVariant)
// Si la valeur est invalide, il suffit d'ins<6E>rer un message <20> la collection 'invalidValues'
// Par exemple, si la valeur ne doit jamais <20>tre <20>gale <20> "admin" :
</span><span class="flow"> if</span><span class="operator"> (</span>value<span class="operator">.</span>toString<span class="operator">() ==</span><span class="string"> "admin"</span><span class="operator">)
{</span> invalidValues<span class="operator">.</span>insert<span class="operator">(</span><span class="string">"la valeur ne peut pas <20>tre <20>gale <20> 'admin'"</span><span class="operator">); }
}</span><span class="type">
void</span> validateDateTime<span class="operator">(</span><span class="keyword">const</span> QDateTime<span class="operator"> &amp;</span> value<span class="operator">,</span><span class="keyword"> const</span> qx<span class="operator">::</span>IxValidator<span class="operator"> *</span> validator<span class="operator">,</span> qx<span class="operator">::</span>QxInvalidValueX<span class="operator"> &amp;</span> invalidValues<span class="operator">)
{</span><span class="comment">
// Ici, on peut tester la valeur d'une propri<72>t<EFBFBD> (en conservant son vrai type, ici il s'agit de tester une date-heure de type 'QDateTime')
// Si la valeur est invalide, il suffit d'ins<6E>rer un message <20> la collection 'invalidValues'
// Par exemple, si la date-heure doit forc<72>ment <20>tre renseign<67>e :
</span><span class="flow"> if</span><span class="operator"> (!</span> value<span class="operator">.</span>isValid<span class="operator">())
{</span> invalidValues<span class="operator">.</span>insert<span class="operator">(</span><span class="string">"la date-heure doit <20>tre renseign<67>e et doit <20>tre valide"</span><span class="operator">); }
}</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
* fichier '<i>main.cpp</i>' :<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>person personValidate<span class="operator">;</span>
personValidate<span class="operator">.</span>_lastName<span class="operator"> =</span><span class="string"> "admin"</span><span class="operator">;</span>
qx<span class="operator">::</span>QxInvalidValueX invalidValues<span class="operator"> =</span> qx<span class="operator">::</span>validate<span class="operator">(</span>personValidate<span class="operator">);</span>
QString sInvalidValues<span class="operator"> =</span> invalidValues<span class="operator">.</span>text<span class="operator">();</span>
qDebug<span class="operator">(</span><span class="string">"[QxOrm] test 'QxValidator' module :\n%s"</span><span class="operator">,</span> qPrintable<span class="operator">(</span>sInvalidValues<span class="operator">));</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
A l'ex<65>cution de ce bout de code, l'instance '<i>personValidate</i>' est non valide : la
collection '<i>invalidValues</i>' contient quatre <20>l<EFBFBD>ments :<br>
- "<i>la valeur de la propri<72>t<EFBFBD> 'firstName' ne peut pas <20>tre vide</i>" ;<br>
- "<i>le sexe de la personne doit <20>tre d<>fini : masculin ou f<>minin</i>" ;<br>
- "<i>la valeur ne peut pas <20>tre <20>gale <20> 'admin'</i>" ;<br>
- "<i>la date-heure doit <20>tre renseign<67>e et doit <20>tre valide</i>".<br>
<br>
Le module <b>QxValidator</b> fournit plusieurs validateurs pour effectuer des v<>rifications
basiques :
<ul>
<li><i>add_NotNull()</i> : v<>rifie que la valeur n'est pas nulle ;</li>
<li><i>add_NotEmpty()</i> : v<>rifie que la cha<68>ne de caract<63>res n'est pas vide ;</li>
<li><i>add_MinValue()</i> : v<>rifie que la valeur num<75>rique n'est pas inf<6E>rieure au param<61>tre
;</li>
<li><i>add_MaxValue()</i> : v<>rifie que la valeur num<75>rique n'est pas sup<75>rieure au param<61>tre
;</li>
<li><i>add_Range()</i> : v<>rifie que la valeur num<75>rique est comprise entre les deux
param<61>tres ;</li>
<li><i>add_MinDecimal()</i> : v<>rifie que la valeur d<>cimale n'est pas inf<6E>rieure au
param<61>tre ;</li>
<li><i>add_MaxDecimal()</i> : v<>rifie que la valeur d<>cimale n'est pas sup<75>rieure au
param<61>tre ;</li>
<li><i>add_RangeDecimal()</i> : v<>rifie que la valeur d<>cimale est comprise entre les deux
param<61>tres ;</li>
<li><i>add_MinLength()</i> : v<>rifie que la cha<68>ne de caract<63>res a une taille minimale ;</li>
<li><i>add_MaxLength()</i> : v<>rifie que la cha<68>ne de caract<63>res ne d<>passe pas un certain
nombre de caract<63>res ;</li>
<li><i>add_Size()</i> : v<>rifie que la taille de la cha<68>ne de caract<63>res est comprise entre
les deux param<61>tres ;</li>
<li><i>add_DatePast()</i> : v<>rifie que la date-heure est dans le pass<73> ;</li>
<li><i>add_DateFuture()</i> : v<>rifie que la date-heure est dans le futur ;</li>
<li><i>add_RegExp()</i> : v<>rifie que la cha<68>ne de caract<63>res est compatible avec
l'expression r<>guli<6C>re pass<73>e en param<61>tre ;</li>
<li><i>add_EMail()</i> : v<>rifie que la cha<68>ne de caract<63>res correspond <20> un e-mail.</li>
</ul>
Comme dans l'exemple de la classe '<i>person</i>', il est possible de d<>finir <20>galement des
validateurs personnalis<69>s : ce sont des fonctions ou m<>thodes de classe qui seront appel<65>es
automatiquement par le module <b>QxValidator</b> pour valider une propri<72>t<EFBFBD> ou une instance de
classe.<br>
Il existe trois types de validateurs personnalis<69>s :
<ul>
<li><i>add_CustomValidator()</i> : m<>thode de classe, la signature de la m<>thode doit <20>tre
"<i>void my_class::my_method(qx::QxInvalidValueX &)</i>" ;</li>
<li><i>add_CustomValidator_QVariant()</i> : fonction globale avec type <i>QVariant</i>
(propri<72>t<EFBFBD> convertie en <i>QVariant</i>), la signature de la fonction doit <20>tre "<i>void
my_validator(const QVariant &, const qx::IxValidator *, qx::QxInvalidValueX &)</i>" ;
</li>
<li><i>add_CustomValidator_DataType()</i> : fonction globale avec le type r<>el de la
propri<72>t<EFBFBD>, la signature de la fonction doit <20>tre "<i>void my_validator(const T &, const
qx::IxValidator *, qx::QxInvalidValueX &)</i>" ;</li>
</ul>
<b>Remarque :</b> <20> chaque validateur peut <20>tre associ<63> un groupe (param<61>tre optionnel pour
chaque m<>thode <i>add_XXX()</i> de la classe <i>qx::IxValidatorX</i>).<br>
Il est ainsi possible de cr<63>er des groupes de validation suivant le contexte d'ex<65>cution : par
exemple, valider la saisie d'une personne sur une IHM A ne n<>cessite peut-<2D>tre pas les m<>mes
v<>rifications que valider une personne sur une IHM B.<br>
Pour ex<65>cuter la validation d'une instance pour un groupe donn<6E> (par exemple "<i>myGroup</i>"),
il faut appeler la fonction suivante : "<i>qx::QxInvalidValueX invalidValues =
qx::validate(personValidate, "myGroup");</i>".<br>
<br>
<b>Autre remarque :</b> le module <b>QxValidator</b> d<>finit des messages par d<>faut lorsqu'une
contrainte n'est pas v<>rifi<66>e.<br>
Il est possible de red<65>finir ces messages par d<>faut en modifiant la collection suivante :
"<i>QHash<QString, QString> * lstMessage = QxClassX::getAllValidatorMessage();</i>".<br>
Par exemple : "<i>lstMessage->insert("min_value", "la valeur '%NAME%' doit <20>tre inf<6E>rieure ou
<20>gale <20> '%CONSTRAINT%'");</i>".<br>
Les champs <i>%NAME%</i> et <i>%CONSTRAINT%</i> seront automatiquement remplac<61>s par les valeurs
correspondantes.<br>
Pour modifier le message pour un validateur donn<6E> (et non de mani<6E>re globale), il faut utiliser
le param<61>tre optionnel disponible pour les m<>thodes <i>add_XXX()</i> de la classe
<i>qx::IxValidatorX</i>.<br>
<br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_430">G<EFBFBD>rer la valeur NULL de
la base de donn<6E>es</a></p>
<div class="manual_div_content">
Les bases de donn<6E>es poss<73>dent la notion de valeur NULL : pour plus de d<>tails sur la valeur
NULL, <a href="https://en.wikipedia.org/wiki/Null_%28SQL%29" target="_blank">rendez-vous sur la
page Wikipedia</a>.<br>
La biblioth<74>que QxOrm permet de g<>rer la valeur NULL de plusieurs fa<66>ons diff<66>rentes :
<ul>
<li>utilisation de la classe <a href="#manual_4300">boost::optional</a> fournie par boost ;
</li>
<li>utilisation de la classe <a href="#manual_4310">QVariant</a> fournie par Qt ;</li>
<li>utilisation de pointeurs ou pointeurs intelligents : un pointeur NULL est associ<63> <20> la
valeur NULL en base de donn<6E>es.</li>
</ul>
<br>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_4300">boost::optional</a>
</p>
<div class="manual_div_content">
La classe <a href="http://www.boost.org/doc/libs/release/libs/optional/doc/html/index.html"
target="_blank">boost::optional&lt;T&gt;</a> fournie par boost est particuli<6C>rement
adapt<70>e pour g<>rer la notion de valeur NULL en base de donn<6E>es.<br>
Pour utiliser <a
href="http://www.boost.org/doc/libs/release/libs/optional/doc/html/index.html"
target="_blank">boost::optional&lt;T&gt;</a> avec la biblioth<74>que QxOrm, il est n<>cessaire
de d<>finir l'option de compilation <b><i>_QX_ENABLE_BOOST</i></b>, ou bien d'inclure
l'en-t<>te <b><i>&lt;QxExtras/QxBoostOptionalOnly.h&gt;</i></b>.<br>
Voici un exemple de classe dont toutes les propri<72>t<EFBFBD>s (sauf la cl<63> primaire) peuvent <20>tre
NULL en utilisant <a
href="http://www.boost.org/doc/libs/release/libs/optional/doc/html/index.html"
target="_blank">boost::optional</a> :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="person.h">
<pre><span class="pre">#ifndef _PERSON_H_
#define _PERSON_H_
</span><span class="keyword">
class</span> person<span class="operator">
{</span><span class="keyword">
public</span><span class="operator">:</span><span class="type">
long</span> id<span class="operator">;</span>
<font style="background-color:yellow">boost::optional&lt;QString&gt; firstName<span class="operator">;</span></font>
<font style="background-color:yellow">boost::optional&lt;QString&gt; lastName<span class="operator">;</span></font>
<font style="background-color:yellow">boost::optional&lt;QDateTime&gt; birthDate<span class="operator">;</span></font>
person<span class="operator">() :</span> id<span class="operator">(</span><span class="int">0</span><span class="operator">) { ; }</span><span class="keyword">
virtual</span><span class="operator"> ~</span>person<span class="operator">() { ; }
};</span>
#endif <span class="comment">// _PERSON_H_</span></span></pre>
</td>
</tr>
</tbody>
</table>
<br>
La classe <a href="http://www.boost.org/doc/libs/release/libs/optional/doc/html/index.html"
target="_blank">boost::optional&lt;T&gt;</a> se manipule facilement : rendez-vous sur <a
href="http://www.boost.org/doc/libs/release/libs/optional/doc/html/index.html"
target="_blank">la documentation fournie par boost</a> pour plus de d<>tails.
<br><br>
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_4310">QVariant</a></p>
<div class="manual_div_content">
La classe <a href="http://doc.qt.io/qt-5/qvariant.html" target="_blank">QVariant</a> fournie
par Qt permet <20>galement de g<>rer la notion de valeur NULL en base de donn<6E>es.<br>
Voici un exemple de classe dont toutes les propri<72>t<EFBFBD>s (sauf la cl<63> primaire) peuvent <20>tre
NULL en utilisant <a href="http://doc.qt.io/qt-5/qvariant.html" target="_blank">QVariant</a>
:<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="person.h">
<pre><span class="pre">#ifndef _PERSON_H_
#define _PERSON_H_
</span><span class="keyword">
class</span> person<span class="operator">
{</span><span class="keyword">
public</span><span class="operator">:</span><span class="type">
long</span> id<span class="operator">;</span>
<font style="background-color:yellow">QVariant firstName<span class="operator">;</span></font>
<font style="background-color:yellow">QVariant lastName<span class="operator">;</span></font>
<font style="background-color:yellow">QVariant birthDate<span class="operator">;</span></font>
person<span class="operator">() :</span> id<span class="operator">(</span><span class="int">0</span><span class="operator">) { ; }</span><span class="keyword">
virtual</span><span class="operator"> ~</span>person<span class="operator">() { ; }
};</span>
#endif <span class="comment">// _PERSON_H_</span></span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Cette solution a pour d<>savantage de perdre le type de donn<6E>e compar<61> <20> <a
href="http://www.boost.org/doc/libs/release/libs/optional/doc/html/index.html"
target="_blank">boost::optional&lt;T&gt;</a>.<br>
Il est donc recommand<6E> d'utiliser <a
href="http://www.boost.org/doc/libs/release/libs/optional/doc/html/index.html"
target="_blank">boost::optional&lt;T&gt;</a> pour g<>rer la valeur NULL avec la
biblioth<74>que QxOrm.
<br><br>
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_440">H<EFBFBD>ritage et
polymorphisme</a></p>
<div class="manual_div_content">
On retrouve g<>n<EFBFBD>ralement dans les diff<66>rents outils de type <i>ORM</i> trois diff<66>rentes
strat<61>gies pour g<>rer la notion d'h<>ritage avec la base de donn<6E>es :
<ul>
<li><i><a href="http://martinfowler.com/eaaCatalog/singleTableInheritance.html"
target="_blank">Single Table Inheritance</a></i> (une seule table regroupant toutes
les propri<72>t<EFBFBD>s d'une hi<68>rarchie d'h<>ritage de classes) ;
</li>
<li><i><a href="http://martinfowler.com/eaaCatalog/classTableInheritance.html"
target="_blank">Class Table Inheritance</a></i> (<28> chaque classe d'une hi<68>rarchie
d'h<>ritage est associ<63>e une table dans la base de donn<6E>es) ;
</li>
<li><i><a href="http://martinfowler.com/eaaCatalog/concreteTableInheritance.html"
target="_blank">Concrete Table Inheritance</a></i> (une table par classe concr<63>te
dans la hi<68>rarchie d'h<>ritage).
</li>
</ul>
<b>QxOrm</b> utilise par d<>faut la strat<61>gie <i>Concrete Table Inheritance</i> (les autres
strat<61>gies ne sont pas fonctionnelles <20> l'heure actuelle).<br>
De nombreux tutoriaux et forums sont disponibles sur internet pour plus de d<>tails sur cette
notion d'h<>ritage.<br>
Un exemple d'utilisation avec une classe de base se trouve dans le dossier
<i>./test/qxDllSample/dll2/</i> avec la classe <i>BaseClassTrigger</i>.
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_450">Interface
qx::IxPersistable (classe abstraite)</a></p>
<div class="manual_div_content">
L'interface <i><a href="../doxygen/html/classqx_1_1_ix_persistable.html"
target="_blank">qx::IxPersistable</a></i> (ou classe abstraite) dispose uniquement de
m<>thodes virtuelles pures.<br>
Elle permet d'avoir une classe de base commune pour appeler les fonctions de persistance sans
conna<6E>tre le type r<>el de l'instance courante (notion de polymorphisme).<br>
La biblioth<74>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'<27>crire des algorithmes g<>n<EFBFBD>riques.<br>
<br>
La classe <i>qx::IxPersistable</i> met <20> disposition les m<>thodes virtuelles suivantes (pour
plus d'informations sur ces m<>thodes, rendez-vous sur la <a href="../doxygen/index.html"
target="_blank">documentation en ligne de la biblioth<74>que QxOrm</a>) :<br>
<br>
<div style="width:900px; height:290px; overflow:auto; background-color:white">
<pre><span class="keyword">virtual</span><span class="type"> long</span> qxCount<span class="operator">(</span><span class="keyword">const</span> qx<span class="operator">::</span>QxSqlQuery<span class="operator"> &amp;</span> query<span class="operator"> =</span> qx<span class="operator">::</span>QxSqlQuery<span class="operator">(),</span> QSqlDatabase<span class="operator"> *</span> pDatabase<span class="operator"> =</span> NULL<span class="operator">);</span><span class="keyword">
virtual</span> QSqlError qxFetchById<span class="operator">(</span><span class="keyword">const</span> QVariant<span class="operator"> &amp;</span> id<span class="operator"> =</span> QVariant<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relation<span class="operator"> =</span> QStringList<span class="operator">(),</span> QSqlDatabase<span class="operator"> *</span> pDatabase<span class="operator"> =</span> NULL<span class="operator">);</span><span class="keyword">
virtual</span> QSqlError qxFetchAll<span class="operator">(</span>qx<span class="operator">::</span>IxCollection<span class="operator"> &amp;</span> list<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relation<span class="operator"> =</span> QStringList<span class="operator">(),</span> QSqlDatabase<span class="operator"> *</span> pDatabase<span class="operator"> =</span> NULL<span class="operator">);</span><span class="keyword">
virtual</span> QSqlError qxFetchByQuery<span class="operator">(</span><span class="keyword">const</span> qx<span class="operator">::</span>QxSqlQuery<span class="operator"> &amp;</span> query<span class="operator">,</span> qx<span class="operator">::</span>IxCollection<span class="operator"> &amp;</span> list<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relation<span class="operator"> =</span> QStringList<span class="operator">(),</span> QSqlDatabase<span class="operator"> *</span> pDatabase<span class="operator"> =</span> NULL<span class="operator">);</span><span class="keyword">
virtual</span> QSqlError qxInsert<span class="operator">(</span><span class="keyword">const</span> QStringList<span class="operator"> &amp;</span> relation<span class="operator"> =</span> QStringList<span class="operator">(),</span> QSqlDatabase<span class="operator"> *</span> pDatabase<span class="operator"> =</span> NULL<span class="operator">);</span><span class="keyword">
virtual</span> QSqlError qxUpdate<span class="operator">(</span><span class="keyword">const</span> qx<span class="operator">::</span>QxSqlQuery<span class="operator"> &amp;</span> query<span class="operator"> =</span> qx<span class="operator">::</span>QxSqlQuery<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relation<span class="operator"> =</span> QStringList<span class="operator">(),</span> QSqlDatabase<span class="operator"> *</span> pDatabase<span class="operator"> =</span> NULL<span class="operator">);</span><span class="keyword">
virtual</span> QSqlError qxSave<span class="operator">(</span><span class="keyword">const</span> QStringList<span class="operator"> &amp;</span> relation<span class="operator"> =</span> QStringList<span class="operator">(),</span> QSqlDatabase<span class="operator"> *</span> pDatabase<span class="operator"> =</span> NULL<span class="operator">);</span><span class="keyword">
virtual</span> QSqlError qxDeleteById<span class="operator">(</span><span class="keyword">const</span> QVariant<span class="operator"> &amp;</span> id<span class="operator"> =</span> QVariant<span class="operator">(),</span> QSqlDatabase<span class="operator"> *</span> pDatabase<span class="operator"> =</span> NULL<span class="operator">);</span><span class="keyword">
virtual</span> QSqlError qxDeleteAll<span class="operator">(</span>QSqlDatabase<span class="operator"> *</span> pDatabase<span class="operator"> =</span> NULL<span class="operator">);</span><span class="keyword">
virtual</span> QSqlError qxDeleteByQuery<span class="operator">(</span><span class="keyword">const</span> qx<span class="operator">::</span>QxSqlQuery<span class="operator"> &amp;</span> query<span class="operator">,</span> QSqlDatabase<span class="operator"> *</span> pDatabase<span class="operator"> =</span> NULL<span class="operator">);</span><span class="keyword">
virtual</span> QSqlError qxDestroyById<span class="operator">(</span><span class="keyword">const</span> QVariant<span class="operator"> &amp;</span> id<span class="operator"> =</span> QVariant<span class="operator">(),</span> QSqlDatabase<span class="operator"> *</span> pDatabase<span class="operator"> =</span> NULL<span class="operator">);</span><span class="keyword">
virtual</span> QSqlError qxDestroyAll<span class="operator">(</span>QSqlDatabase<span class="operator"> *</span> pDatabase<span class="operator"> =</span> NULL<span class="operator">);</span><span class="keyword">
virtual</span> QSqlError qxDestroyByQuery<span class="operator">(</span><span class="keyword">const</span> qx<span class="operator">::</span>QxSqlQuery<span class="operator"> &amp;</span> query<span class="operator">,</span> QSqlDatabase<span class="operator"> *</span> pDatabase<span class="operator"> =</span> NULL<span class="operator">);</span><span class="keyword">
virtual</span> qx_bool qxExist<span class="operator">(</span><span class="keyword">const</span> QVariant<span class="operator"> &amp;</span> id<span class="operator"> =</span> QVariant<span class="operator">(),</span> QSqlDatabase<span class="operator"> *</span> pDatabase<span class="operator"> =</span> NULL<span class="operator">);</span><span class="keyword">
virtual</span> qx<span class="operator">::</span>QxInvalidValueX qxValidate<span class="operator">(</span><span class="keyword">const</span> QStringList<span class="operator"> &amp;</span> groups<span class="operator"> =</span> QStringList<span class="operator">());</span><span class="keyword">
virtual</span> qx<span class="operator">::</span>IxPersistableCollection_ptr qxNewPersistableCollection<span class="operator">()</span><span class="keyword"> const</span><span class="operator">;</span><span class="keyword">
virtual</span> qx<span class="operator">::</span>IxClass<span class="operator"> *</span> qxClass<span class="operator">()</span><span class="keyword"> const</span><span class="operator">;</span></pre>
</div>
<br>
Par exemple, <20> partir d'une liste de pointeurs de type <i>qx::IxPersistable</i>, il est possible
d'enregistrer les <20>l<EFBFBD>ments dans plusieurs tables diff<66>rentes de la base de donn<6E>es de la fa<66>on
suivante :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>QList<span class="operator">&lt;</span>qx<span class="operator">::</span>IxPersistable<span class="operator"> *&gt;</span> lst<span class="operator"> = ...;</span>
foreach<span class="operator">(</span>qx<span class="operator">::</span>IxPersistable<span class="operator"> *</span> p<span class="operator">,</span> lst<span class="operator">)
{</span>
QSqlError daoError<span class="operator"> =</span> p<span class="operator">-&gt;</span>qxSave<span class="operator">();</span><span class="flow">
if</span><span class="operator"> (</span>daoError<span class="operator">.</span>isValid<span class="operator">()) {</span><span class="comment"> /* an error occured */</span><span class="operator"> }</span><span class="comment">
// etc...
</span><span class="operator">}</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Pour impl<70>menter l'interface <i>qx::IxPersistable</i>, il faut :
<ul>
<li>faire h<>riter la classe persistante du type <i>qx::IxPersistable</i> ;</li>
<li>dans la d<>finition de la classe (<i>myClass.h</i> par exemple), ajouter la macro
<i>QX_PERSISTABLE_HPP(myClass)</i> ;
</li>
<li>dans l'impl<70>mentation de la classe (<i>myClass.cpp</i> par exemple), ajouter la macro
<i>QX_PERSISTABLE_CPP(myClass)</i>.
</li>
</ul>
Par exemple, impl<70>menter l'interface <i>qx::IxPersistable</i> pour la classe <a
href="./tutorial.html#tuto_6"><i>author</i></a> du tutoriel <i>qxBlog</i> revient <20> <20>crire
(les modifications par rapport au code du tutoriel apparaissent en gras) :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="author.h">
<pre><span class="pre">#ifndef _QX_BLOG_AUTHOR_H_
#define _QX_BLOG_AUTHOR_H_
</span><span class="keyword">
class</span> blog<span class="operator">;</span><span class="keyword">
class</span> QX_BLOG_DLL_EXPORT author : <font style="background-color:yellow"><b>public qx::IxPersistable</b></font><span class="operator">
{</span>
<font style="background-color:yellow"><b>QX_PERSISTABLE_HPP(author)</b></font>
<span class="keyword">public</span><span class="operator">:</span><span class="comment">
// -- typedef
</span><span class="keyword"> typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>blog<span class="operator">&gt;</span> blog_ptr<span class="operator">;</span><span class="keyword">
typedef</span> std<span class="operator">::</span>vector<span class="operator">&lt;</span>blog_ptr<span class="operator">&gt;</span> list_blog<span class="operator">;</span><span class="comment">
// -- enum
</span><span class="keyword"> enum</span> enum_sex<span class="operator"> {</span> male<span class="operator">,</span> female<span class="operator">,</span> unknown<span class="operator"> };</span><span class="comment">
// -- propri<72>t<EFBFBD>s
</span> QString m_id<span class="operator">;</span>
QString m_name<span class="operator">;</span>
QDate m_birthdate<span class="operator">;</span>
enum_sex m_sex<span class="operator">;</span>
list_blog m_blogX<span class="operator">;</span><span class="comment">
// -- constructeur, destructeur virtuel
</span> author<span class="operator">() :</span> m_id<span class="operator">(</span><span class="int">0</span><span class="operator">),</span> m_sex<span class="operator">(</span>unknown<span class="operator">) { ; }</span><span class="keyword">
virtual</span><span class="operator"> ~</span>author<span class="operator">() { ; }</span><span class="comment">
// -- m<>thodes
</span><span class="type"> int</span> age<span class="operator">()</span><span class="keyword"> const</span><span class="operator">;
};</span>
QX_REGISTER_PRIMARY_KEY<span class="operator">(</span>author<span class="operator">,</span> QString<span class="operator">)</span>
QX_REGISTER_HPP_QX_BLOG<span class="operator">(</span>author<span class="operator">,</span> qx<span class="operator">::</span>trait<span class="operator">::</span>no_base_class_defined<span class="operator">,</span><span class="int"> 0</span><span class="operator">)</span><span class="keyword">
typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>author<span class="operator">&gt;</span> author_ptr<span class="operator">;</span><span class="keyword">
typedef</span> qx<span class="operator">::</span>QxCollection<span class="operator">&lt;</span>QString<span class="operator">,</span> author_ptr<span class="operator">&gt;</span> list_author<span class="operator">;</span><span class="pre">
#endif <span class="comment">// _QX_BLOG_AUTHOR_H_</span>
</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="author.cpp">
<pre><span class="pre">#include <span class="string">"../include/precompiled.h"</span>
#include <span class="string">"../include/author.h"</span>
#include <span class="string">"../include/blog.h"</span>
#include <span class="string">&lt;QxOrm_Impl.h&gt;</span>
</span>
QX_REGISTER_CPP_QX_BLOG<span class="operator">(</span>author<span class="operator">)</span>
<font style="background-color:yellow"><b>QX_PERSISTABLE_CPP(author)</b></font>
<span class="keyword">namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>author<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span>
t<span class="operator">.</span>id<span class="operator">(&amp;</span> author<span class="operator">::</span>m_id<span class="operator">,</span><span class="string"> "author_id"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> author<span class="operator">::</span>m_name<span class="operator">,</span><span class="string"> "name"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> author<span class="operator">::</span>m_birthdate<span class="operator">,</span><span class="string"> "birthdate"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> author<span class="operator">::</span>m_sex<span class="operator">,</span><span class="string"> "sex"</span><span class="operator">);</span>
t<span class="operator">.</span>relationOneToMany<span class="operator">(&amp;</span> author<span class="operator">::</span>m_blogX<span class="operator">,</span><span class="string"> "list_blog"</span><span class="operator">,</span><span class="string"> "author_id"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">int</span><span class="operator">&gt;(&amp;</span> author<span class="operator">::</span>age<span class="operator">,</span><span class="string"> "age"</span><span class="operator">);
}}</span><span class="type">
int</span> author<span class="operator">::</span>age<span class="operator">()</span><span class="keyword"> const</span><span class="operator">
{</span><span class="flow">
if</span><span class="operator"> (!</span> m_birthdate<span class="operator">.</span>isValid<span class="operator">()) {</span><span class="flow"> return</span><span class="operator"> -</span><span class="int">1</span><span class="operator">; }</span><span class="flow">
return</span><span class="operator"> (</span>QDate<span class="operator">::</span>currentDate<span class="operator">().</span>year<span class="operator">() -</span> m_birthdate<span class="operator">.</span>year<span class="operator">());
}</span>
</pre>
</td>
</tr>
</tbody>
</table>
<br>
<b>Remarque :</b> le projet de test <i>./test/qxDllSample/dll1/</i> met <20> disposition une sorte
de 'super classe de base' : la classe <i>qx::QxPersistable</i> impl<70>mente l'interface <i><a
href="../doxygen/html/classqx_1_1_ix_persistable.html"
target="_blank">qx::IxPersistable</a></i> et h<>rite de <i>QObject</i>.<br>
Le m<>canisme <a href="http://doc.qt.io/qt-5/signalsandslots.html"
target="_blank"><i>SIGNAL-SLOT</i></a> de Qt peut donc <20>tre utilis<69> avec cette classe, ce qui
peut <20>tre int<6E>ressant par exemple pour la notion de d<>clencheurs (ou <a
href="#manual_410"><i>trigger</i></a>).<br>
La classe <i>qx::QxPersistable</i> met <20>galement <20> disposition des m<>thodes virtuelles qu'il est
possible de surcharger pour g<>rer notamment la notion de validation des donn<6E>es avec le module
<a href="#manual_420"><i>QxValidator</i></a>.<br>
La classe <i>qx::QxPersistable</i> 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<69>s :
<ul>
<li>acc<EFBFBD>der au fichier <a href="./resource/qx_persistable_hpp.html"
target="_blank"><i>QxPersistable.hpp</i></a> ;</li>
<li>acc<EFBFBD>der au fichier <a href="./resource/qx_persistable_cpp.html"
target="_blank"><i>QxPersistable.cpp</i></a>.</li>
</ul>
<br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_455">Utiliser le pattern C++
PIMPL (Private Implementation idiom ou d-pointer)</a></p>
<div class="manual_div_content">
<a href="https://en.cppreference.com/w/cpp/language/pimpl" target="_blank">D<EFBFBD>finition du site
cppreference :</a> "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.
<br><br>
<a href="https://dzone.com/articles/the-pimpl-pattern-what-you-should-know" target="_blank">Les
avantages <20> utiliser le pattern PIMPL</a> pour d<>finir une classe persistente enregistr<74>e
dans le contexte QxOrm :
<ul>
<li><b>Compilation Firewall</b> : si l'impl<70>mentation priv<69>e change, le code client n'a pas
besoin d'<27>tre recompil<69> ;</li>
<li>R<EFBFBD>duction des <b>temps de compilation</b> : les fichiers d'en-t<>tes (*.h, *.hpp) sont
moins volumineux ;</li>
<li><b>Compatibilit<EFBFBD> binaire</b> : vous pouvez d<>velopper plusieurs versions d'une
biblioth<74>que sans casser la compatibilit<69> ;</li>
<li>R<EFBFBD>duction de la <b>taille des ex<65>cutables</b> g<>n<EFBFBD>r<EFBFBD>s.</li>
</ul>
<a href="https://www.geeksforgeeks.org/pimpl-idiom-in-c-with-examples/" target="_blank">Les
inconvients du pattern PIMPL</a> :
<ul>
<li>L<EFBFBD>g<EFBFBD>re baisse des performances : n<>cessit<69> d'utiliser un niveau d'indirection
suppl<70>mentaire avec le pointeur opaque ;</li>
<li>N<EFBFBD>cessit<EFBFBD> d'allouer un pointeur par instance (peut causer des probl<62>mes de <i>memory
fragmentation</i>).</li>
</ul>
<br>
La biblioth<74>que QxOrm fournit un projet de test o<> toutes les classes persistantes sont cod<6F>es
en utilisant le pattern PIMPL : <a
href="https://github.com/QxOrm/QxOrm/tree/master/test/qxBlogPImpl"
target="_blank"><i>qxBlogPImpl</i> (avec gestion des relations)</a>.<br>
<font style="background-color:yellow">Il est <20>galement possible d'utiliser <a
href="./manual_qxee.html#cpp_export_settings_parameters">l'application
<b>QxEntityEditor</b></a></font> pour g<>n<EFBFBD>rer facilement et automatiquement toutes les
classes persistantes d'un projet C++ avec <b>l'option PIMPL</b>.<br>
<br>
Exemple de classe persistante C++ enregistr<74>e dans le contexte QxOrm en utilisant le pattern
PIMPL (avec relations <i>1-n</i>, <i>n-1</i> et <i>n-n</i>) :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="pre">#ifndef _QX_BLOG_BLOG_H_
#define _QX_BLOG_BLOG_H_</span>
<span class="keyword">class</span> author;
<span class="keyword">class</span> comment;
<span class="keyword">class</span> category;
<span class="keyword">class</span> QX_BLOG_DLL_EXPORT blog
{
QX_REGISTER_FRIEND_CLASS(blog)
<font style="background-color:yellow">private:
<span class="keyword">struct</span> blog_impl;
std::unique_ptr&lt;blog_impl&gt; m_pImpl; <span class="comment">//!&lt; Private implementation idiom</span></font>
<span class="keyword">public</span>:
blog();
virtual ~blog();
<font style="background-color:yellow">blog(<span class="keyword">const</span> blog & other);
blog & operator=(<span class="keyword">const</span> blog & other);
#ifdef Q_COMPILER_RVALUE_REFS
blog(blog && other) Q_DECL_NOEXCEPT;
blog & operator=(blog && other) Q_DECL_NOEXCEPT;
#endif <span class="comment">// Q_COMPILER_RVALUE_REFS</span></font>
<span class="type">long</span> id() <span class="keyword">const</span>;
<span class="type">QString</span> text() <span class="keyword">const</span>;
<span class="type">QDateTime</span> dateCreation() <span class="keyword">const</span>;
<span class="type">void</span> setId(long l);
<span class="type">void</span> setText(<span class="keyword">const</span> QString & s);
<span class="type">void</span> setDateCreation(<span class="keyword">const</span> QDateTime & d);
std::shared_ptr&lt;author&gt; & getAuthor();
QList&lt; std::shared_ptr&lt;comment&gt; &gt; & listOfComments();
qx::QxCollection&lt;long, QSharedPointer&lt;category&gt; &gt; & listOfCategories();
};
QX_REGISTER_HPP_QX_BLOG(blog, qx::trait::no_base_class_defined, 0)
<span class="keyword">typedef</span> std::shared_ptr&lt;blog&gt; blog_ptr;
<span class="keyword">typedef</span> std::vector&lt;blog_ptr&gt; list_blog;
<span class="pre">#endif <span class="comment">// _QX_BLOG_BLOG_H_</span></span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="pre">#include <span class="string">"../include/precompiled.h"</span>
#include <span class="string">"../include/blog.h"</span>
#include <span class="string">"../include/author.h"</span>
#include <span class="string">"../include/comment.h"</span>
#include <span class="string">"../include/category.h"</span>
#include <span class="string">&lt;QxOrm_Impl.h&gt;</span></span>
QX_REGISTER_CPP_QX_BLOG(blog)
<font style="background-color:yellow"><span class="keyword">struct</span> Q_DECL_HIDDEN blog::blog_impl
{
<span class="type">long</span> m_id;
<span class="type">QString</span> m_text;
<span class="type">QDateTime</span> m_dt_creation;
author_ptr m_author;
list_comment m_commentX;
list_category m_categoryX;
blog_impl() : m_id(0) { ; }
~blog_impl() { ; }
};</font>
<span class="keyword">namespace</span> qx {
<span class="keyword">template</span> &lt;&gt; <span class="keyword">void</span> register_class(QxClass&lt;blog&gt; & t)
{
<font style="background-color:yellow">IxDataMember * pImpl = t.pimpl(& blog::m_pImpl);</font>
t.id(<font style="background-color:yellow">& blog::blog_impl::m_id</font>, <span class="string">"blog_id"</span>, 0, <font style="background-color:yellow">pImpl</font>);
t.data(<font style="background-color:yellow">& blog::blog_impl::m_text</font>, <span class="string">"blog_text"</span>, 0, true, true, <font style="background-color:yellow">pImpl</font>);
t.data(<font style="background-color:yellow">& blog::blog_impl::m_dt_creation</font>, <span class="string">"date_creation"</span>, 0, true, true, <font style="background-color:yellow">pImpl</font>);
t.relationManyToOne(<font style="background-color:yellow">& blog::blog_impl::m_author</font>, <span class="string">"author_id"</span>, 0, <font style="background-color:yellow">pImpl</font>);
t.relationOneToMany(<font style="background-color:yellow">& blog::blog_impl::m_commentX</font>, <span class="string">"list_comment"</span>, <span class="string">"blog_id"</span>, 0, <font style="background-color:yellow">pImpl</font>);
t.relationManyToMany(<font style="background-color:yellow">& blog::blog_impl::m_categoryX</font>, <span class="string">"list_category"</span>, <span class="string">"category_blog"</span>, <span class="string">"blog_id"</span>, <span class="string">"category_id"</span>, 0, <font style="background-color:yellow">pImpl</font>);
}}
blog::blog() : <font style="background-color:yellow">m_pImpl(new blog_impl())</font> { ; }
blog::~blog() { ; }
<font style="background-color:yellow">blog::blog(<span class="keyword">const</span> blog & other) : m_pImpl(new blog_impl(* other.m_pImpl)) { ; }
blog & blog::operator=(<span class="keyword">const</span> blog & other)
{
if (this != (& other)) { (* m_pImpl) = (* other.m_pImpl); }
<span class="keyword">return</span> (* this);
}
#ifdef Q_COMPILER_RVALUE_REFS
blog::blog(blog && other) Q_DECL_NOEXCEPT : m_pImpl(std::move(other.m_pImpl)) { ; }
blog & blog::operator=(blog && other) Q_DECL_NOEXCEPT { if (this != (& other)) { m_pImpl = std::move(other.m_pImpl); }; <span class="keyword">return</span> (* this); }
#endif <span class="comment">// Q_COMPILER_RVALUE_REFS</span></font>
<span class="type">long</span> blog::id() <span class="keyword">const</span> { <span class="keyword">return</span> <font style="background-color:yellow">m_pImpl-&gt;m_id</font>; }
<span class="type">QString</span> blog::text() <span class="keyword">const</span> { <span class="keyword">return</span> <font style="background-color:yellow">m_pImpl-&gt;m_text</font>; }
<span class="type">QDateTime</span> blog::dateCreation() <span class="keyword">const</span> { <span class="keyword">return</span> <font style="background-color:yellow">m_pImpl-&gt;m_dt_creation</font>; }
<span class="keyword">void</span> blog::setId(<span class="type">long</span> l) { <font style="background-color:yellow">m_pImpl-&gt;m_id</font> = l; }
<span class="keyword">void</span> blog::setText(<span class="keyword">const</span> <span class="type">QString</span> & s) { <font style="background-color:yellow">m_pImpl-&gt;m_text</font> = s; }
<span class="keyword">void</span> blog::setDateCreation(<span class="keyword">const</span> <span class="type">QDateTime</span> & d) { <font style="background-color:yellow">m_pImpl-&gt;m_dt_creation</font> = d; }
std::shared_ptr&lt;author&gt; & blog::getAuthor() { <span class="keyword">return</span> <font style="background-color:yellow">m_pImpl-&gt;m_author</font>; }
QList&lt; std::shared_ptr&lt;comment&gt; &gt; & blog::listOfComments() { <span class="keyword">return</span> <font style="background-color:yellow">m_pImpl-&gt;m_commentX</font>; }
qx::QxCollection&lt;long, QSharedPointer&lt;category&gt; &gt; & blog::listOfCategories() { <span class="keyword">return</span> <font style="background-color:yellow">m_pImpl-&gt;m_categoryX</font>; }</pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_460">Persister des types
personnalis<69>s</a></p>
<div class="manual_div_content">
La biblioth<74>que QxOrm permet de persister n'importe quel type, m<>me si ce dernier n'est pas
enregistr<74> dans le contexte QxOrm par la m<>thode <i>qx::register_class&lt;T&gt;()</i>.<br>
<br>
Il est n<>cessaire d'<27>crire les fonctions de s<>rialisation de la biblioth<74>que boost, en utilisant
la m<>thode <b>non intrusive</b> (puisque le code source n'est pas disponible ou ne peut pas <20>tre
modifi<66>).
Pour plus d'informations sur la s<>rialisation des donn<6E>es avec la biblioth<74>que boost,
rendez-vous sur <a href="http://khayyam.developpez.com/articles/cpp/boost/serialization/"
target="_blank">le tutoriel de developpez.com</a>.<br>
<br>
Par exemple, imaginons une classe '<i>ExtObject3D</i>' provenant d'une biblioth<74>que tierce et
dont le code source n'est pas disponible ou ne peut pas <20>tre modifi<66>.
Voici le code n<>cessaire pour pouvoir persister une instance de type '<i>ExtObject3D</i>' en
base de donn<6E>es :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="pre">#ifndef _PERSIST_EXTOBJECT3D_H_
#define _PERSIST_EXTOBJECT3D_H_
#include "ExtObject3D.h"
#include &lt;boost/serialization/serialization.hpp&gt;
#include &lt;boost/serialization/split_free.hpp&gt;
#include &lt;boost/serialization/nvp.hpp&gt;
</span><span class="keyword">
namespace</span> boost<span class="operator"> {</span><span class="keyword">
namespace</span> serialization<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;</span><span class="keyword">class</span> Archive<span class="operator">&gt;</span><span class="type">
void</span> save<span class="operator">(</span>Archive<span class="operator"> &amp;</span> ar<span class="operator">,</span><span class="keyword"> const</span> ExtObject3D<span class="operator"> &amp;</span> t<span class="operator">,</span><span class="type"> unsigned int</span> version<span class="operator">)
{</span>
Q_UNUSED<span class="operator">(</span>version<span class="operator">);</span><span class="type">
double</span> x<span class="operator">(</span>t<span class="operator">.</span>getX<span class="operator">()),</span> y<span class="operator">(</span>t<span class="operator">.</span>getY<span class="operator">()),</span> z<span class="operator">(</span>t<span class="operator">.</span>getZ<span class="operator">()),</span> angle<span class="operator">(</span>t<span class="operator">.</span>getAngle<span class="operator">());</span>
ar<span class="operator"> &lt;&lt;</span> boost<span class="operator">::</span>serialization<span class="operator">::</span>make_nvp<span class="operator">(</span><span class="string">"x"</span><span class="operator">,</span> x<span class="operator">);</span>
ar<span class="operator"> &lt;&lt;</span> boost<span class="operator">::</span>serialization<span class="operator">::</span>make_nvp<span class="operator">(</span><span class="string">"y"</span><span class="operator">,</span> y<span class="operator">);</span>
ar<span class="operator"> &lt;&lt;</span> boost<span class="operator">::</span>serialization<span class="operator">::</span>make_nvp<span class="operator">(</span><span class="string">"z"</span><span class="operator">,</span> z<span class="operator">);</span>
ar<span class="operator"> &lt;&lt;</span> boost<span class="operator">::</span>serialization<span class="operator">::</span>make_nvp<span class="operator">(</span><span class="string">"angle"</span><span class="operator">,</span> angle<span class="operator">);
}</span><span class="keyword">
template</span><span class="operator"> &lt;</span><span class="keyword">class</span> Archive<span class="operator">&gt;</span><span class="type">
void</span> load<span class="operator">(</span>Archive<span class="operator"> &amp;</span> ar<span class="operator">,</span> ExtObject3D<span class="operator"> &amp;</span> t<span class="operator">,</span><span class="type"> unsigned int</span> version<span class="operator">)
{</span>
Q_UNUSED<span class="operator">(</span>version<span class="operator">);</span><span class="type">
double</span> x<span class="operator">(</span><span class="float">0.0</span><span class="operator">),</span> y<span class="operator">(</span><span class="float">0.0</span><span class="operator">),</span> z<span class="operator">(</span><span class="float">0.0</span><span class="operator">),</span> angle<span class="operator">(</span><span class="float">0.0</span><span class="operator">);</span>
ar<span class="operator"> &gt;&gt;</span> boost<span class="operator">::</span>serialization<span class="operator">::</span>make_nvp<span class="operator">(</span><span class="string">"x"</span><span class="operator">,</span> x<span class="operator">);</span>
ar<span class="operator"> &gt;&gt;</span> boost<span class="operator">::</span>serialization<span class="operator">::</span>make_nvp<span class="operator">(</span><span class="string">"y"</span><span class="operator">,</span> y<span class="operator">);</span>
ar<span class="operator"> &gt;&gt;</span> boost<span class="operator">::</span>serialization<span class="operator">::</span>make_nvp<span class="operator">(</span><span class="string">"z"</span><span class="operator">,</span> z<span class="operator">);</span>
ar<span class="operator"> &gt;&gt;</span> boost<span class="operator">::</span>serialization<span class="operator">::</span>make_nvp<span class="operator">(</span><span class="string">"angle"</span><span class="operator">,</span> angle<span class="operator">);</span>
t<span class="operator">.</span>setX<span class="operator">(</span>x<span class="operator">);</span>
t<span class="operator">.</span>setY<span class="operator">(</span>y<span class="operator">);</span>
t<span class="operator">.</span>setZ<span class="operator">(</span>z<span class="operator">);</span>
t<span class="operator">.</span>setAngle<span class="operator">(</span>angle<span class="operator">);
}
}</span><span class="comment"> // namespace serialization
</span><span class="operator">}</span><span class="comment"> // namespace boost
</span>
BOOST_SERIALIZATION_SPLIT_FREE<span class="operator">(</span>ExtObject3D<span class="operator">)</span><span class="pre">
#endif // _PERSIST_EXTOBJECT3D_H_</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Le code ci-dessus est suffisant pour persister une instance de type '<i>ExtObject3D</i>' en base
de donn<6E>es : il est ainsi possible d'utiliser une propri<72>t<EFBFBD> de type '<i>ExtObject3D</i>' dans
une classe persistante enregistr<74>e dans le contexte QxOrm.
Cette propri<72>t<EFBFBD> peut <20>tre mapp<70>e sur une colonne de type <i>TEXT</i> ou <i>VARCHAR</i> en base
de donn<6E>es.<br>
<br>
Le comportement par d<>faut de la biblioth<74>que QxOrm est le suivant : l'instance est s<>rialis<69>e
au format XML avant d'<27>tre ins<6E>r<EFBFBD>e ou mise <20> jour en base de donn<6E>es.
Ce comportement par d<>faut peut <20>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<6E>es).
Par exemple, si l'on utilise une propri<72>t<EFBFBD> de type <i>std::vector&lt;mon_objet&gt;</i> dans une
classe persistante sans relation associ<63>e, la liste d'<27>l<EFBFBD>ments sera automatiquement enregistr<74>e
au format XML en base de donn<6E>es.<br>
<br>
<b>Remarque :</b> ce comportement par d<>faut peut <20>tre facilement modifi<66> pour un type donn<6E>.
Le moteur <i>QtSql</i> utilise le type <i>QVariant</i> pour faire le lien entre le code C++ et
la base de donn<6E>es.
Le type <i>QVariant</i> peut contenir du texte, des valeurs num<75>riques, du binaire, etc.
Il peut donc <20>tre int<6E>ressant de sp<73>cialiser le comportement par d<>faut (s<>rialisation XML) si
l'on souhaite stocker des donn<6E>es au format binaire ou bien optimiser les performances (la
s<>rialisation XML peut <20>tre couteuse en temps d'ex<65>cution).
Il suffit de proposer (en plus des fonctions de s<>rialisation boost) les conversions n<>cessaires
en <i>QVariant</i>, par exemple avec la classe '<i>ExtObject3D</i>' :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">namespace</span> qx<span class="operator"> {</span><span class="keyword">
namespace</span> cvt<span class="operator"> {</span><span class="keyword">
namespace</span> detail<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="keyword"> struct</span> QxConvert_ToVariant<span class="operator">&lt;</span> ExtObject3D<span class="operator"> &gt; {</span><span class="keyword">
static inline</span> QVariant toVariant<span class="operator">(</span><span class="keyword">const</span> ExtObject3D<span class="operator"> &amp;</span> t<span class="operator">,</span><span class="keyword"> const</span> QString<span class="operator"> &amp;</span> format<span class="operator">,</span><span class="type"> int</span> index, qx::cvt::context::ctx_type ctx<span class="operator">)
{</span><span class="comment"> /* Ici je convertis ExtObject3D en QVariant */</span><span class="operator"> } };</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="keyword"> struct</span> QxConvert_FromVariant<span class="operator">&lt;</span> ExtObject3D<span class="operator"> &gt; {</span><span class="keyword">
static inline</span> qx_bool fromVariant<span class="operator">(</span><span class="keyword">const</span> QVariant<span class="operator"> &amp;</span> v<span class="operator">,</span> ExtObject3D<span class="operator"> &amp;</span> t<span class="operator">,</span><span class="keyword"> const</span> QString<span class="operator"> &amp;</span> format<span class="operator">,</span><span class="type"> int</span> index, qx::cvt::context::ctx_type ctx<span class="operator">)
{</span><span class="comment"> /* Ici je convertis QVariant en ExtObject3D */</span><span class="operator">;</span><span class="flow"> return</span> qx_bool<span class="operator">(</span><span class="bool">true</span><span class="operator">); } };
}</span><span class="comment"> // namespace detail
</span><span class="operator">}</span><span class="comment"> // namespace cvt
</span><span class="operator">}</span><span class="comment"> // namespace qx</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
<b>Remarque :</b> Voici un template pour cr<63>er un type personnalis<69> persistable :
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="pre">#ifndef _MY_CUSTOM_PERSISTABLE_TYPE_H_
#define _MY_CUSTOM_PERSISTABLE_TYPE_H_
#ifdef _MSC_VER
#pragma once
#endif
#include &lt;QxOrm.h&gt;</span>
<span class="keyword">class</span> MyPersistableType
{
<span class="comment">/* What you want here */</span>
};
QX_REGISTER_CLASS_NAME(MyPersistableType)
QX_CLASS_VERSION(MyPersistableType, 0)
QDataStream & operator&lt;&lt; (QDataStream & stream, const MyPersistableType & t)
{
<span class="comment">/* Your implementation here */</span>
}
QDataStream & operator&gt;&gt; (QDataStream & stream, MyPersistableType & t)
{
<span class="comment">/* Your implementation here */</span>
}
<span class="keyword">namespace</span> qx {
<span class="keyword">namespace</span> cvt {
<span class="keyword">namespace</span> detail {
<span class="keyword">template</span> &lt;&gt; <span class="keyword">struct</span> QxConvert_ToVariant&lt; MyPersistableType &gt; {
<span class="keyword">static</span> <span class="keyword">inline</span> QVariant toVariant(const MyPersistableType & t, const QString & format, int index, qx::cvt::context::ctx_type ctx)
{
<span class="comment">/* Here I convert from MyPersistableType to QVariant */</span>
} };
<span class="keyword">template</span> <> <span class="keyword">struct</span> QxConvert_FromVariant&lt; MyPersistableType &gt; {
<span class="keyword">static</span> <span class="keyword">inline</span> qx_bool fromVariant(const QVariant & v, MyPersistableType & t, const QString & format, int index, qx::cvt::context::ctx_type ctx)
{
<span class="comment">/* Here I convert from QVariant to MyPersistableType */</span>
return qx_bool(true);
} };
} <span class="comment">// namespace detail</span>
} <span class="comment">// namespace cvt</span>
} <span class="comment">// namespace qx</span>
<span class="keyword">#ifndef</span> _QX_NO_JSON
<span class="keyword">namespace</span> qx {
<span class="keyword">namespace</span> cvt {
<span class="keyword">namespace</span> detail {
<span class="keyword">template</span> &lt;&gt;
<span class="keyword">struct</span> QxConvert_ToJson&lt; MyPersistableType &gt;
{
<span class="keyword">static inline</span> QJsonValue toJson(const MyPersistableType & t, const QString & format)
{
<span class="comment">/* Your implementation here */</span>
}
};
<span class="keyword">template</span> &lt;&gt;
<span class="keyword">struct</span> QxConvert_FromJson&lt; MyPersistableType &gt;
{
<span class="keyword">static inline</span> qx_bool fromJson(const QJsonValue & j, MyPersistableType & t, const QString & format)
{
<span class="comment">/* Your implementation here */</span>
}
};
} <span class="comment">// namespace detail</span>
} <span class="comment">// namespace cvt</span>
} <span class="comment">// namespace qx</span>
<span class="keyword">#endif</span> <span class="comment">// _QX_NO_JSON</span>
<span class="comment">// ------------------------------------</span>
<span class="comment">// If you are using boost serialization, you have also to implement save/load functions like above 'ExtObject3D' example</span>
<span class="comment">// ------------------------------------</span>
<span class="keyword">#endif</span> <span class="comment">// _MY_CUSTOM_PERSISTABLE_TYPE_H_</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_470">G<EFBFBD>n<EFBFBD>rer le sch<63>ma DDL SQL
de la base de donn<6E>es</a></p>
<div class="manual_div_content">
<a href="#manual_110"><b>
<font style="background-color:yellow">!!! Il est fortement recommand<6E> d'utiliser
l'application <b>QxEntityEditor</b> pour g<>rer cette probl<62>matique !!!</font>
</b></a><br>
<br>
La biblioth<74>que QxOrm ne fournit pas de m<>canisme pour g<>rer automatiquement la cr<63>ation et mise
<20> jour des tables dans la base de donn<6E>es.<br>
En effet, la fonction <i>qx::dao::create_table&lt;T&gt;</i> doit <20>tre utilis<69>e uniquement pour
cr<63>er des prototypes.<br>
Il est fortement recommand<6E> d'utiliser un outil sp<73>cifique <20> chaque SGBD pour cr<63>er et maintenir
les tables de la base de donn<6E>es (par exemple <i>Navicat</i> pour <i>MySql</i>, <i>pgAdmin</i>
pour <i>PostgreSQL</i>, <i>SQLite Manager</i> pour <i>SQLite</i>, etc.).<br>
De plus, un outil sp<73>cifique <20> chaque SGBD permet d'appliquer certaines optimisations (ajout
d'index par exemple).<br>
<br>
Cependant, il peut <20>tre int<6E>ressant pour certaines applications de ne pas avoir <20> g<>rer
manuellement les tables de la base de donn<6E>es.<br>
Dans ce cas, il est possible de cr<63>er une fonction C++ pour parcourir la liste des classes
persistantes enregistr<74>es dans le contexte QxOrm (en utilisant le moteur d'introspection de la
biblioth<74>que) et ainsi cr<63>er un script SQL de g<>n<EFBFBD>ration et mise <20> jour des tables de la base de
donn<6E>es.<br>
<br>
La biblioth<74>que QxOrm fournit une fonction C++ cr<63><72>e uniquement <20> titre d'exemple : elle peut
donc servir de base de travail pour cr<63>er sa propre fonction de g<>n<EFBFBD>ration de script SQL.<br>
Cette fonction se trouve dans le fichier <i><a href="./resource/qxclassx_dump_sql_schema.html"
target="_blank">./src/QxRegister/QxClassX.cpp</a></i> et se nomme <i><a
href="./resource/qxclassx_dump_sql_schema.html" target="_blank">QString
qx::QxClassX::dumpSqlSchema()</a></i>.<br>
Elle g<>n<EFBFBD>re un script SQL et le renvoie sous forme de <i>QString</i> : il est possible d'adapter
cette fonction pour g<>n<EFBFBD>rer un fichier contenant le script SQL ou bien appliquer chaque
instruction SQL directement au SGBD.<br>
<br>
Voici un exemple d'impl<70>mentation propos<6F> par <a
href="http://www.developpez.net/forums/u449556/dodobibi/" target="_blank">dodobibi</a> pour
g<>rer une base <i>PostgreSQL</i> : cet exemple g<>re les <20>volutions futures de son application
(ajout de colonnes dans une table existante, ajout d'index sur une colonne existante, etc.).<br>
Au lancement de l'application, le num<75>ro de version est indiqu<71> de la fa<66>on suivante :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>QApplication app<span class="operator">(</span>argc<span class="operator">,</span> argv<span class="operator">);</span>
app<span class="operator">.</span>setProperty<span class="operator">(</span><span class="string">"DomainVersion"</span><span class="operator">,</span><span class="int"> 1</span><span class="operator">);</span><span class="comment"> // Version increment<6E>e <20> chaque compilation et diffusion de l'application</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Une table de la base de donn<6E>es permet de stocker le num<75>ro de version courant.<br>
Une classe persistante C++ est mapp<70>e sur cette table de la fa<66>on suivante :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="pre">#ifndef _DATABASE_VERSION_H_
#define _DATABASE_VERSION_H_
</span><span class="keyword">
class</span> MY_DLL_EXPORT DatabaseVersion<span class="operator">
{</span><span class="keyword">
public</span><span class="operator">:</span>
QString name<span class="operator">;</span><span class="type">
long</span> version<span class="operator">;
};</span>
QX_REGISTER_HPP_MY_APP<span class="operator">(</span>DatabaseVersion<span class="operator">,</span> qx<span class="operator">::</span>trait<span class="operator">::</span>no_base_class_defined<span class="operator">,</span><span class="int"> 0</span><span class="operator">)</span><span class="pre">
#endif // _DATABASE_VERSION_H_</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="pre">#include "../include/precompiled.h"
#include "../include/DatabaseVersion.h"
#include &lt;QxOrm_Impl.h&gt;
</span>
QX_REGISTER_CPP_MY_APP<span class="operator">(</span>DatabaseVersion<span class="operator">)</span><span class="keyword">
namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>DatabaseVersion<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span>
t<span class="operator">.</span>id<span class="operator">(&amp;</span> DatabaseVersion<span class="operator">::</span>name<span class="operator">,</span><span class="string"> "name"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> DatabaseVersion<span class="operator">::</span>version<span class="operator">,</span><span class="string"> "version"</span><span class="operator">);
}}</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Avec la classe <i>DatabaseVersion</i>, il est possible de v<>rifier si la version de la base de
donn<6E>es est <20> jour.<br>
C'est le r<>le de la fonction <i>isDatabaseVersionOld()</i> :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="type">bool</span> isDatabaseVersionOld<span class="operator">()
{</span>
DatabaseVersion dbVersion<span class="operator">;</span>
dbVersion<span class="operator">.</span>name<span class="operator"> =</span><span class="string"> "MyAppName"</span><span class="operator">;</span>
QSqlError err<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_id<span class="operator">(</span>dbVersion<span class="operator">);</span><span class="flow">
if</span><span class="operator"> (</span>err<span class="operator">.</span>isValid<span class="operator">()) {</span> qAssert<span class="operator">(</span><span class="bool">false</span><span class="operator">);</span><span class="flow"> return</span><span class="bool"> false</span><span class="operator">; }</span><span class="flow">
return</span><span class="operator"> (</span>dbVersion<span class="operator">.</span>version<span class="operator"> &lt;</span> qApp<span class="operator">-&gt;</span>property<span class="operator">(</span><span class="string">"DomainVersion"</span><span class="operator">).</span>toInt<span class="operator">());
}</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Si au lancement de l'application, la fonction <i>isDatabaseVersionOld()</i> renvoie <i>true</i>,
alors la mise <20> jour de la base de donn<6E>es est effectu<74>e de la fa<66>on suivante :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="type">void</span> updateDatabaseVersion<span class="operator">()
{</span><span class="flow">
try</span><span class="operator">
{</span><span class="type">
int</span> domainVersion<span class="operator"> =</span> qApp<span class="operator">-&gt;</span>property<span class="operator">(</span><span class="string">"DomainVersion"</span><span class="operator">).</span>toInt<span class="operator">();</span><span class="comment">
// On se connecte avec un utilisateur de la base de donn<6E>es qui a les droits de modifications du sch<63>ma
</span> QSqlDatabase db<span class="operator"> =</span> qx<span class="operator">::</span>QxSqlDatabase<span class="operator">::</span>getSingleton<span class="operator">()-&gt;</span>getDatabaseCloned<span class="operator">();</span>
db<span class="operator">.</span>setUserName<span class="operator">(</span><span class="string">"MyAdminLogin"</span><span class="operator">);</span>
db<span class="operator">.</span>setPassword<span class="operator">(</span><span class="string">"MyAdminPassword"</span><span class="operator">);</span><span class="comment">
// On s'assure que la session d<>marre une transaction et l<>ve une exception <20> la moindre erreur
</span> qx<span class="operator">::</span>QxSession session<span class="operator">(</span>db<span class="operator">,</span><span class="bool"> true</span><span class="operator">,</span><span class="bool"> true</span><span class="operator">);</span><span class="comment">
// On "fetch" la version de la base de donn<6E>es avec un verrou pour <20>viter les modifications concurrentes !
// Si plusieurs utilisateurs lancent l'application en m<>me temps et qu'une mise <20> jour
// est n<>cessaire, le premier fera la mise <20> jour, et les autres seront en attente
</span> DatabaseVersion dbVersion<span class="operator">;</span>
session<span class="operator">.</span>fetchByQuery<span class="operator">(</span>qx_query<span class="operator">(</span><span class="string">"WHERE name='MyAppName' FOR UPDATE"</span><span class="operator">),</span> dbVersion<span class="operator">);</span><span class="comment">
// Pour les autres utilisateurs, une fois le verrou lev<65>, on v<>rifie si la mise <20> jour est toujours n<>cessaire
</span><span class="flow"> if</span><span class="operator"> (</span>dbVersion<span class="operator">.</span>version<span class="operator"> &gt;=</span> domainVersion<span class="operator">) {</span><span class="flow"> return</span><span class="operator">; }</span><span class="comment">
// On ex<65>cute chaque instruction SQL avec la variable "query"
</span> QSqlQuery query<span class="operator">(</span>db<span class="operator">);</span><span class="comment">
// On r<>cup<75>re toutes les classes persistantes C++ enregistr<74>es dans le contexte QxOrm
</span> qx<span class="operator">::</span>QxCollection<span class="operator">&lt;</span>QString<span class="operator">,</span> qx<span class="operator">::</span>IxClass<span class="operator"> *&gt; *</span> pAllClasses<span class="operator"> =</span> qx<span class="operator">::</span>QxClassX<span class="operator">::</span>getAllClasses<span class="operator">();</span><span class="flow">
if</span><span class="operator"> (!</span> pAllClasses<span class="operator">) {</span> qAssert<span class="operator">(</span><span class="bool">false</span><span class="operator">);</span><span class="flow"> return</span><span class="operator">; }</span><span class="comment">
// on r<>cup<75>re la liste des tables existantes dans la base (fonction de Qt)
</span> QStringList tables<span class="operator"> =</span> db<span class="operator">.</span>tables<span class="operator">();</span><span class="flow">
for</span><span class="operator"> (</span><span class="type">long</span> k<span class="operator"> =</span><span class="int"> 0</span><span class="operator">;</span> k<span class="operator"> &lt;</span> pAllClasses<span class="operator">-&gt;</span>count<span class="operator">();</span> k<span class="operator">++)
{</span>
qx<span class="operator">::</span>IxClass<span class="operator"> *</span> pClass<span class="operator"> =</span> pAllClasses<span class="operator">-&gt;</span>getByIndex<span class="operator">(</span>k<span class="operator">);</span><span class="flow">
if</span><span class="operator"> (!</span> pClass<span class="operator">) {</span><span class="flow"> continue</span><span class="operator">; }</span><span class="comment">
// Filtre les classes non persistantes
</span><span class="flow"> if</span><span class="operator"> (</span>pClass<span class="operator">-&gt;</span>isKindOf<span class="operator">(</span><span class="string">"qx::service::IxParameter"</span><span class="operator">) ||</span> pClass<span class="operator">-&gt;</span>isKindOf<span class="operator">(</span><span class="string">"qx::service::IxService"</span><span class="operator">)) {</span><span class="flow"> continue</span><span class="operator">; }</span><span class="comment">
// Filtre les classes <20> jour : si la version de pClass est &lt;= <20> la version enregistr<74>e dans la base, la mise <20> jour n'est pas n<>cessaire
</span><span class="flow"> if</span><span class="operator"> (</span>pClass<span class="operator">-&gt;</span>getVersion<span class="operator">() &lt;=</span> dbVersion<span class="operator">.</span>version<span class="operator">) {</span><span class="flow"> continue</span><span class="operator">; }</span><span class="comment">
// On cr<63>e la table si elle n'existe pas, et on d<>finit son propri<72>taire
</span><span class="flow"> if</span><span class="operator"> (!</span> tables<span class="operator">.</span>contains<span class="operator">(</span>pClass<span class="operator">-&gt;</span>getName<span class="operator">()))
{</span>
query<span class="operator">.</span>exec<span class="operator">(</span><span class="string">"CREATE TABLE "</span><span class="operator"> +</span> pClass<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> " ( ) WITH (OIDS = FALSE);"
"ALTER TABLE "</span><span class="operator"> +</span> pClass<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> " OWNER TO \"MyAdminLogin\";"</span><span class="operator">);</span>
session<span class="operator"> +=</span> query<span class="operator">.</span>lastError<span class="operator">();
}</span><span class="comment">
// On ajoute les colonnes <20> la table si elles n'existent pas
</span> qx<span class="operator">::</span>IxDataMemberX<span class="operator"> *</span> pDataMemberX<span class="operator"> =</span> pClass<span class="operator">-&gt;</span>getDataMemberX<span class="operator">();</span><span class="flow">
for</span><span class="operator"> (</span><span class="type">long</span> l<span class="operator"> =</span><span class="int"> 0</span><span class="operator">; (</span>pDataMemberX<span class="operator"> &amp;&amp; (</span>l<span class="operator"> &lt;</span> pDataMemberX<span class="operator">-&gt;</span>count_WithDaoStrategy<span class="operator">()));</span> l<span class="operator">++)
{</span>
qx<span class="operator">::</span>IxDataMember<span class="operator"> *</span> p<span class="operator"> =</span> pDataMemberX<span class="operator">-&gt;</span>get_WithDaoStrategy<span class="operator">(</span>l<span class="operator">);</span><span class="flow">
if</span><span class="operator"> (!</span> p<span class="operator"> || (</span>p<span class="operator">-&gt;</span>getVersion<span class="operator">() &lt;=</span> dbVersion<span class="operator">.</span>version<span class="operator">)) {</span><span class="flow"> continue</span><span class="operator">; }</span>
query<span class="operator">.</span>exec<span class="operator">(</span><span class="string">"ALTER TABLE "</span><span class="operator"> +</span> pClass<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> " ADD COLUMN "</span><span class="operator"> +</span> p<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> " "</span><span class="operator"> +</span> p<span class="operator">-&gt;</span>getSqlType<span class="operator">() +</span><span class="string"> ";"</span><span class="operator">);</span>
session<span class="operator"> +=</span> query<span class="operator">.</span>lastError<span class="operator">();</span><span class="flow">
if</span><span class="operator"> (</span>p<span class="operator">-&gt;</span>getIsPrimaryKey<span class="operator">())</span><span class="comment"> // PRIMARY KEY
</span><span class="operator"> {</span>
query<span class="operator">.</span>exec<span class="operator">(</span><span class="string">"ALTER TABLE "</span><span class="operator"> +</span> pClass<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> " ADD PRIMARY KEY ("</span><span class="operator"> +</span> p<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> ");"</span><span class="operator">);</span>
session<span class="operator"> +=</span> query<span class="operator">.</span>lastError<span class="operator">();
}</span><span class="flow">
if</span><span class="operator"> (</span>p<span class="operator">-&gt;</span>getAllPropertyBagKeys<span class="operator">().</span>contains<span class="operator">(</span><span class="string">"INDEX"</span><span class="operator">))</span><span class="comment"> // INDEX
</span><span class="operator"> {</span>
query<span class="operator">.</span>exec<span class="operator">(</span><span class="string">"CREATE INDEX "</span><span class="operator"> +</span> pClass<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> "_"</span><span class="operator"> +</span> p<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> "_idx"</span><span class="operator"> +</span><span class="string">
" ON "</span><span class="operator"> +</span> pClass<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> " USING "</span><span class="operator"> +</span> p<span class="operator">-&gt;</span>getPropertyBag<span class="operator">(</span><span class="string">"INDEX"</span><span class="operator">).</span>toString<span class="operator">() +</span><span class="string"> " ("</span><span class="operator"> +</span> p<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> ");"</span><span class="operator">);</span>
session<span class="operator"> +=</span> query<span class="operator">.</span>lastError<span class="operator">();
}</span><span class="flow">
if</span><span class="operator"> (</span>p<span class="operator">-&gt;</span>getNotNull<span class="operator">())</span><span class="comment"> // NOT NULL
</span><span class="operator"> {</span>
query<span class="operator">.</span>exec<span class="operator">(</span><span class="string">"ALTER TABLE "</span><span class="operator"> +</span> pClass<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> " ALTER COLUMN "</span><span class="operator"> +</span> p<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> " SET NOT NULL;"</span><span class="operator">);</span>
session<span class="operator"> +=</span> query<span class="operator">.</span>lastError<span class="operator">();
}</span><span class="flow">
if</span><span class="operator"> (</span>p<span class="operator">-&gt;</span>getAutoIncrement<span class="operator">())</span><span class="comment"> // AUTO INCREMENT
</span><span class="operator"> {</span>
query<span class="operator">.</span>exec<span class="operator">(</span><span class="string">"CREATE SEQUENCE "</span><span class="operator"> +</span> pClass<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> "_"</span><span class="operator"> +</span> p<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> "_seq"</span><span class="operator"> +</span><span class="string"> "; "
"ALTER TABLE "</span><span class="operator"> +</span> pClass<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> "_"</span><span class="operator"> +</span> p<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> "_seq"</span><span class="operator"> +</span><span class="string"> " OWNER TO \"MyAdminLogin\"; "
"ALTER TABLE "</span><span class="operator"> +</span> pClass<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> " ALTER COLUMN "</span><span class="operator"> +</span> p<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> " "</span><span class="operator"> +</span><span class="string">
"SET DEFAULT nextval('"</span><span class="operator"> +</span> pClass<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> "_"</span><span class="operator"> +</span> p<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> "_seq"</span><span class="operator"> +</span><span class="string"> "'::regclass);"</span><span class="operator">);</span>
session<span class="operator"> +=</span> query<span class="operator">.</span>lastError<span class="operator">();
}</span><span class="flow">
if</span><span class="operator"> (</span>p<span class="operator">-&gt;</span>getDescription<span class="operator">() !=</span><span class="string"> ""</span><span class="operator">)</span><span class="comment"> // DESCRIPTION
</span><span class="operator"> {</span><span class="comment">
// $$ceci est un texte ne n<>cessitant pas de caract<63>res d'<27>chappement dans postgres grace aux doubles dolars$$
</span> query<span class="operator">.</span>exec<span class="operator">(</span><span class="string">"COMMENT ON COLUMN "</span><span class="operator"> +</span> pClass<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> "."</span><span class="operator"> +</span> p<span class="operator">-&gt;</span>getName<span class="operator">() +</span><span class="string"> " IS $$"</span><span class="operator"> +</span> p<span class="operator">-&gt;</span>getDescription<span class="operator">() +</span><span class="string"> "$$ ;"</span><span class="operator">);</span>
session<span class="operator"> +=</span> query<span class="operator">.</span>lastError<span class="operator">();
}
}
}</span><span class="comment">
// On enregistre la version courante de la base de donn<6E>es
</span> dbVersion<span class="operator">.</span>version<span class="operator"> =</span> domainVersion<span class="operator">;</span>
session<span class="operator">.</span>save<span class="operator">(</span>dbVersion<span class="operator">);</span><span class="comment">
// Fin du block "try" : la session est d<>truite =&gt; commit ou rollback automatique
// De plus, un commit ou rollback sur la transaction l<>ve automatiquement le verrou pos<6F> pr<70>c<EFBFBD>demment
</span><span class="operator"> }</span><span class="flow">
catch</span><span class="operator"> (</span><span class="keyword">const</span> qx<span class="operator">::</span>dao<span class="operator">::</span>sql_error<span class="operator"> &amp;</span> err<span class="operator">)
{</span>
QSqlError sqlError<span class="operator"> =</span> err<span class="operator">.</span>get<span class="operator">();</span>
qDebug<span class="operator">() &lt;&lt;</span> sqlError<span class="operator">.</span>databaseText<span class="operator">();</span>
qDebug<span class="operator">() &lt;&lt;</span> sqlError<span class="operator">.</span>driverText<span class="operator">();</span>
qDebug<span class="operator">() &lt;&lt;</span> sqlError<span class="operator">.</span>number<span class="operator">();</span>
qDebug<span class="operator">() &lt;&lt;</span> sqlError<span class="operator">.</span>type<span class="operator">();
}
}</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<b>Remarque :</b> le code pr<70>c<EFBFBD>dent (tout comme la fonction <a
href="./resource/qxclassx_dump_sql_schema.html"
target="_blank"><i>qx::QxClassX::dumpSqlSchema()</i></a>) peut <20>tre modifi<66> pour s'adapter
aux besoins sp<73>cifiques d'une application.<br>
Par exemple, il pourrait <20>tre int<6E>ressant de cr<63>er par d<>faut une seconde table (en plus de la
table <i>DatabaseVersion</i>) pour enregistrer la liste des classes persistantes enregistr<74>es
dans le contexte QxOrm : ainsi, au lieu d'utiliser la fonction propos<6F>e par Qt
"<i>db.tables()</i>", il serait possible de r<>cup<75>rer toutes les tables mapp<70>es sur des classes
persistantes avec des informations suppl<70>mentaires (num<75>ro de version pour chaque table, nombre
de colonnes enregistr<74>es dans le contexte QxOrm, description de chaque table, etc.).
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_475">Associer un type SQL <20>
une classe C++</a></p>
<div class="manual_div_content">
Chaque base de donn<6E>es propose des types SQL diff<66>rents pour stocker l'information.<br>
La biblioth<74>que QxOrm propose une association par d<>faut pour les classes C++ les plus
fr<66>quemment utilis<69>es dans un programme.<br>
Voici cette association par d<>faut :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="string">"bool"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "SMALLINT"
"qx_bool"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "SMALLINT"
"short"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "SMALLINT"
"int"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "INTEGER"
"long"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "INTEGER"
"long long"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "INTEGER"
"float"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "FLOAT"
"double"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "FLOAT"
"long double"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "FLOAT"
"unsigned short"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "SMALLINT"
"unsigned int"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "INTEGER"
"unsigned long"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "INTEGER"
"unsigned long long"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "INTEGER"
"std::string"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TEXT"
"std::wstring"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TEXT"
"QString"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TEXT"
"QVariant"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TEXT"
"QUuid"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TEXT"
"QDate"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "DATE"
"QTime"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TIME"
"QDateTime"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TIMESTAMP"
"QByteArray"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "BLOB"
"qx::QxDateNeutral"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TEXT"
"qx::QxTimeNeutral"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TEXT"
"qx::QxDateTimeNeutral"</span><span class="operator"> &lt;-&gt;</span><span class="string"> "TEXT"</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Si le type SQL propos<6F> par d<>faut par la biblioth<74>que QxOrm ne correspond pas <20> la base de
donn<6E>es utilis<69>e, il peut facilement <20>tre modifi<66> (de mani<6E>re globale <20> toute l'application) en
utilisant la collection suivante :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>QHash<span class="operator">&lt;</span>QString<span class="operator">,</span> QString<span class="operator">&gt; *</span> lstSqlType<span class="operator"> =</span> qx<span class="operator">::</span>QxClassX<span class="operator">::</span>getAllSqlTypeByClassName<span class="operator">();</span>
lstSqlType<span class="operator">-&gt;</span>insert<span class="operator">(</span><span class="string">"QString"</span><span class="operator">,</span><span class="string"> "VARCHAR(255)"</span><span class="operator">);</span>
lstSqlType<span class="operator">-&gt;</span>insert<span class="operator">(</span><span class="string">"std::string"</span><span class="operator">,</span><span class="string"> "VARCHAR(255)"</span><span class="operator">);</span><span class="comment">
// etc.</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Pour modifier le type SQL de mani<6E>re sp<73>cifique pour une colonne d'une table de la base de
donn<6E>es, il faut d<>finir le type SQL dans la fonction de mapping de QxOrm :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>MyClass<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span><span class="comment">
//...
</span> IxDataMember<span class="operator"> *</span> p<span class="operator"> =</span> t<span class="operator">.</span>data<span class="operator">(&amp;</span> MyClass<span class="operator">::</span>m_MyProperty<span class="operator">,</span><span class="string"> "my_property"</span><span class="operator">);</span>
p<span class="operator">-&gt;</span>setSqlType<span class="operator">(</span><span class="string">"VARCHAR(255)"</span><span class="operator">);</span><span class="comment">
//...
</span><span class="operator">}}</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Pour les classes non support<72>es par d<>faut par la biblioth<74>que QxOrm (voir ce chapitre du manuel
utilisateur : <i><a href="#manual_460">Persister des types personnalis<69>s</a></i>), il est
possible d'associer un type SQL par d<>faut en utilisant la macro suivante (en dehors de tout
<i>namespace</i>) :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>QX_REGISTER_TRAIT_GET_SQL_TYPE<span class="operator">(</span>MyClass<span class="operator">,</span><span class="string"> "my_sql_type"</span><span class="operator">)</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_480">Effectuer des requ<71>tes
asynchrones <20> la base de donn<6E>es</a></p>
<div class="manual_div_content">
Il peut <20>tre parfois int<6E>ressant d'ex<65>cuter certaines requ<71>tes <20> la base de donn<6E>es de mani<6E>re
asynchrone (multi-thread), par exemple pour <20>viter de bloquer une IHM si une requ<71>te est trop
longue <20> s'ex<65>cuter.<br>
Pour simplifier les requ<71>tes asynchrones, la biblioth<74>que QxOrm fournit la classe <i><a
href="../doxygen/html/classqx_1_1_qx_dao_async.html"
target="_blank">qx::QxDaoAsync</a></i>.<br>
Cette classe ex<65>cute une requ<71>te dans un thread d<>di<64> et renvoie un <i>SIGNAL</i>
<i>queryFinished()</i> lorsque la requ<71>te est termin<69>e.<br>
Pour utiliser la classe <i><a href="../doxygen/html/classqx_1_1_qx_dao_async.html"
target="_blank">qx::QxDaoAsync</a></i>, il suffit de :
<ul>
<li>v<EFBFBD>rifier que la requ<71>te fait appel <20> une classe qui impl<70>mente l'interface <i><a
href="../doxygen/html/classqx_1_1_ix_persistable.html"
target="_blank">qx::IxPersistable</a></i> ;</li>
<li>cr<EFBFBD>er une instance de type <i>qx::QxDaoAsync</i> (par exemple, une propri<72>t<EFBFBD> membre d'une
classe d<>rivant du type <i>QWidget</i>) ;</li>
<li>connecter un <i>SLOT</i> au <i>SIGNAL</i> <i>qx::QxDaoAsync::queryFinished()</i> (par
exemple, un <i>SLOT</i> d<>fini dans une classe d<>rivant du type <i>QWidget</i>) ;</li>
<li>ex<EFBFBD>cuter une requ<71>te <20> la base de donn<6E>es en utilisant l'une des m<>thodes commen<65>ant par
<i>async</i> : <i>qx::QxDaoAsync::asyncXXXX()</i>.
</li>
</ul>
Voici un exemple d'utilisation avec une classe nomm<6D>e <i>MyWidget</i> :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">class</span> MyWidget<span class="operator"> :</span><span class="keyword"> public</span> QWidget<span class="operator">
{</span> Q_OBJECT<span class="comment">
//...
</span> qx<span class="operator">::</span>QxDaoAsync m_daoAsync<span class="operator">;</span><span class="comment">
//...
</span>Q_SLOTS<span class="operator">:</span><span class="type">
void</span> onQueryFinished<span class="operator">(</span><span class="keyword">const</span> QSqlError<span class="operator"> &amp;</span> daoError<span class="operator">,</span> qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>QxDaoAsyncParams_ptr pDaoParams<span class="operator">);</span><span class="comment">
//...
</span><span class="operator">};</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Et voici l'impl<70>mentation de la classe <i>MyWidget</i> :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>MyWidget<span class="operator">::</span>MyWidget<span class="operator">() :</span> QObject<span class="operator">()
{</span><span class="comment">
//...
</span> QObject<span class="operator">::</span>connect<span class="operator">((&amp;</span> m_daoAsync<span class="operator">),</span> SIGNAL<span class="operator">(</span>queryFinished<span class="operator">(</span><span class="keyword">const</span> QSqlError<span class="operator"> &amp;,</span> qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>QxDaoAsyncParams_ptr<span class="operator">)),</span><span class="keyword">
this</span><span class="operator">,</span> SLOT<span class="operator">(</span>onQueryFinished<span class="operator">(</span><span class="keyword">const</span> QSqlError<span class="operator"> &amp;,</span> qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>QxDaoAsyncParams_ptr<span class="operator">)));</span><span class="comment">
//...
</span><span class="operator">}</span><span class="type">
void</span> MyWidget<span class="operator">::</span>onQueryFinished<span class="operator">(</span><span class="keyword">const</span> QSqlError<span class="operator"> &amp;</span> daoError<span class="operator">,</span> qx<span class="operator">::</span>dao<span class="operator">::</span>detail<span class="operator">::</span>QxDaoAsyncParams_ptr pDaoParams<span class="operator">)
{</span><span class="flow">
if</span><span class="operator"> (!</span> pDaoParams<span class="operator">) {</span><span class="flow"> return</span><span class="operator">; }</span>
qx<span class="operator">::</span>QxSqlQuery query<span class="operator"> =</span> pDaoParams<span class="operator">-&gt;</span>query<span class="operator">;</span><span class="flow">
if</span><span class="operator"> (!</span> daoError<span class="operator">.</span>isValid<span class="operator">()) { ; }</span><span class="comment">
// If the async query is associated to a simple object, just use 'pDaoParams-&gt;pInstance' method
</span> qx<span class="operator">::</span>IxPersistable_ptr ptr<span class="operator"> =</span> pDaoParams<span class="operator">-&gt;</span>pInstance<span class="operator">;</span><span class="comment">
// If the async query is associated to a list of objects, just use 'pDaoParams-&gt;pListOfInstances' method
</span> qx<span class="operator">::</span>IxPersistableCollection_ptr lst<span class="operator"> =</span> pDaoParams<span class="operator">-&gt;</span>pListOfInstances<span class="operator">;</span><span class="comment">
//...
</span><span class="operator">}</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_490">Gestion du cache pour
sauvegarder des instances C++ (module QxCache)</a></p>
<div class="manual_div_content">
Le cache propos<6F> par la biblioth<74>que QxOrm (<a href="../doxygen/html/group___qx_cache.html"
target="_blank">module <i>QxCache</i></a>) est <i>thread-safe</i> et permet de stocker
facilement n'importe quel type de donn<6E>es.<br>
Les fonctions pour acc<63>der au cache se trouvent dans le <a
href="../doxygen/html/namespaceqx_1_1cache.html" target="_blank"><i>namespace</i>
<i>qx::cache</i></a>.<br>
Le cache permet une optimisation du programme : il est possible par exemple de stocker des
<20>l<EFBFBD>ments issus d'une requ<71>te effectu<74>e en base de donn<6E>es.<br>
<br>
Chaque <20>l<EFBFBD>ment stock<63> dans le cache est associ<63> <20> une cl<63> de type <i>QString</i> : cette cl<63>
permet de retrouver rapidement un <20>l<EFBFBD>ment du cache.<br>
Si un nouvel <20>l<EFBFBD>ment est stock<63> dans le cache avec une cl<63> qui existe d<>j<EFBFBD>, alors l'ancien
<20>l<EFBFBD>ment associ<63> <20> cette cl<63> est effac<61> automatiquement du cache.<br>
<br>
Le cache de la biblioth<74>que QxOrm ne g<>re pas la dur<75>e de vie des objets : il n'y a aucun
<i>delete</i> effectu<74> par le cache.<br>
C'est pourquoi il est fortement recommand<6E> (mais ce n'est pas une obligation) de privil<69>gier le
stockage de pointeurs intelligents : par exemple, <a
href="http://www.boost.org/doc/libs/release/libs/smart_ptr/shared_ptr.htm"
target="_blank"><i>boost::shared_ptr&lt;T&gt;</i></a> pour la biblioth<74>que <i>boost</i> ou
bien <a href="http://doc-snapshots.qt.io/4.8/qsharedpointer.html"
target="_blank"><i>QSharedPointer&lt;T&gt;</i></a> pour la biblioth<74>que <i>Qt</i>.<br>
<br>
Le cache peut avoir un co<63>t relatif maximum pour <20>viter une utilisation de la m<>moire trop
importante : chaque <20>l<EFBFBD>ment ins<6E>r<EFBFBD> dans le cache peut indiquer un co<63>t repr<70>sentant une
estimation de sa taille m<>moire (par exemple, le nombre d'<27>l<EFBFBD>ments d'une collection).<br>
Lorsque le co<63>t maximum du cache est atteint, les premiers <20>l<EFBFBD>ments ins<6E>r<EFBFBD>s dans le cache sont
supprim<69>s (en respectant l'ordre d'insertion dans le cache) jusqu'<27> ce que la limite du cache ne
soit plus d<>pass<73>e.<br>
<br>
Il est possible d'associer <20> chaque <20>l<EFBFBD>ment du cache une date-heure d'insertion.<br>
Si aucune date-heure n'est renseign<67>e, alors la date-heure courante est prise en compte.<br>
Ce m<>canisme permet de v<>rifier si un <20>l<EFBFBD>ment stock<63> dans le cache n<>cessite une mise <20> jour ou
non.<br>
<br>
Voici un exemple d'utilisation du cache de la biblioth<74>que QxOrm (fonctions du <a
href="../doxygen/html/namespaceqx_1_1cache.html" target="_blank"><i>namespace</i>
<i>qx::cache</i></a>) :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// D<>fini le co<63>t maximum du cache <20> 500
</span>qx<span class="operator">::</span>cache<span class="operator">::</span>max_cost<span class="operator">(</span><span class="int">500</span><span class="operator">);</span><span class="comment">
// R<>cup<75>re une liste de 'author' de la base de donn<6E>es
</span>boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span> QList<span class="operator">&lt;</span>author<span class="operator">&gt; &gt;</span> list_author<span class="operator">;</span>
QSqlError daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_all<span class="operator">(</span>list_author<span class="operator">);</span><span class="comment">
// Ins<6E>re la liste de 'author' dans le cache
</span>qx<span class="operator">::</span>cache<span class="operator">::</span>set<span class="operator">(</span><span class="string">"list_author"</span><span class="operator">,</span> list_author<span class="operator">);</span><span class="comment">
// R<>cup<75>re une liste de 'blog' de la base de donn<6E>es
</span>QSharedPointer<span class="operator">&lt;</span> std<span class="operator">::</span>vector<span class="operator">&lt;</span>blog<span class="operator">&gt; &gt;</span> list_blog<span class="operator">;</span>
daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_all<span class="operator">(</span>list_blog<span class="operator">);</span><span class="comment">
// Ins<6E>re la liste de 'blog' dans le cache (co<63>t = nombre de 'blog')
</span>qx<span class="operator">::</span>cache<span class="operator">::</span>set<span class="operator">(</span><span class="string">"list_blog"</span><span class="operator">,</span> list_blog<span class="operator">,</span> list_blog<span class="operator">.</span>count<span class="operator">());</span><span class="comment">
// Pointeur vers un objet de type 'comment'
</span>comment_ptr my_comment<span class="operator">;</span>
my_comment<span class="operator">.</span>reset<span class="operator">(</span><span class="keyword">new</span> comment<span class="operator">(</span><span class="int">50</span><span class="operator">));</span>
daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_id<span class="operator">(</span>my_comment<span class="operator">);</span><span class="comment">
// Ins<6E>re le 'comment' dans le cache en pr<70>cisant une date-heure d'insertion
</span>qx<span class="operator">::</span>cache<span class="operator">::</span>set<span class="operator">(</span><span class="string">"comment"</span><span class="operator">,</span> my_comment<span class="operator">,</span><span class="int"> 1</span><span class="operator">,</span> my_comment<span class="operator">-&gt;</span>dateModif<span class="operator">());</span><span class="comment">
// R<>cup<75>re la liste de 'blog' stock<63>e dans le cache
</span>list_blog<span class="operator"> =</span> qx<span class="operator">::</span>cache<span class="operator">::</span>get<span class="operator">&lt;</span> QSharedPointer<span class="operator">&lt;</span> std<span class="operator">::</span>vector<span class="operator">&lt;</span>blog<span class="operator">&gt; &gt; &gt;(</span><span class="string">"list_blog"</span><span class="operator">);</span><span class="comment">
// R<>cup<75>re la liste de 'blog' sans pr<70>ciser le type
</span>qx_bool bGetOk<span class="operator"> =</span> qx<span class="operator">::</span>cache<span class="operator">::</span>get<span class="operator">(</span><span class="string">"list_blog"</span><span class="operator">,</span> list_blog<span class="operator">);</span><span class="comment">
// Supprime du cache la liste de 'author'
</span><span class="type">bool</span> bRemoveOk<span class="operator"> =</span> qx<span class="operator">::</span>cache<span class="operator">::</span>remove<span class="operator">(</span><span class="string">"list_author"</span><span class="operator">);</span><span class="comment">
// Compte le nombre d'<27>l<EFBFBD>ments du cache
</span><span class="type">long</span> lCount<span class="operator"> =</span> qx<span class="operator">::</span>cache<span class="operator">::</span>count<span class="operator">();</span><span class="comment">
// R<>cup<75>re le co<63>t actuel des <20>l<EFBFBD>ments stock<63>s dans le cache
</span><span class="type">long</span> lCurrentCost<span class="operator"> =</span> qx<span class="operator">::</span>cache<span class="operator">::</span>current_cost<span class="operator">();</span><span class="comment">
// V<>rifie qu'un <20>l<EFBFBD>ment associ<63> <20> la cl<63> "comment" existe dans le cache
</span><span class="type">bool</span> bExist<span class="operator"> =</span> qx<span class="operator">::</span>cache<span class="operator">::</span>exist<span class="operator">(</span><span class="string">"comment"</span><span class="operator">);</span><span class="comment">
// R<>cup<75>re le 'comment' stock<63> dans le cache avec sa date-heure d'insertion
</span>QDateTime dt<span class="operator">;</span>
bGetOk<span class="operator"> =</span> qx<span class="operator">::</span>cache<span class="operator">::</span>get<span class="operator">(</span><span class="string">"comment"</span><span class="operator">,</span> my_comment<span class="operator">,</span> dt<span class="operator">);</span><span class="comment">
// Vide le cache
</span>qx<span class="operator">::</span>cache<span class="operator">::</span>clear<span class="operator">();</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_500">Travailler avec plusieurs
bases de donn<6E>es</a></p>
<div class="manual_div_content">
Dans le chapitre <a href="#manual_310">Connexion <20> la base de donn<6E>es</a>, nous avons vu comment
param<61>trer la connexion par d<>faut avec la classe singleton : <a
href="../doxygen/html/classqx_1_1_qx_sql_database.html"
target="_blank">qx::QxSqlDatabase</a>.
La biblioth<74>que QxOrm <20>tant bas<61>e sur le moteur <a
href="http://doc.qt.io/qt-5/sql-programming.html" target="_blank">QtSql</a> de Qt, elle
utilise en interne la classe <a href="http://doc.qt.io/qt-5/qsqldatabase.html"
target="_blank">QSqlDatabase</a> de Qt.
Toutes les fonctions d'acc<63>s <20> la base de donn<6E>es (<a
href="../doxygen/html/namespaceqx_1_1dao.html" target="_blank">namespace qx::dao</a>, classe
<a href="../doxygen/html/classqx_1_1_qx_session.html" target="_blank">qx::QxSession</a>, etc...)
ont un param<61>tre optionnel : <b><i>QSqlDatabase * pDatabase = NULL</i></b> :
<ul>
<li>si la valeur de ce param<61>tre est <20> NULL (valeur par d<>faut) : alors la biblioth<74>que QxOrm
utilise la classe singleton <a href="../doxygen/html/classqx_1_1_qx_sql_database.html"
target="_blank">qx::QxSqlDatabase</a> pour se connecter <20> la base de donn<6E>es (avec
gestion automatique du multi-threading) ;</li>
<li>si la valeur est non nulle : alors la biblioth<74>que QxOrm utilise la connexion fournie par
le pointeur <a href="http://doc.qt.io/qt-5/qsqldatabase.html"
target="_blank"><i>QSqlDatabase * pDatabase</i></a>.</li>
</ul>
Ce param<61>tre permet donc de g<>rer son propre pool de connexions <20> une ou plusieurs bases de
donn<6E>es.
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_510">D<EFBFBD>clarer une classe
abstraite dans le contexte QxOrm</a></p>
<div class="manual_div_content">
Une classe abstraite C++ (contenant au moins une m<>thode virtuelle pure) ne peut pas <20>tre mapp<70>e
avec une table d'une base de donn<6E>es (puisqu'elle ne peut pas <20>tre instanci<63>e).<br>
Cependant, il peut <20>tre int<6E>ressant de d<>finir une classe abstraite contenant une liste de
propri<72>t<EFBFBD>s utilis<69>es par plusieurs objets persistants.<br>
Un exemple de classe abstraite se trouve dans le dossier <i>./test/qxDllSample/dll2/</i> de la
distribution de QxOrm avec la classe <i>BaseClassTrigger</i>.<br>
QxOrm propose le m<>canisme suivant pour d<>finir une classe abstraite dans le contexte QxOrm :
<ul>
<li>d<EFBFBD>clarer la classe avec la m<>thode '<i>void register_class</i>' comme n'importe qu'elle
autre classe ;</li>
<li>utiliser la macro <b>QX_REGISTER_ABSTRACT_CLASS(className)</b> juste apr<70>s la d<>finition
de la classe.</li>
</ul>
<br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_520">D<EFBFBD>clarer automatiquement
les m<>ta-propri<72>t<EFBFBD>s de Qt (macro <i>Q_PROPERTY</i>)</a></p>
<div class="manual_div_content">
Toute classe h<>ritant du type <i>QObject</i> peut d<>clarer ses propri<72>t<EFBFBD>s avec la macro <i><a
href="http://doc-snapshots.qt.io/4.8/properties.html" target="_blank">Q_PROPERTY</a></i> :
les propri<72>t<EFBFBD>s deviennent alors des m<>ta-propri<72>t<EFBFBD>s.
Ce m<>canisme permet au framework Qt de proposer un moteur d'introspection gr<67>ce au
pr<70>-compilateur <i>moc</i>.
Les m<>ta-propri<72>t<EFBFBD>s peuvent alors <20>tre utilis<69>es par exemple par le moteur <i>QML</i>,
<i>QtScript</i>, etc.<br>
<br>
La biblioth<74>que <b>QxOrm</b> n<>cessite une d<>claration de chacune des propri<72>t<EFBFBD>s d'une classe
dans la fonction de mapping <i>void qx::register_class&lt;T&gt;()</i> afin de proposer
l'ensemble de ses fonctionnalit<69>s (persistance des donn<6E>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<72>t<EFBFBD>s sans maintenir une fonction de mapping <i>void qx::register_class&lt;T&gt;()</i>
: la macro <b>QX_REGISTER_ALL_QT_PROPERTIES()</b> utilise le moteur d'introspection de Qt pour
parcourir la liste des m<>ta-propri<72>t<EFBFBD>s.<br>
<br>
Voici un exemple d'utilisation avec la classe <i>TestQtProperty</i> se trouvant dans le dossier
<i>./test/qxDllSample/dll1/include/</i> de la distribution QxOrm :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="TestQtProperty.h">
<pre><span class="pre">#ifndef _QX_TEST_QT_META_PROPERTY_H_
#define _QX_TEST_QT_META_PROPERTY_H_
</span><span class="keyword">
class</span> QX_DLL1_EXPORT TestQtProperty<span class="operator"> :</span><span class="keyword"> public</span> QObject<span class="operator">
{</span>
Q_OBJECT
<font style="background-color:yellow">Q_PROPERTY<span class="operator">(</span><span class="type">int</span> id READ id WRITE setId<span class="operator">)</span>
Q_PROPERTY<span class="operator">(</span><span class="type">long</span> number READ number WRITE setNumber<span class="operator">)</span>
Q_PROPERTY<span class="operator">(</span>QString desc READ desc WRITE setDesc<span class="operator">)</span>
Q_PROPERTY<span class="operator">(</span>QDateTime birthDate READ birthDate WRITE setBirthDate<span class="operator">)</span>
Q_PROPERTY<span class="operator">(</span>QVariant photo READ photo WRITE setPhoto<span class="operator">)</span></font><span class="keyword">
protected</span><span class="operator">:</span><span class="type">
int</span> m_id<span class="operator">;</span><span class="type">
long</span> m_number<span class="operator">;</span>
QString m_desc<span class="operator">;</span>
QDateTime m_birthDate<span class="operator">;</span>
QVariant m_photo<span class="operator">;</span><span class="keyword">
public</span><span class="operator">:</span>
TestQtProperty<span class="operator">() :</span> QObject<span class="operator">(),</span> m_id<span class="operator">(</span><span class="int">0</span><span class="operator">),</span> m_number<span class="operator">(</span><span class="int">0</span><span class="operator">) { ; }</span><span class="keyword">
virtual</span><span class="operator"> ~</span>TestQtProperty<span class="operator">() { ; }</span><span class="type">
int</span> id<span class="operator">()</span><span class="keyword"> const</span><span class="operator"> {</span><span class="flow"> return</span> m_id<span class="operator">; }</span><span class="type">
long</span> number<span class="operator">()</span><span class="keyword"> const</span><span class="operator"> {</span><span class="flow"> return</span> m_number<span class="operator">; }</span>
QString desc<span class="operator">()</span><span class="keyword"> const</span><span class="operator"> {</span><span class="flow"> return</span> m_desc<span class="operator">; }</span>
QDateTime birthDate<span class="operator">()</span><span class="keyword"> const</span><span class="operator"> {</span><span class="flow"> return</span> m_birthDate<span class="operator">; }</span>
QVariant photo<span class="operator">()</span><span class="keyword"> const</span><span class="operator"> {</span><span class="flow"> return</span> m_photo<span class="operator">; }</span><span class="type">
void</span> setId<span class="operator">(</span><span class="type">int</span> i<span class="operator">) {</span> m_id<span class="operator"> =</span> i<span class="operator">; }</span><span class="type">
void</span> setNumber<span class="operator">(</span><span class="type">long</span> l<span class="operator">) {</span> m_number<span class="operator"> =</span> l<span class="operator">; }</span><span class="type">
void</span> setDesc<span class="operator">(</span><span class="keyword">const</span> QString<span class="operator"> &amp;</span> s<span class="operator">) {</span> m_desc<span class="operator"> =</span> s<span class="operator">; }</span><span class="type">
void</span> setBirthDate<span class="operator">(</span><span class="keyword">const</span> QDateTime<span class="operator"> &amp;</span> dt<span class="operator">) {</span> m_birthDate<span class="operator"> =</span> dt<span class="operator">; }</span><span class="type">
void</span> setPhoto<span class="operator">(</span><span class="keyword">const</span> QVariant<span class="operator"> &amp;</span> v<span class="operator">) {</span> m_photo<span class="operator"> =</span> v<span class="operator">; }
};</span>
QX_REGISTER_HPP_QX_DLL1<span class="operator">(</span>TestQtProperty<span class="operator">,</span> QObject<span class="operator">,</span><span class="int"> 0</span><span class="operator">)</span><span class="pre">
#endif // _QX_TEST_QT_META_PROPERTY_H_</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="TestQtProperty.cpp">
<pre><span class="pre">#include "../include/precompiled.h"
#include "../include/TestQtProperty.h"
#include &lt;QxOrm_Impl.h&gt;
</span>
QX_REGISTER_CPP_QX_DLL1<span class="operator">(</span>TestQtProperty<span class="operator">)</span>
<font style="background-color:yellow">QX_REGISTER_ALL_QT_PROPERTIES<span class="operator">(</span>TestQtProperty<span class="operator">,</span><span class="string"> "id"</span><span class="operator">)</span></font></pre>
</td>
</tr>
</tbody>
</table>
<br>
Pour ceux qui ne souhaitent pas utiliser la macro <i>QX_REGISTER_ALL_QT_PROPERTIES</i>, il est
possible d'<27>crire <20> la place les quatre lignes de code suivantes :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>TestQtProperty<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span> <font style="background-color:yellow">qx<span class="operator">::</span>register_all_qt_properties<span class="operator">&lt;</span>TestQtProperty<span class="operator">&gt;(</span>t<span class="operator">,</span><span class="string"> "id"</span><span class="operator">);</span></font> <span class="operator">}
}</span><span class="comment"> // namespace qx</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<b>Remarque :</b> le deuxi<78>me param<61>tre de la macro <i>QX_REGISTER_ALL_QT_PROPERTIES</i> permet
d'indiquer la propri<72>t<EFBFBD> qui servira de cl<63> primaire dans la base de donn<6E>es.
Si ce param<61>tre est vide, cela signifie que la classe ne poss<73>de pas de cl<63> primaire ou bien que
celle-ci est d<>finie dans une classe de base.<br>
<br>
Toute propri<72>t<EFBFBD> d<>finie avec la macro <i>Q_PROPERTY</i> peut s'enregistrer dans le contexte
QxOrm de deux mani<6E>res diff<66>rentes :<br>
<b>1-</b> par la m<>thode classique : <i>t.data(& MyQObject::my_property, "my_property",
0);</i><br>
<b>2-</b> ou bien sans mentionner le pointeur vers la donn<6E>e membre de la classe :
<i>t.data("my_property", 0);</i><br>
<br>
Peu importe la m<>thode d'enregistrement des propri<72>t<EFBFBD>s dans le contexte QxOrm, elles seront
accessibles par la m<>me interface <i><a href="../doxygen/html/classqx_1_1_ix_data_member.html"
target="_blank">qx::IxDataMember</a></i> et proposent donc les m<>mes fonctionnalit<69>s.
Il est possible d'utiliser les deux m<>thodes dans une m<>me fonction de mapping <i>void
qx::register_class&lt;T&gt;()</i>.
Chaque m<>thode d'enregistrement pr<70>sente des avantages et inconv<6E>nients.<br>
<br>
Voici la liste des avantages de la deuxi<78>me m<>thode d'enregistrement des propri<72>t<EFBFBD>s dans le
contexte QxOrm :
<ul>
<li>temps de compilation du projet beaucoup plus rapide ;</li>
<li>taille de l'ex<65>cutable g<>n<EFBFBD>r<EFBFBD> plus petite ;</li>
<li>forte int<6E>gration avec le moteur d'introspection du framework Qt ;</li>
<li>pas besoin de maintenir la fonction de mapping en utilisant la macro
<i>QX_REGISTER_ALL_QT_PROPERTIES</i>.
</li>
</ul>
Voici les inconv<6E>nients par rapport <20> la m<>thode classique d'enregistrement des propri<72>t<EFBFBD>s :
<ul>
<li>n<EFBFBD>cessite un h<>ritage de la classe <i>QObject</i> pour pouvoir utiliser la macro
<i>Q_PROPERTY</i> ;
</li>
<li>ex<EFBFBD>cution du programme plus lente (utilisation du type <i>QVariant</i> <20> la place des
<i>template</i> C++) ;
</li>
<li>ne supporte pas la notion de relation entre tables de la base de donn<6E>es
(<i>one-to-one</i>, <i>one-to-many</i>, <i>many-to-one</i> et <i>many-to-many</i>) ;</li>
<li>pas d'acc<63>s au pointeur sur la donn<6E>e membre de la classe (conversion n<>cessaire au type
<i>QVariant</i> pour acc<63>der et modifier une valeur).
</li>
</ul>
</div>
</div>
<p class="manual_p_title_1"><a class="manual_a_title_1" name="manual_60">S<EFBFBD>rialisation</a></p>
<div class="manual_div_content_1">
La <b>s<EFBFBD>rialisation</b> est un m<>canisme permettant de sauvegarder l'<27>tat d'une instance d'objet
dans un flux (fichier, r<>seau, etc...) sous un certain format (binaire, XML, JSON, texte, etc...).
La <b>d<EFBFBD>s<EFBFBD>rialisation</b> est le processus inverse permettant de restaurer l'<27>tat d'un objet <20>
partir d'un flux.
Pour plus d'informations sur la notion de s<>rialisation : <a
href="https://en.wikipedia.org/wiki/Serialization" target="_blank">rendez-vous sur la page
Wikipedia</a>.
<br><br>
Toute classe C++ enregistr<74>e dans le contexte QxOrm peut <20>tre s<>rialis<69>e dans diff<66>rents formats :
<ul>
<li><a href="#manual_600">format binaire avec le moteur QDataStream de Qt</a> ;</li>
<li><a href="#manual_606">format JSON avec le moteur QJson de Qt</a> ;</li>
<li><a href="#manual_610">format XML avec le moteur boost::serialization</a> ;</li>
<li><a href="#manual_620">format binaire avec le moteur boost::serialization</a> ;</li>
<li><a href="#manual_630">d'autres formats propos<6F>s par le moteur boost::serialization</a>.</li>
</ul>
<b>Remarque :</b> le moteur de s<>rialisation de la biblioth<74>que QxOrm permet de proposer des
fonctionnalit<69>s suppl<70>mentaires comme <a href="#manual_640">le clonage d'entit<69>s</a>, <a
href="#manual_650">le dump d'entit<69>s (format XML ou JSON)</a> ou encore <a href="#manual_80">le
module QxService</a>.
<br><br>
<b>Autre remarque :</b> par d<>faut, toutes les propri<72>t<EFBFBD>s enregistr<74>es dans le contexte QxOrm sont
s<>rialisables. Pour supprimer une propri<72>t<EFBFBD> du moteur de s<>rialisation, il est possible d'<27>crire
:<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>person<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span>
IxDataMember<span class="operator"> *</span> pDataMember<span class="operator"> =</span> t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>age<span class="operator">,</span><span class="string"> "age"</span><span class="operator">);</span>
<font style="background-color:yellow">pDataMember<span class="operator">-&gt;</span>setSerialize<span class="operator">(</span><span class="bool">false</span><span class="operator">);</span></font>
<span class="operator">}}</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_605">N<EFBFBD> version pour assurer
une compatibilit<69> ascendante</a></p>
<div class="manual_div_content">
La compatibilit<69> ascendante permet <20> une application de pouvoir d<>s<EFBFBD>rialiser un flux provenant
d'une version ant<6E>rieure.
La biblioth<74>que QxOrm impose un num<75>ro de version par classe ainsi qu'un num<75>ro de version pour
chaque propri<72>t<EFBFBD> enregistr<74>e dans le contexte QxOrm afin de pouvoir assurer une compatibilit<69>
ascendante automatiquement.
<br><br>
Par exemple, imaginons une classe <i>person</i> cr<63><72>e dans <b>une application en version A</b> :
nous renseignons dans la macro <i>QX_REGISTER_HPP</i> une n<> de version <20> 0 (correspond <20> la
1<>re version de notre classe <i>person</i>), ainsi qu'un n<> de version <20> 0 pour chacune des
propri<72>t<EFBFBD>s de la classe (si param<61>tre non renseign<67>, 0 est la valeur par d<>faut).
Ce qui donne le r<>sultat suivant :
<br><br>
<i>* Fichier person.h :</i><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="person.h">
<pre><span class="pre">#ifndef _PERSON_H_
#define _PERSON_H_
</span><span class="keyword">
class</span> person<span class="operator">
{</span><span class="keyword">
public</span><span class="operator">:</span><span class="type">
long</span> id<span class="operator">;</span>
QString firstName<span class="operator">;</span>
QString lastName<span class="operator">;</span>
QDateTime birthDate<span class="operator">;</span>
person<span class="operator">() :</span> id<span class="operator">(</span><span class="int">0</span><span class="operator">) { ; }</span><span class="keyword">
virtual</span><span class="operator"> ~</span>person<span class="operator">() { ; }
};</span>
QX_REGISTER_HPP_MY_TEST_EXE<span class="operator">(</span>person<span class="operator">,</span> qx<span class="operator">::</span>trait<span class="operator">::</span>no_base_class_defined<span class="operator">,</span><span class="int"><font style="background-color:yellow"> 0</font></span><span class="operator">)</span>
<span class="pre">#endif <span class="comment">// _PERSON_H_</span></span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<i>* Fichier person.cpp :</i><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="person.cpp">
<pre><span class="keyword">namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>person<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span>
t<span class="operator">.</span>id<span class="operator">(&amp;</span> person<span class="operator">::</span>id<span class="operator">,</span><span class="string"> "id"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>firstName<span class="operator">,</span><span class="string"> "first_name"</span><span class="operator">,</span><span class="int"><font style="background-color:yellow"> 0</font></span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>lastName<span class="operator">,</span><span class="string"> "last_name"</span><span class="operator">,</span><span class="int"><font style="background-color:yellow"> 0</font></span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>birthDate<span class="operator">,</span><span class="string"> "birth_date"</span><span class="operator">,</span><span class="int"><font style="background-color:yellow"> 0</font></span><span class="operator">);
}}</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
<b>Dans la version B de notre application</b>, nous modifions la classe <i>person</i> pour
ajouter 2 nouvelles propri<72>t<EFBFBD>s : <i>sex</i> et <i>address</i>. Notre classe ayant <20>volu<6C>e, il
faut donc incr<63>menter son n<> de version, et les nouvelles propri<72>t<EFBFBD>s doivent avoir un n<> de
version <20> 1, ce qui donne :
<br><br>
<i>* Fichier person.h :</i><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="person.h">
<pre><span class="pre">#ifndef _PERSON_H_
#define _PERSON_H_
</span><span class="keyword">
class</span> person<span class="operator">
{</span><span class="keyword">
public</span><span class="operator">:</span><span class="type">
long</span> id<span class="operator">;</span>
QString firstName<span class="operator">;</span>
QString lastName<span class="operator">;</span>
QDateTime birthDate<span class="operator">;</span>
<font style="background-color:yellow">QString sex<span class="operator">;</span></font>
<font style="background-color:yellow">QString address<span class="operator">;</span></font>
person<span class="operator">() :</span> id<span class="operator">(</span><span class="int">0</span><span class="operator">) { ; }</span><span class="keyword">
virtual</span><span class="operator"> ~</span>person<span class="operator">() { ; }
};</span>
QX_REGISTER_HPP_MY_TEST_EXE<span class="operator">(</span>person<span class="operator">,</span> qx<span class="operator">::</span>trait<span class="operator">::</span>no_base_class_defined<span class="operator">,</span><span class="int"><font style="background-color:yellow"> 1</font></span><span class="operator">)</span><span class="pre">
#endif // _PERSON_H_</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<i>* Fichier person.cpp :</i><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="person.cpp">
<pre><span class="keyword">namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>person<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span>
t<span class="operator">.</span>id<span class="operator">(&amp;</span> person<span class="operator">::</span>id<span class="operator">,</span><span class="string"> "id"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>firstName<span class="operator">,</span><span class="string"> "first_name"</span><span class="operator">,</span><span class="int"> 0</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>lastName<span class="operator">,</span><span class="string"> "last_name"</span><span class="operator">,</span><span class="int"> 0</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>birthDate<span class="operator">,</span><span class="string"> "birth_date"</span><span class="operator">,</span><span class="int"> 0</span><span class="operator">);</span>
<font style="background-color:yellow">t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>sex<span class="operator">,</span><span class="string"> "sex"</span><span class="operator">,</span><span class="int"> 1</span><span class="operator">);</span></font>
<font style="background-color:yellow">t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>address<span class="operator">,</span><span class="string"> "address"</span><span class="operator">,</span><span class="int"> 1</span><span class="operator">);</font>
}}</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
<b>Remarque :</b> en proc<6F>dant ainsi, la biblioth<74>que QxOrm peut s<>rialiser une instance de la
classe <i>person</i> dans une application en version A, puis d<>s<EFBFBD>rialiser <20> partir de ce flux
issu de la version A afin de recr<63>er une instance de la classe <i>person</i> dans une version B
de l'application.
<br><br>
<b>Autre remarque :</b> la suppression d'une propri<72>t<EFBFBD> casse la compatibilit<69> ascendante.
Il est donc recommand<6E> de ne jamais supprimer de propri<72>t<EFBFBD> pour utiliser le moteur de
s<>rialisation : il est possible par exemple de mettre une visibilit<69> <20> <i>private</i> et de
supprimer les accesseurs <i>get/set</i>, la propri<72>t<EFBFBD> devenant ainsi inaccessible <20> l'ext<78>rieur
de la classe, elle peut alors <20>tre consid<69>r<EFBFBD>e comme <20>tant obsol<6F>te.
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_600">Moteur QDataStream de
Qt</a></p>
<div class="manual_div_content">
Toute classe C++ enregistr<74>e dans le contexte QxOrm peut <20>tre s<>rialis<69>e en utilisant <a
href="http://doc.qt.io/qt-5/qdatastream.html" target="_blank">le moteur QDataStream de
Qt</a>.
Les fonctions pour utiliser ce type de s<>rialisation sont disponibles dans l'espace de nom : <a
href="../doxygen/html/namespaceqx_1_1serialization_1_1qt.html" target="_blank">namespace
qx::serialization::qt</a>.
<ul>
<li><a href="../doxygen/html/namespaceqx_1_1serialization_1_1qt.html"
target="_blank">qx::serialization::qt::to_byte_array()</a> : s<>rialise une instance C++
(dont le type est enregistr<74> dans le contexte QxOrm) en flux binaire de type <a
href="http://doc.qt.io/qt-5/qbytearray.html" target="_blank">QByteArray</a> ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1serialization_1_1qt.html"
target="_blank">qx::serialization::qt::from_byte_array()</a> : restaure une instance
C++ en fonction d'un flux binaire s<>rialis<69> de type <a
href="http://doc.qt.io/qt-5/qbytearray.html" target="_blank">QByteArray</a> ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1serialization_1_1qt.html"
target="_blank">qx::serialization::qt::to_string()</a> : m<>me s<>rialisation que la
fonction <i>to_byte_array()</i> + conversion en base 64 de type <a
href="http://doc.qt.io/qt-5/qstring.html" target="_blank">QString</a> ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1serialization_1_1qt.html"
target="_blank">qx::serialization::qt::from_string()</a> : restaure une instance C++ en
fonction d'un flux binaire s<>rialis<69> en base 64 ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1serialization_1_1qt.html"
target="_blank">qx::serialization::qt::to_file()</a> : s<>rialisation binaire d'une
instance C++ (dont le type est enregistr<74> dans le contexte QxOrm) dans un fichier ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1serialization_1_1qt.html"
target="_blank">qx::serialization::qt::from_file()</a> : restauration d'une instance
C++ <20> partir d'un fichier contenant le flux binaire s<>rialis<69> ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1serialization_1_1qt.html"
target="_blank">qx::serialization::qt::to_file_compressed()</a> : s<>rialisation binaire
d'une instance C++ (dont le type est enregistr<74> dans le contexte QxOrm) dans un fichier
compress<73> ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1serialization_1_1qt.html"
target="_blank">qx::serialization::qt::from_file_compressed()</a> : restauration d'une
instance C++ <20> partir d'un fichier compress<73> contenant le flux binaire s<>rialis<69>.</li>
</ul>
<b>Remarque :</b> la s<>rialisation <a href="http://doc.qt.io/qt-5/qdatastream.html"
target="_blank">QDataStream</a> est portable (s<>rialisation/d<>s<EFBFBD>rialisation compatible sur
tous types d'environnement : Windows, Linux, Mac OS X, etc...).
Le flux s<>rialis<69> est au format binaire : la taille du flux est donc r<>duite (compar<61> <20> un flux
XML par exemple).
La s<>rialisation <a href="http://doc.qt.io/qt-5/qdatastream.html"
target="_blank">QDataStream</a> <20>tant bas<61>e sur le moteur d'introspection de la biblioth<74>que
QxOrm, elle est moins performante que les s<>rialisations bas<61>es sur le moteur <a
href="http://www.boost.org/doc/libs/release/libs/serialization/doc/index.html"
target="_blank">boost::serialization</a>.
<br><br>
<b>Par exemple :</b><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="Qt QDataStream serialization">
<pre><span class="comment"> // Fetch a drug with id '3' in a new variable
// drug is a C++ class registered in QxOrm context
</span> drug d<span class="operator">;</span>
d<span class="operator">.</span>id<span class="operator"> =</span><span class="int"> 3</span><span class="operator">;</span>
QSqlError daoError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_id<span class="operator">(</span>d<span class="operator">);</span><span class="comment">
// Serialize the drug to a file
</span> qx<span class="operator">::</span>serialization<span class="operator">::</span>qt<span class="operator">::</span>to_file<span class="operator">(</span>d<span class="operator">,</span><span class="string"> "export_drug.txt"</span><span class="operator">);</span><span class="comment">
// Import drug from file in a new instance
</span> drug d2<span class="operator">;</span>
qx<span class="operator">::</span>serialization<span class="operator">::</span>qt<span class="operator">::</span>from_file<span class="operator">(</span>d2<span class="operator">,</span><span class="string"> "export_drug.txt"</span><span class="operator">);</span><span class="comment">
// Check if d == d2
</span> qAssert<span class="operator">(</span>d<span class="operator"> ==</span> d2<span class="operator">);</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<b>Remarque :</b> dans l'exemple ci-dessus, nous s<>rialisons une instance C++.
Toutes les fonctions du namespace <a href="../doxygen/html/namespaceqx_1_1serialization.html"
target="_blank">qx::serialization</a> peuvent <20>galement s<>rialiser des listes d'instances
C++.
Pour connaitre la liste des collections support<72>es, rendez-vous dans le chapitre : <a
href="#manual_390">Collections support<72>es par QxOrm</a>.
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_606">Moteur JSON de Qt</a></p>
<div class="manual_div_content">
Toute classe C++ enregistr<74>e dans le contexte QxOrm peut <20>tre s<>rialis<69>e en utilisant <a
href="http://doc.qt.io/qt-5/json.html" target="_blank">le moteur QJson de Qt</a> (n<>cessite
Qt5).
Les fonctions pour utiliser ce type de s<>rialisation sont disponibles dans l'espace de nom : <a
href="../doxygen/html/namespaceqx_1_1serialization_1_1json.html" target="_blank">namespace
qx::serialization::json</a>.
<ul>
<li><a href="../doxygen/html/namespaceqx_1_1serialization_1_1json.html"
target="_blank">qx::serialization::json::to_string()</a> : s<>rialise une instance C++
au format JSON ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1serialization_1_1json.html"
target="_blank">qx::serialization::json::from_string()</a> : restaure une instance C++
<20> partir d'un flux JSON ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1serialization_1_1json.html"
target="_blank">qx::serialization::json::to_file()</a> : s<>rialisation JSON d'une
instance C++ (dont le type est enregistr<74> dans le contexte QxOrm) dans un fichier ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1serialization_1_1json.html"
target="_blank">qx::serialization::json::from_file()</a> : restauration d'une instance
C++ <20> partir d'un fichier contenant le flux JSON s<>rialis<69> ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1serialization_1_1json.html"
target="_blank">qx::serialization::json::to_file_compressed()</a> : s<>rialisation JSON
d'une instance C++ (dont le type est enregistr<74> dans le contexte QxOrm) dans un fichier
compress<73> ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1serialization_1_1json.html"
target="_blank">qx::serialization::json::from_file_compressed()</a> : restauration
d'une instance C++ <20> partir d'un fichier compress<73> contenant le flux JSON s<>rialis<69> ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1serialization_1_1json.html"
target="_blank">qx::serialization::json::to_byte_array()</a> : s<>rialise une instance
C++ (dont le type est enregistr<74> dans le contexte QxOrm) en flux binaire de type <a
href="http://doc.qt.io/qt-5/qbytearray.html" target="_blank">QByteArray</a> ;</li>
<li><a href="../doxygen/html/namespaceqx_1_1serialization_1_1json.html"
target="_blank">qx::serialization::json::from_byte_array()</a> : restaure une instance
C++ en fonction d'un flux binaire s<>rialis<69> de type <a
href="http://doc.qt.io/qt-5/qbytearray.html" target="_blank">QByteArray</a>.</li>
</ul>
<b>Remarque :</b> le moteur de s<>rialisation <a href="http://doc.qt.io/qt-5/json.html"
target="_blank">JSON</a> est le plus permissif (compar<61> au <a href="#manual_610">moteur
XML</a> par exemple) : en effet, les propri<72>t<EFBFBD>s d'une instance peuvent <20>tre d<>finies dans
n'importe quel ordre, les propri<72>t<EFBFBD>s peuvent <20>tre supprim<69>es ou ajout<75>es.
La d<>s<EFBFBD>rialisation JSON ne retourne jamais d'erreur : elle ignore tout si le format des donn<6E>es
est incorrect (le flux JSON doit par contre <20>tre valide) ou bien si des propri<72>t<EFBFBD>s sont absentes
: le moteur JSON est donc beaucoup plus flexible que <a href="#manual_610">le moteur XML</a>.
<br><br>
<b>Autre remarque :</b> la s<>rialisation <a href="http://doc.qt.io/qt-5/json.html"
target="_blank">JSON</a> est bas<61>e sur le moteur d'introspection de la biblioth<74>que QxOrm,
elle est moins performante que les s<>rialisations bas<61>es sur le moteur <a
href="http://www.boost.org/doc/libs/release/libs/serialization/doc/index.html"
target="_blank">boost::serialization</a>.
<br><br>
<b>Par exemple :</b><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="Qt JSON serialization">
<pre><span class="comment"> // Fetch a list of authors from database and serialize them to a JSON file
</span> list_author list_of_author<span class="operator">;</span>
qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_all<span class="operator">(</span>list_of_author<span class="operator">);</span>
qx<span class="operator">::</span>serialization<span class="operator">::</span>json<span class="operator">::</span>to_file<span class="operator">(</span>list_of_author<span class="operator">,</span><span class="string"> "list_of_author.json"</span><span class="operator">);</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
L'exemple ci-dessus g<>n<EFBFBD>re le flux JSON suivant :<br>
<div class="json_pretty">
<pre>{
"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
}
}</pre>
</div>
<br><br>
<b>Remarque :</b> le <a href="#manual_97">module QxRestApi</a> de la biblioth<74>que QxOrm est bas<61>
sur le moteur de s<>rialisation JSON.
<br><br>
<b>Autre remarque :</b> il est possible de personnaliser le format de sortie JSON (filtrer les
propri<72>t<EFBFBD>s du flux JSON g<>n<EFBFBD>r<EFBFBD> par la s<>rialisation).
Les fonctions de s<>rialisation JSON dispose d'un param<61>tre optionnel de type <i>QString</i>
nomm<6D> <i>format</i>.
Les pr<70>-requis pour utiliser le param<61>tre <i>format</i> sont :
<ul>
<li>le param<61>tre <i>format</i> doit <20>tre pr<70>fix<69> par : <i><b>filter:</b></i> ;</li>
<li>les propri<72>t<EFBFBD>s <20> exporter sont d<>finies entre <i><b>{ }</b></i> ;</li>
<li>les relations sont s<>par<61>es par le caract<63>re <i><b>|</b></i> ;</li>
<li>il est possible d'utiliser le caract<63>re <i><b>*</b></i> pour d<>finir : <i>toutes les
relations sur 1 niveau</i> ;</li>
<li>le caract<63>re <i><b>-</b></i> devant les <i><b>{ }</b></i> signifie : <i>toutes les
propri<72>t<EFBFBD>s sauf</i>.</li>
</ul>
<br>
<b>Exemple :</b> voici un exemple de s<>rialisation JSON en d<>finissant un format de sortie pour
filtrer certaines propri<72>t<EFBFBD>s :
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="Qt JSON serialization">
<pre><span class="comment">// Serialize a C++ instance to a JSON string</span>
QString jsonFormat = <font style="background-color:yellow"><span class="string">"filter: { blog_text } | author_id { name, birthdate } | list_comment { comment_text } -&gt; blog_id -&gt; *"</span></font>;
QString outputJsonFiltered = qx::serialization::json::to_string(blog, <span class="int">1</span>, <font style="background-color:yellow">jsonFormat</font>);
qDebug(<span class="string">"[QxOrm] custom JSON serialization process (filtered) : \n%s"</span>, qPrintable(outputJsonFiltered));
<span class="comment">// Fill a C++ instance based on a JSON string</span>
blog_ptr blogFromJsonFiltered; blogFromJsonFiltered.reset(<span class="operator">new</span> blog());
qx::serialization::json::from_string(blogFromJsonFiltered, outputJsonFiltered, <span class="int">1</span>, <font style="background-color:yellow">jsonFormat</font>);
qx::dump(blogFromJsonFiltered);
qAssert(blogFromJsonFiltered-&gt;m_text != <span class="string">""</span>); <span class="comment">// Fetched</span>
qAssert(blogFromJsonFiltered-&gt;m_dt_creation.isNull()); <span class="comment">// Not fetched</span>
qAssert(blogFromJsonFiltered-&gt;m_author->m_sex == author::unknown); <span class="comment">// Not fetched</span>
qAssert(blogFromJsonFiltered-&gt;m_author->m_name != <span class="string">""</span>); <span class="comment">// Fetched</span>
qAssert(blogFromJsonFiltered-&gt;m_commentX.size() > <span class="int">0</span>);
qAssert(blogFromJsonFiltered-&gt;m_commentX[0]->m_dt_create.isNull()); <span class="comment">// Not fetched</span>
qAssert(blogFromJsonFiltered-&gt;m_commentX[0]->m_text != <span class="string">""</span>); <span class="comment">// Fetched</span>
qAssert(blogFromJsonFiltered-&gt;m_commentX[0]->m_blog);</pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_610">Moteur XML de
boost::serialization</a></p>
<div class="manual_div_content">
Le moteur XML de <a
href="http://www.boost.org/doc/libs/release/libs/serialization/doc/index.html"
target="_blank">boost::serialization</a> n'est pas activ<69> par d<>faut : pour activer cette
fonctionnalit<69>, il est n<>cessaire de d<>finir les options de compilation
<b>_QX_ENABLE_BOOST_SERIALIZATION</b> et <b>_QX_ENABLE_BOOST_SERIALIZATION_XML</b> dans <a
href="#manual_220">le fichier de configuration QxOrm.pri (ou QxOrm.cmake)</a>.
Il est <20>galement n<>cessaire de compiler le binaire <a
href="http://www.boost.org/doc/libs/release/libs/serialization/doc/index.html"
target="_blank">boost::serialization</a> (ce module de boost n'<27>tant pas <i>header only</i>),
et de renseigner le chemin d'acc<63>s <20> ce binaire dans les variables <b>QX_BOOST_LIB_PATH</b>,
<b>QX_BOOST_LIB_SERIALIZATION_DEBUG</b> et <b>QX_BOOST_LIB_SERIALIZATION_RELEASE</b> du <a
href="#manual_220">fichier de configuration QxOrm.pri (ou QxOrm.cmake)</a>.
<br><br>
Toute classe C++ enregistr<74>e dans le contexte QxOrm peut <20>tre s<>rialis<69>e en utilisant <a
href="http://www.boost.org/doc/libs/release/libs/serialization/doc/index.html"
target="_blank">le moteur XML de boost::serialization</a>.
Les fonctions pour utiliser ce type de s<>rialisation sont disponibles dans l'espace de nom : <a
href="../doxygen/html/namespaceqx_1_1serialization_1_1xml.html" target="_blank">namespace
qx::serialization::xml</a> (m<>mes fonctions que dans <a href="#manual_600">l'espace de nom
qx::serialization::qt</a>).
<br><br>
Ce type de s<>rialisation poss<73>de les caract<63>ristiques suivantes :
<ul>
<li><i>portable</i> : compatible sur tous types d'environnement : Windows, Linux, Mac OS X,
etc... ;</li>
<li><i>slowest</i> : plus lente que les s<>rialisations <i>binary</i> et <i>text</i> ;</li>
<li><i>largest</i> : taille des flux g<>n<EFBFBD>r<EFBFBD>s plus importante que les s<>rialisations
<i>binary</i> et <i>text</i> ;
</li>
<li><i>human-readable</i> : un flux XML peut facilement <20>tre analys<79> et lu par un <20>diteur
externe ou un <20>tre humain.</li>
</ul>
<br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_620">Moteur binaire de
boost::serialization</a></p>
<div class="manual_div_content">
Le moteur binaire de <a
href="http://www.boost.org/doc/libs/release/libs/serialization/doc/index.html"
target="_blank">boost::serialization</a> n'est pas activ<69> par d<>faut : pour activer cette
fonctionnalit<69>, il est n<>cessaire de d<>finir les options de compilation
<b>_QX_ENABLE_BOOST_SERIALIZATION</b> et <b>_QX_ENABLE_BOOST_SERIALIZATION_BINARY</b> dans <a
href="#manual_220">le fichier de configuration QxOrm.pri (ou QxOrm.cmake)</a>.
Il est <20>galement n<>cessaire de compiler le binaire <a
href="http://www.boost.org/doc/libs/release/libs/serialization/doc/index.html"
target="_blank">boost::serialization</a> (ce module de boost n'<27>tant pas <i>header only</i>),
et de renseigner le chemin d'acc<63>s <20> ce binaire dans les variables <b>QX_BOOST_LIB_PATH</b>,
<b>QX_BOOST_LIB_SERIALIZATION_DEBUG</b> et <b>QX_BOOST_LIB_SERIALIZATION_RELEASE</b> du <a
href="#manual_220">fichier de configuration QxOrm.pri (ou QxOrm.cmake)</a>.
<br><br>
Toute classe C++ enregistr<74>e dans le contexte QxOrm peut <20>tre s<>rialis<69>e en utilisant <a
href="http://www.boost.org/doc/libs/release/libs/serialization/doc/index.html"
target="_blank">le moteur binaire de boost::serialization</a>.
Les fonctions pour utiliser ce type de s<>rialisation sont disponibles dans l'espace de nom : <a
href="../doxygen/html/namespaceqx_1_1serialization_1_1xml.html" target="_blank">namespace
qx::serialization::binary</a> (m<>mes fonctions que dans <a href="#manual_600">l'espace de nom
qx::serialization::qt</a>).
<br><br>
Ce type de s<>rialisation poss<73>de les caract<63>ristiques suivantes :
<ul>
<li><i>non-portable</i> : un flux s<>rialis<69> sur un environnement Windows peut <20>tre
incompatible si d<>s<EFBFBD>rialisation sur un environnement Linux par exemple : il est donc
fortement recommand<6E> de rester sur le m<>me environnement ;</li>
<li><i>fastest</i> : plus rapide que les s<>rialisations <i>XML</i> et <i>text</i> ;</li>
<li><i>smallest</i> : taille des flux g<>n<EFBFBD>r<EFBFBD>s r<>duite compar<61> aux s<>rialisations <i>XML</i>
et <i>text</i> ;</li>
<li><i>non-human-readable</i> : un flux binaire n'est pas lisible (pas de log possible par
exemple).</li>
</ul>
<br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_630">Autres types de
s<>rialisation propos<6F>s par boost</a></p>
<div class="manual_div_content">
Le moteur <a href="http://www.boost.org/doc/libs/release/libs/serialization/doc/index.html"
target="_blank">boost::serialization</a> propose d'autres types de s<>rialisation.
Ces diff<66>rents types ne sont pas activ<69>s par d<>faut, pour utiliser ces fonctionnalit<69>s (m<>mes
fonctions que dans <a href="#manual_600">l'espace de nom qx::serialization::qt</a>), il est
n<>cessaire de d<>finir les options de compilation suivantes dans <a href="#manual_220">le fichier
de configuration QxOrm.pri (ou QxOrm.cmake)</a> :
<ul>
<li><b>_QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC :</b> active les fonctions des espaces de
nom <a href="../doxygen/html/namespaceqx_1_1serialization_1_1polymorphic__binary.html"
target="_blank">qx::serialization::polymorphic_binary</a>, <a
href="../doxygen/html/namespaceqx_1_1serialization_1_1polymorphic__xml.html"
target="_blank">qx::serialization::polymorphic_xml</a> et <a
href="../doxygen/html/namespaceqx_1_1serialization_1_1polymorphic__text.html"
target="_blank">qx::serialization::polymorphic_text</a> ;</li>
<li><b>_QX_ENABLE_BOOST_SERIALIZATION_TEXT :</b> active les fonctions de l'espace de nom <a
href="../doxygen/html/namespaceqx_1_1serialization_1_1text.html"
target="_blank">qx::serialization::text</a> ;</li>
<li><b>_QX_ENABLE_BOOST_SERIALIZATION_PORTABLE_BINARY :</b> active les fonctions de l'espace
de nom <a href="../doxygen/html/namespaceqx_1_1serialization_1_1portable__binary.html"
target="_blank">qx::serialization::portable_binary</a> (non support<72> officiellement par
la biblioth<74>que boost) ;</li>
<li><b>_QX_ENABLE_BOOST_SERIALIZATION_WIDE_BINARY :</b> active les fonctions de l'espace de
nom <a href="../doxygen/html/namespaceqx_1_1serialization_1_1wide_1_1binary.html"
target="_blank">qx::serialization::wide::binary</a> ;</li>
<li><b>_QX_ENABLE_BOOST_SERIALIZATION_WIDE_TEXT :</b> active les fonctions de l'espace de nom
<a href="../doxygen/html/namespaceqx_1_1serialization_1_1wide_1_1text.html"
target="_blank">qx::serialization::wide::text</a> ;
</li>
<li><b>_QX_ENABLE_BOOST_SERIALIZATION_WIDE_XML :</b> active les fonctions de l'espace de nom
<a href="../doxygen/html/namespaceqx_1_1serialization_1_1wide_1_1xml.html"
target="_blank">qx::serialization::wide::xml</a>.
</li>
</ul>
<br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_640">Cloner une instance
C++</a></p>
<div class="manual_div_content">
Toute classe C++ enregistr<74>e dans le contexte QxOrm peut <20>tre clon<6F>e en utilisant une des
fonctions suivantes :
<ul>
<li><a href="../doxygen/html/_qx_clone_8h.html" target="_blank">qx::clone&lt;T&gt;(const T &
t) :</a> cr<63>ation d'une nouvelle instance identique <20> <i>t</i> de type
<i>std::shared_ptr&lt;T&gt;</i> (par d<>faut type <20>gal <20> <i>boost::shared_ptr&lt;T&gt;</i>,
si C++11 activ<69> alors type <20>gal <20> <i>std::shared_ptr&lt;T&gt;</i>) ;
</li>
<li><a href="../doxygen/html/_qx_clone_8h.html"
target="_blank">qx::clone_to_boost_shared_ptr&lt;T&gt;(const T & t) :</a> cr<63>ation
d'une nouvelle instance identique <20> <i>t</i> de type <i>boost::shared_ptr&lt;T&gt;</i> ;
</li>
<li><a href="../doxygen/html/_qx_clone_8h.html"
target="_blank">qx::clone_to_qt_shared_ptr&lt;T&gt;(const T & t) :</a> cr<63>ation d'une
nouvelle instance identique <20> <i>t</i> de type <i>QSharedPointer&lt;T&gt;</i> ;</li>
<li><a href="../doxygen/html/_qx_clone_8h.html"
target="_blank">qx::clone_to_std_shared_ptr&lt;T&gt;(const T & t) :</a> cr<63>ation d'une
nouvelle instance identique <20> <i>t</i> de type <i>std::shared_ptr&lt;T&gt;</i> ;</li>
<li><a href="../doxygen/html/_qx_clone_8h.html"
target="_blank">qx::clone_to_nude_ptr&lt;T&gt;(const T & t) :</a> cr<63>ation d'une
nouvelle instance identique <20> <i>t</i> sous forme de pointeur nu : attention <20> lib<69>rer la
m<>moire (<i>delete</i>) une fois l'utilisation du pointeur nu termin<69>e (afin d'<27>viter des
fuites m<>moire).</li>
</ul>
<b>Par exemple :</b>
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="qx::clone">
<pre> drug_ptr d1<span class="operator">;</span>
d1<span class="operator">.</span>reset<span class="operator">(</span><span class="keyword">new</span> drug<span class="operator">());</span>
d1<span class="operator">-&gt;</span>name<span class="operator"> =</span><span class="string"> "name1"</span><span class="operator">;</span>
d1<span class="operator">-&gt;</span>description<span class="operator"> =</span><span class="string"> "desc1"</span><span class="operator">;</span><span class="comment">
// Clone a drug
</span> <font style="background-color:yellow">drug_ptr d_clone<span class="operator"> =</span> qx<span class="operator">::</span>clone<span class="operator">(*</span> d1<span class="operator">);</span></font><span class="comment">
// Check if (d1 == d_clone)
</span> qAssert<span class="operator">((*</span> d1<span class="operator">) == (*</span> d_clone<span class="operator">));</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<b>Remarque importante :</b> il faut faire attention lorsqu'on clone un pointeur intelligent
(<i>boost::shared_ptr</i> ou <i>QSharedPointer</i> par exemple) dont l'<27>l<EFBFBD>ment parent
(<i>root</i>) peut <20>tre r<>f<EFBFBD>renc<6E> plusieurs fois dans sa hi<68>rarchie (cas d'une structure en
arbre par exemple).
Dans ce cas, afin de prot<6F>ger le pointeur parent d'une double suppression (2 pointeurs
intelligents qui pointent sur le m<>me pointeur brut), il est conseill<6C> de cloner de cette fa<66>on
:
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="qx::clone smart-pointer">
<pre><span class="comment">// 'pOther' type is boost::shared_ptr&lt;myClass&gt; (smart-pointer)
</span>boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>myClass<span class="operator">&gt; *</span> pCloneTemp<span class="operator"> =</span> qx<span class="operator">::</span>clone_to_nude_ptr<span class="operator">(</span>pOther<span class="operator">);</span>
boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>myClass<span class="operator">&gt;</span> pClone<span class="operator"> = (</span>pCloneTemp<span class="operator"> ? (*</span> pCloneTemp<span class="operator">) :</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>myClass<span class="operator">&gt;());</span><span class="flow">
if</span><span class="operator"> (</span>pCloneTemp<span class="operator">) {</span><span class="keyword"> delete</span> pCloneTemp<span class="operator">;</span> pCloneTemp<span class="operator"> =</span> NULL<span class="operator">; }</span><span class="comment">
// Now use 'pClone' ...</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_650">Afficher le d<>tail d'une
instance C++ (dump au format XML ou JSON)</a></p>
<div class="manual_div_content">
Toute instance C++ enregistr<74>e dans le contexte QxOrm peut <20>tre affich<63>e <a
href="#manual_606">au format JSON</a>.
Si le <a href="#manual_610">moteur XML de boost::serialization</a> est activ<69>, alors il est
<20>galement possible d'afficher un dump sous format XML (param<61>tre d'entr<74>e de la fonction
<i>qx::dump</i>).
Cette fonctionnalit<69> peut <20>tre utile pour faire du d<>bogage par exemple, ou bien pour g<>n<EFBFBD>rer
des logs.
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="qx::dump">
<pre> blog_ptr b<span class="operator">;</span>
b<span class="operator">.</span>reset<span class="operator">(</span><span class="keyword">new</span> blog<span class="operator">());</span>
b<span class="operator">-&gt;</span>id<span class="operator"> =</span><span class="int"> 36</span><span class="operator">;</span>
qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_id_with_all_relation<span class="operator">(</span>b<span class="operator">);</span><span class="comment">
// Dump 'b' instance result from database (XML or JSON serialization)
// Second parameter is optional : 'true' = JSON format, 'false' = XML format
</span> <font style="background-color:yellow">qx<span class="operator">::</span>dump<span class="operator">(</span>b, false<span class="operator">);</span></font></pre>
</td>
</tr>
</tbody>
</table>
<br>
Ce qui g<>n<EFBFBD>re le flux XML suivant :
<br><br>
<div style="width:900px; height:200px; overflow:auto; background-color:white">
<pre>
[QxOrm] start dump 'boost::shared_ptr&lt;blog&gt;'
&lt;boost.shared_ptr-blog- class_id="0" tracking_level="0" version="1"&gt;
&lt;px class_id="1" tracking_level="1" version="0" object_id="_0"&gt;
&lt;blog_id&gt;113&lt;/blog_id&gt;
&lt;blog_text class_id="2" tracking_level="0" version="0"&gt;update blog_text_1&lt;/blog_text&gt;
&lt;date_creation class_id="3" tracking_level="0" version="0"&gt;20100409162612000&lt;/date_creation&gt;
&lt;author_id class_id="4" tracking_level="0" version="1"&gt;
&lt;px class_id="5" tracking_level="1" version="0" object_id="_1"&gt;
&lt;author_id&gt;author_id_2&lt;/author_id&gt;
&lt;name&gt;author_2&lt;/name&gt;
&lt;birthdate class_id="6" tracking_level="0" version="0"&gt;20100409&lt;/birthdate&gt;
&lt;sex&gt;1&lt;/sex&gt;
&lt;list_blog class_id="7" tracking_level="0" version="0"&gt;
&lt;count&gt;0&lt;/count&gt;
&lt;item_version&gt;1&lt;/item_version&gt;
&lt;/list_blog&gt;
&lt;/px&gt;
&lt;/author_id&gt;
&lt;list_comment class_id="8" tracking_level="0" version="0"&gt;
&lt;count&gt;2&lt;/count&gt;
&lt;item class_id="9" tracking_level="0" version="1"&gt;
&lt;px class_id="10" tracking_level="1" version="0" object_id="_2"&gt;
&lt;comment_id&gt;209&lt;/comment_id&gt;
&lt;comment_text&gt;comment_1 text&lt;/comment_text&gt;
&lt;date_creation&gt;20100409162612000&lt;/date_creation&gt;
&lt;blog_id&gt;
&lt;px class_id_reference="1" object_id="_3"&gt;
&lt;blog_id&gt;113&lt;/blog_id&gt;
&lt;blog_text&gt;&lt;/blog_text&gt;
&lt;date_creation&gt;&lt;/date_creation&gt;
&lt;author_id&gt;
&lt;px class_id="-1"&gt;&lt;/px&gt;
&lt;/author_id&gt;
&lt;list_comment&gt;
&lt;count&gt;0&lt;/count&gt;
&lt;/list_comment&gt;
&lt;list_category class_id="11" tracking_level="0" version="0"&gt;
&lt;count&gt;0&lt;/count&gt;
&lt;/list_category&gt;
&lt;/px&gt;
&lt;/blog_id&gt;
&lt;/px&gt;
&lt;/item&gt;
&lt;item&gt;
&lt;px class_id_reference="10" object_id="_4"&gt;
&lt;comment_id&gt;210&lt;/comment_id&gt;
&lt;comment_text&gt;comment_2 text&lt;/comment_text&gt;
&lt;date_creation&gt;20100409162612000&lt;/date_creation&gt;
&lt;blog_id&gt;
&lt;px class_id_reference="1" object_id="_5"&gt;
&lt;blog_id&gt;113&lt;/blog_id&gt;
&lt;blog_text&gt;&lt;/blog_text&gt;
&lt;date_creation&gt;&lt;/date_creation&gt;
&lt;author_id&gt;
&lt;px class_id="-1"&gt;&lt;/px&gt;
&lt;/author_id&gt;
&lt;list_comment&gt;
&lt;count&gt;0&lt;/count&gt;
&lt;/list_comment&gt;
&lt;list_category&gt;
&lt;count&gt;0&lt;/count&gt;
&lt;/list_category&gt;
&lt;/px&gt;
&lt;/blog_id&gt;
&lt;/px&gt;
&lt;/item&gt;
&lt;/list_comment&gt;
&lt;list_category&gt;
&lt;count&gt;2&lt;/count&gt;
&lt;item class_id="12" tracking_level="0" version="0"&gt;
&lt;first&gt;355&lt;/first&gt;
&lt;second class_id="13" tracking_level="0" version="0"&gt;
&lt;qt_shared_ptr class_id="14" tracking_level="1" version="0" object_id="_6"&gt;
&lt;category_id&gt;355&lt;/category_id&gt;
&lt;name&gt;category_1&lt;/name&gt;
&lt;description&gt;desc_1&lt;/description&gt;
&lt;list_blog class_id="15" tracking_level="0" version="0"&gt;
&lt;count&gt;0&lt;/count&gt;
&lt;/list_blog&gt;
&lt;/qt_shared_ptr&gt;
&lt;/second&gt;
&lt;/item&gt;
&lt;item&gt;
&lt;first&gt;357&lt;/first&gt;
&lt;second&gt;
&lt;qt_shared_ptr class_id_reference="14" object_id="_7"&gt;
&lt;category_id&gt;357&lt;/category_id&gt;
&lt;name&gt;category_3&lt;/name&gt;
&lt;description&gt;desc_3&lt;/description&gt;
&lt;list_blog&gt;
&lt;count&gt;0&lt;/count&gt;
&lt;/list_blog&gt;
&lt;/qt_shared_ptr&gt;
&lt;/second&gt;
&lt;/item&gt;
&lt;/list_category&gt;
&lt;/px&gt;
&lt;/boost.shared_ptr-blog-&gt;
[QxOrm] end dump 'boost::shared_ptr&lt;blog&gt;'
</pre>
</div>
<br>
</div>
</div>
<p class="manual_p_title_1"><a class="manual_a_title_1" name="manual_70">Introspection - R<>flexion</a>
</p>
<div class="manual_div_content_1">
Toute classe enregistr<74>e dans le contexte QxOrm par la m<>thode <i>qx::register_class&lt;T&gt;()</i>
peut <20>tre utilis<69>e par le moteur d'introspection (ou r<>flexion) de la biblioth<74>que QxOrm.
Le moteur d'introspection permet d'obtenir de fa<66>on dynamique (donc pendant l'ex<65>cution du
programme) des informations propres <20> un type.
Ces informations correspondent <20> des <i>m<EFBFBD>ta-donn<6E>es</i> et d<>crivent de fa<66>on exhaustive les
caract<63>ristiques d'une classe (propri<72>t<EFBFBD>s, m<>thodes, etc.).
De nombreux langages de programmation (par exemple Java ou C#) int<6E>grent nativement ce m<>canisme,
ce n'est pas le cas du C++, c'est pourquoi la biblioth<74>que QxOrm <20>mule un moteur d'introspection.
Pour plus de d<>tails sur l'introspection (ou r<>flexion), <a
href="https://en.wikipedia.org/wiki/Reflection_(computer_programming)"
target="_blank">rendez-vous sur la page Wikipedia</a>.
<br><br>
Voici la liste des classes disponibles pour acc<63>der aux <i>m<EFBFBD>ta-donn<6E>es</i> :
<ul>
<li><a href="../doxygen/html/classqx_1_1_qx_class_x.html" target="_blank">qx::QxClassX</a> :
singleton permettant de parcourir l'ensemble des classes enregistr<74>es dans le contexte QxOrm
par la m<>thode <i>qx::register_class&lt;T&gt;()</i> ;</li>
<li><a href="../doxygen/html/classqx_1_1_ix_class.html" target="_blank">qx::IxClass</a> :
interface pour une classe enregistr<74>e dans le contexte QxOrm ;</li>
<li><a href="../doxygen/html/classqx_1_1_ix_data_member_x.html"
target="_blank">qx::IxDataMemberX</a> : liste des propri<72>t<EFBFBD>s associ<63>es <20> une classe ;</li>
<li><a href="../doxygen/html/classqx_1_1_ix_data_member.html"
target="_blank">qx::IxDataMember</a> : interface pour une propri<72>t<EFBFBD> d'une classe ;</li>
<li><a href="../doxygen/html/classqx_1_1_ix_function.html" target="_blank">qx::IxFunctionX</a> :
liste des m<>thodes associ<63>es <20> une classe ;</li>
<li><a href="../doxygen/html/classqx_1_1_ix_function.html" target="_blank">qx::IxFunction</a> :
interface pour une m<>thode d'une classe (<i>static</i> ou <i>non static</i>).</li>
</ul>
Une instance de type <a href="../doxygen/html/classqx_1_1_ix_class.html"
target="_blank"><i>qx::IxClass</i></a> poss<73>de la liste des propri<72>t<EFBFBD>s d'une classe (<a
href="../doxygen/html/classqx_1_1_ix_data_member_x.html"
target="_blank"><i>qx::IxDataMemberX</i></a>) ainsi que la liste des m<>thodes d'une classe (<a
href="../doxygen/html/classqx_1_1_ix_function.html"
target="_blank"><i>qx::IxFunctionX</i></a>).<br>
<br>
Le moteur d'introspection de la biblioth<74>que QxOrm permet par exemple de :
<ul>
<li><a href="#manual_740">cr<EFBFBD>er dynamiquement une instance en fonction du nom d'une classe sous
forme de cha<68>ne de caract<63>res (<i>qx::create()</i>)</a> ;</li>
<li><a href="#manual_710">acc<EFBFBD>der/modifier le contenu d'un champ d'un objet de fa<66>on
dynamique</a> en prenant pour param<61>tres un objet et le nom du champ qu'on souhaite
acc<63>der/modifier (<i>qx::IxDataMember::getValue()</i> et <i>qx::IxDataMember::setValue()</i>)
;</li>
<li><a href="#manual_730">invoquer une m<>thode de classe de fa<66>on dynamique</a>, en g<>rant bien
entendu le passage des param<61>tres souhait<69>s <20> la m<>thode (<i>qx::IxFunction::invoke()</i>) ;
</li>
<li><a href="#manual_750">acc<EFBFBD>der <20> la hi<68>rarchie d'une classe
(<i>qx::IxClass::getBaseClass()</i>)</a>.</li>
</ul>
<br>
<b>Remarque :</b> le module <a href="../doxygen/html/group___qx_service.html"
target="_blank">QxService</a> de la biblioth<74>que QxOrm (<a href="./tutorial_2.html"
target="_blank">cliquez ici pour acc<63>der au tutoriel</a>) permettant de cr<63>er un serveur
d'applications C++ est bas<61> sur le moteur d'introspection pour appeler dynamiquement les m<>thodes
de type service (demande du client) sur le serveur, ainsi que pour cr<63>er dynamiquement les
instances des classes de param<61>tre (entr<74>e/sortie).<br>
<br>
<b>Autre remarque :</b> il est possible d'ajouter de nouvelles informations au moteur
d'introspection en utilisant <a href="../doxygen/html/classqx_1_1_qx_property_bag.html"
target="_blank">la notion de <i>property bag</i></a>.
En effet, les classes <i>qx::IxClass</i>, <i>qx::IxDataMember</i> et <i>qx::IxFunction</i>
poss<73>dent chacune une liste d'<27>l<EFBFBD>ments de type <i>QVariant</i> accessibles par cl<63> de type
<i>QString</i> (voir <a href="../doxygen/html/classqx_1_1_qx_property_bag.html" target="_blank">la
classe <i>qx::QxPropertyBag</i></a> pour plus de d<>tails sur cette notion).
<br><br>
<b>Autre remarque :</b> afin d'initialiser le moteur d'introspection QxOrm, il est recommand<6E>
d'appeler la fonction suivante en d<>but de programme (<i>main</i> par exemple) :
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="Register introspection engine">
<pre><span class="comment">// Following command is recommanded to initialize QxOrm introspection engine</span>
qx::QxClassX::registerAllClasses(true);</pre>
</td>
</tr>
</tbody>
</table>
<br><br>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_710">Obtenir dynamiquement la
valeur d'une donn<6E>e membre</a></p>
<div class="manual_div_content">
Pour obtenir dynamiquement la valeur d'une donn<6E>e membre en utilisant le moteur d'introspection
de la biblioth<74>que QxOrm, il est n<>cessaire de passer par la classe : <a
href="../doxygen/html/classqx_1_1_ix_data_member.html" target="_blank">qx::IxDataMember</a>.
La classe <a href="../doxygen/html/classqx_1_1_ix_data_member.html"
target="_blank">qx::IxDataMember</a> fournit plusieurs m<>thodes pour obtenir la valeur d'une
donn<6E>e membre (chacune prenant en param<61>tre un pointeur g<>n<EFBFBD>rique de type <i>void *</i>
correspondant <20> l'adresse de l'instance courante) :
<ul>
<li><a href="../doxygen/html/classqx_1_1_ix_data_member.html" target="_blank">toVariant()</a>
: retourne la valeur de la donn<6E>e membre convertie en <a
href="http://doc.qt.io/qt-5/qvariant.html" target="_blank">QVariant</a> ;</li>
<li><a href="../doxygen/html/classqx_1_1_ix_data_member.html"
target="_blank">getValue&lt;T&gt;()</a> : retourne la valeur avec son type r<>el pr<70>cis<69>
dans le param<61>tre template T ;</li>
<li><a href="../doxygen/html/classqx_1_1_ix_data_member.html"
target="_blank">getValueAnyPtr()</a> : retourne la valeur encapsul<75>e par le type <a
href="http://www.boost.org/doc/libs/release/doc/html/any.html"
target="_blank">qx::any</a> (possibilit<69> de caster ensuite en utilisant : <a
href="http://www.boost.org/doc/libs/release/doc/html/any.html"
target="_blank">qx::any_cast</a>).</li>
</ul>
<b>Par exemple :</b> imaginons un pointeur g<>n<EFBFBD>rique de type <i>void *</i> vers une classe
<i>person</i>. Nous pouvons obtenir la valeur de la propri<72>t<EFBFBD> <i>firstName</i> de type
<i>QString</i> de la fa<66>on suivante :
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="Introspection engine">
<pre><span class="comment">// Generic pointer of type void * : we know that p is of type 'person'
</span><span class="type">void</span><span class="operator"> *</span> p<span class="operator"> = ...;</span><span class="comment">
// Get a pointer to the registered data member 'firstName' of class 'person'
</span>qx<span class="operator">::</span>IxDataMember<span class="operator"> *</span> pDataMember<span class="operator"> =</span> qx<span class="operator">::</span>QxClassX<span class="operator">::</span>getDataMember<span class="operator">(</span><span class="string">"person"</span><span class="operator">,</span><span class="string"> "firstName"</span><span class="operator">);</span><span class="comment">
// First method to get the data member value with the real type
</span>QString sFirstName<span class="operator"> =</span> pDataMember<span class="operator">-&gt;</span>getValue<span class="operator">&lt;</span>QString<span class="operator">&gt;(</span>p<span class="operator">);</span><span class="comment">
// Second method to get the data member value converted in QVariant
</span>QVariant vFirstName<span class="operator"> =</span> pDataMember<span class="operator">-&gt;</span>toVariant<span class="operator">(</span>p<span class="operator">);</span><span class="comment">
// Third method to get the value encapsulated in qx::any type
</span>boost<span class="operator">::</span>any aFirstName<span class="operator"> =</span> pDataMember<span class="operator">-&gt;</span>getValueAnyPtr<span class="operator">(</span>p<span class="operator">);</span><span class="comment">
// Check if all values are equals
</span>qAssert<span class="operator">((</span>sFirstName<span class="operator"> ==</span> vFirstName<span class="operator">.</span>toString<span class="operator">()) &amp;&amp; (</span>sFirstName<span class="operator"> == (*</span> boost<span class="operator">::</span>any_cast<span class="operator">&lt;</span>QString<span class="operator"> *&gt;(</span>aFirstName<span class="operator">))));</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_720">Valoriser dynamiquement
une donn<6E>e membre</a></p>
<div class="manual_div_content">
De la m<>me fa<66>on que pour obtenir la valeur d'une donn<6E>e membre, la classe <a
href="../doxygen/html/classqx_1_1_ix_data_member.html" target="_blank">qx::IxDataMember</a>
permet de valoriser une donn<6E>e membre (modifier sa valeur).
La classe <a href="../doxygen/html/classqx_1_1_ix_data_member.html"
target="_blank">qx::IxDataMember</a> fournit les 2 m<>thodes suivantes (chacune prend en
param<61>tre un pointeur de type <i>void *</i> correspondant <20> l'adresse de l'instance courante,
ainsi que la nouvelle valeur <20> positionner) :
<ul>
<li><a href="../doxygen/html/classqx_1_1_ix_data_member.html"
target="_blank">fromVariant()</a> : valorise la donn<6E>e membre en fonction du param<61>tre
de type <a href="http://doc.qt.io/qt-5/qvariant.html" target="_blank">QVariant</a> ;</li>
<li><a href="../doxygen/html/classqx_1_1_ix_data_member.html"
target="_blank">setValue&lt;T&gt;()</a> : valorise la donn<6E>e membre avec un param<61>tre
du type r<>el T de la donn<6E>e membre.</li>
</ul>
<b>Par exemple :</b> imaginons un pointeur g<>n<EFBFBD>rique de type <i>void *</i> vers une classe
<i>person</i>. Nous pouvons modifier la valeur de la propri<72>t<EFBFBD> <i>firstName</i> de type
<i>QString</i> de la fa<66>on suivante :
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="Introspection engine">
<pre><span class="comment">// Generic pointer of type void * : we know that p is of type 'person'
</span><span class="type">void</span><span class="operator"> *</span> p<span class="operator"> = ...;</span><span class="comment">
// Get a pointer to the registered data member 'firstName' of class 'person'
</span>qx<span class="operator">::</span>IxDataMember<span class="operator"> *</span> pDataMember<span class="operator"> =</span> qx<span class="operator">::</span>QxClassX<span class="operator">::</span>getDataMember<span class="operator">(</span><span class="string">"person"</span><span class="operator">,</span><span class="string"> "firstName"</span><span class="operator">);</span><span class="comment">
// First method to change the data member value
</span>QVariant vFirstName<span class="operator"> =</span> QVariant<span class="operator">(</span><span class="string">"my new firstname 1"</span><span class="operator">);</span>
pDataMember<span class="operator">-&gt;</span>fromVariant<span class="operator">(</span>p<span class="operator">,</span> vFirstName<span class="operator">);</span><span class="comment">
// Other method to change the data member value (using real type)
</span>QString sFirstName<span class="operator"> =</span><span class="string"> "other firstname 2"</span><span class="operator">;</span>
pDataMember<span class="operator">-&gt;</span>setValue<span class="operator">&lt;</span>QString<span class="operator">&gt;(</span>p<span class="operator">,</span> sFirstName<span class="operator">);</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_730">Appeler dynamiquement une
fonction</a></p>
<div class="manual_div_content">
Tout comme les donn<6E>es membre (propri<72>t<EFBFBD>s), il est possible d'enregistrer des m<>thodes membre
(fonctions) dans le contexte QxOrm (support des m<>thodes <i>static</i> et <i>non static</i>).
Le moteur d'introspection de la biblioth<74>que QxOrm permet d'invoquer dynamiquement des m<>thodes
de classe.
Toutes les fonctions enregistr<74>es dans le contexte QxOrm sont associ<63>es <20> une instance de la
classe : <a href="../doxygen/html/classqx_1_1_ix_function.html"
target="_blank">qx::IxFunction</a>.
Pour enregistrer des m<>thodes dans le contexte QxOrm, il faut utiliser :
<ul>
<li><a href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::fct_0()</a> : enregistre une m<>thode de classe
sans param<61>tre ;</li>
<li><a href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::fct_1()</a> : enregistre une m<>thode de classe
avec 1 param<61>tre ;</li>
<li><a href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::fct_2()</a> : enregistre une m<>thode de classe
avec 2 param<61>tres ;</li>
<li>etc... <a href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::fct_X()</a> : le suffixe <i>X</i> correspondant
au nombre de param<61>tres de la m<>thode de classe ;</li>
<br>
<li><a href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::fctStatic_0()</a> : enregistre une m<>thode de
classe <i>static</i> sans param<61>tre ;</li>
<li><a href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::fctStatic_1()</a> : enregistre une m<>thode de
classe <i>static</i> avec 1 param<61>tre ;</li>
<li><a href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::fctStatic_2()</a> : enregistre une m<>thode de
classe <i>static</i> avec 2 param<61>tres ;</li>
<li>etc... <a href="../doxygen/html/classqx_1_1_qx_class.html"
target="_blank">qx::QxClass&lt;T&gt;::fctStatic_X()</a> : le suffixe <i>X</i>
correspondant au nombre de param<61>tres de la m<>thode de classe <i>static</i> ;</li>
</ul>
<b>Par exemple :</b> on souhaite enregistrer dans le contexte QxOrm plusieurs m<>thodes d'une
classe <i>person</i> :
<br><br>
<i>* Fichier person.h :</i><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="Introspection engine - function">
<pre><span class="pre">#ifndef _PERSON_H_
#define _PERSON_H_
</span><span class="keyword">
class</span> person<span class="operator">
{</span><span class="keyword">
public</span><span class="operator">:</span><span class="type">
long</span> id<span class="operator">;</span>
QString firstName<span class="operator">;</span>
QString lastName<span class="operator">;</span>
QDateTime birthDate<span class="operator">;</span>
person<span class="operator">() :</span> id<span class="operator">(</span><span class="int">0</span><span class="operator">) { ; }</span><span class="keyword">
virtual</span><span class="operator"> ~</span>person<span class="operator">() { ; }</span>
<font style="background-color:yellow"><span class="type">long</span> getId<span class="operator">()</span><span class="keyword"> const</span><span class="operator">;</span></font>
<font style="background-color:yellow"><span class="type">void</span> myMethodWith2Params<span class="operator">(</span><span class="type">int</span> param1<span class="operator">,</span><span class="keyword"> const</span> QString<span class="operator"> &amp;</span> param2<span class="operator">);</span></font>
<font style="background-color:yellow"><span class="keyword">static</span><span class="type"> double</span> myStaticMethodWith1Param<span class="operator">(</span><span class="type">long</span> param1<span class="operator">);</span></font>
<span class="operator">};</span>
QX_REGISTER_HPP_MY_TEST_EXE<span class="operator">(</span>person<span class="operator">,</span> qx<span class="operator">::</span>trait<span class="operator">::</span>no_base_class_defined<span class="operator">,</span><span class="int"> 0</span><span class="operator">)</span><span class="pre">
#endif // _PERSON_H_</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<i>* Fichier person.cpp :</i><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="Introspection engine - function">
<pre><span class="keyword">namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>person<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span>
t<span class="operator">.</span>id<span class="operator">(&amp;</span> person<span class="operator">::</span>id<span class="operator">,</span><span class="string"> "id"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>firstName<span class="operator">,</span><span class="string"> "first_name"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>lastName<span class="operator">,</span><span class="string"> "last_name"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> person<span class="operator">::</span>birthDate<span class="operator">,</span><span class="string"> "birth_date"</span><span class="operator">);</span>
<font style="background-color:yellow">t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">long</span><span class="operator">&gt;(&amp;</span> person<span class="operator">::</span>getId<span class="operator">,</span><span class="string"> "getId"</span><span class="operator">);</span></font>
<font style="background-color:yellow">t<span class="operator">.</span>fct_2<span class="operator">&lt;</span><span class="type">void</span><span class="operator">,</span><span class="type"> int</span><span class="operator">,</span><span class="keyword"> const</span> QString<span class="operator"> &amp;&gt;(&amp;</span> person<span class="operator">::</span>myMethodWith2Params<span class="operator">,</span><span class="string"> "myMethodWith2Params"</span><span class="operator">);</span></font>
<font style="background-color:yellow">t<span class="operator">.</span>fctStatic_1<span class="operator">&lt;</span><span class="type">double</span><span class="operator">,</span><span class="type"> long</span><span class="operator">&gt;(&amp;</span> person<span class="operator">::</span>myStaticMethodWith1Param<span class="operator">,</span><span class="string"> "myStaticMethodWith1Param"</span><span class="operator">);</font>
}}</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
Une fois enregistr<74>es dans le contexte QxOrm, il est possible d'appeler dynamiquement ces
fonctions avec les m<>thodes <a href="../doxygen/html/classqx_1_1_qx_class_x.html"
target="_blank">qx::QxClassX::invoke()</a> et <a
href="../doxygen/html/classqx_1_1_qx_class_x.html"
target="_blank">qx::QxClassX::invokeStatic()</a> :
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="Introspection engine - function">
<pre> <span class="comment">// Generic pointer of type void * : we know that p is of type 'person'
</span><span class="type"> void</span><span class="operator"> *</span> p<span class="operator"> = ...;</span><span class="comment">
// Call method 'long getId() const' and get return value
</span> boost<span class="operator">::</span>any returnValue<span class="operator">;</span>
qx<span class="operator">::</span>QxClassX<span class="operator">::</span>invoke<span class="operator">(</span><span class="string">"person"</span><span class="operator">,</span><span class="string"> "getId"</span><span class="operator">,</span> p<span class="operator">,</span><span class="string"> ""</span><span class="operator">, (&amp;</span> returnValue<span class="operator">));</span><span class="type">
long</span> lId<span class="operator"> =</span> boost<span class="operator">::</span>any_cast<span class="operator">&lt;</span><span class="type">long</span><span class="operator">&gt;(</span>returnValue<span class="operator">);</span><span class="comment">
// Call method 'myMethodWith2Params' with 2 parameters encapsulated in a string (default separator for parameters is character '|')
// This way to pass parameters to the function works only if parameters are numeric or string
// If parameters are more complex, then you have to encapsulate parameters in a list of qx::any, as shown below
</span> qx<span class="operator">::</span>QxClassX<span class="operator">::</span>invoke<span class="operator">(</span><span class="string">"person"</span><span class="operator">,</span><span class="string"> "myMethodWith2Params"</span><span class="operator">,</span> p<span class="operator">,</span><span class="string"> "36|my string param 2"</span><span class="operator">);</span><span class="comment">
// Call method 'myMethodWith2Params' with 2 parameters encapsulated in a list of qx::any : std::vector&lt;qx::any&gt;
</span> std<span class="operator">::</span>vector<span class="operator">&lt;</span>boost<span class="operator">::</span>any<span class="operator">&gt;</span> lstParams<span class="operator">;</span><span class="type">
int</span> iParam1<span class="operator"> =</span><span class="int"> 36</span><span class="operator">;</span> lstParams<span class="operator">.</span>push_back<span class="operator">(</span>iParam1<span class="operator">);</span><span class="comment"> // Parameter at position 1
</span> QString sParam2<span class="operator"> =</span><span class="string"> "my string param 2"</span><span class="operator">;</span> lstParams<span class="operator">.</span>push_back<span class="operator">(</span>sParam2<span class="operator">);</span><span class="comment"> // Parameter at position 2
</span> qx<span class="operator">::</span>QxClassX<span class="operator">::</span>invoke<span class="operator">(</span><span class="string">"person"</span><span class="operator">,</span><span class="string"> "myMethodWith2Params"</span><span class="operator">,</span> p<span class="operator">,</span> lstParams<span class="operator">);</span><span class="comment">
// Call static method 'myStaticMethodWith1Param' with 1 parameter and get return value
</span> qx<span class="operator">::</span>QxClassX<span class="operator">::</span>invokeStatic<span class="operator">(</span><span class="string">"person"</span><span class="operator">,</span><span class="string"> "myStaticMethodWith1Param"</span><span class="operator">,</span><span class="string"> "19"</span><span class="operator">, (&amp;</span> returnValue<span class="operator">));</span><span class="type">
double</span> dValue<span class="operator"> =</span> boost<span class="operator">::</span>any_cast<span class="operator">&lt;</span><span class="type">double</span><span class="operator">&gt;(</span>returnValue<span class="operator">);</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_740">Cr<EFBFBD>er une instance C++
dynamiquement</a></p>
<div class="manual_div_content">
Le moteur d'introspection de la biblioth<74>que QxOrm permet de cr<63>er dynamiquement des instances
de classe (<a href="../doxygen/html/group___qx_factory.html" target="_blank">module
QxFactory</a>, mod<6F>le de conception fabrique ou design pattern factory) avec les m<>thodes
suivantes :
<ul>
<li><a href="../doxygen/html/group___qx_factory.html" target="_blank">qx::create(const
QString & sKey)</a> : cr<63>ation d'une instance de type <i>sKey</i> sous la forme <a
href="http://www.boost.org/doc/libs/release/doc/html/any.html"
target="_blank">qx::any</a> (contenant un pointeur intelligent de type
<i>std::shared_ptr</i>, alias de <i>boost::shared_ptr</i> par d<>faut) ;
</li>
<li><a href="../doxygen/html/group___qx_factory.html"
target="_blank">qx::create_nude_ptr&lt;T&gt;(const QString & sKey)</a> : cr<63>ation d'une
instance de type <i>sKey</i> sous la forme d'un pointeur nu de type <i>T *</i> (attention
<20> lib<69>rer la m<>moire de ce pointeur pour <20>viter les fuites m<>moire) ;</li>
<li><a href="../doxygen/html/group___qx_factory.html"
target="_blank">qx::create_void_ptr(const QString & sKey)</a> : cr<63>ation d'une instance
de type <i>sKey</i> sous la forme d'un pointeur nu de type <i>void *</i> (attention <20>
lib<69>rer la m<>moire de ce pointeur pour <20>viter les fuites m<>moire).</li>
</ul>
<b>Par exemple :</b> <a href="#manual_80">le module QxService</a> de la biblioth<74>que QxOrm
utilise ce m<>canisme pour cr<63>er dynamiquement les instances de classe de service pour ex<65>cuter
les routines c<>t<EFBFBD> serveur :
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="Introspection engine - factory">
<pre> qx<span class="operator">::</span>service<span class="operator">::</span>IxService<span class="operator"> *</span> ptr<span class="operator"> =</span> qx<span class="operator">::</span>create_nude_ptr<span class="operator">&lt;</span>qx<span class="operator">::</span>service<span class="operator">::</span>IxService<span class="operator">&gt;(</span>m_sServiceName<span class="operator">);</span> </pre>
</td>
</tr>
</tbody>
</table>
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_750">Parcourir la liste des
classes/propri<72>t<EFBFBD>s enregistr<74>es dans le contexte QxOrm</a></p>
<div class="manual_div_content">
Voici un exemple d'utilisation du moteur d'introspection de la biblioth<74>que QxOrm : comment
lister toutes les classes, propri<72>t<EFBFBD>s et m<>thodes enregistr<74>es dans le contexte QxOrm ?<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>QString QxClassX<span class="operator">::</span>dumpAllClasses<span class="operator">()
{</span>
QxClassX<span class="operator">::</span>registerAllClasses<span class="operator">();</span>
QxCollection<span class="operator">&lt;</span>QString<span class="operator">,</span> IxClass<span class="operator"> *&gt; *</span> pAllClasses<span class="operator"> =</span> QxClassX<span class="operator">::</span>getAllClasses<span class="operator">();</span><span class="flow">
if</span><span class="operator"> (!</span> pAllClasses<span class="operator">) {</span> qAssert<span class="operator">(</span><span class="bool">false</span><span class="operator">);</span><span class="flow"> return</span><span class="string"> ""</span><span class="operator">; }</span>
QString sDump<span class="operator">;</span><span class="type">
long</span> lCount<span class="operator"> =</span> pAllClasses<span class="operator">-&gt;</span>count<span class="operator">();</span>
qDebug<span class="operator">(</span><span class="string">"[QxOrm] start dump all registered classes (%ld)"</span><span class="operator">,</span> lCount<span class="operator">);</span>
_foreach<span class="operator">(</span>IxClass<span class="operator"> *</span> pClass<span class="operator">, (*</span> pAllClasses<span class="operator">))
{</span><span class="flow"> if</span><span class="operator"> (</span>pClass<span class="operator">) {</span> sDump<span class="operator"> +=</span> pClass<span class="operator">-&gt;</span>dumpClass<span class="operator">(); } }</span>
qDebug<span class="operator">(</span><span class="string">"[QxOrm] %s"</span><span class="operator">,</span><span class="string"> "end dump all registered classes"</span><span class="operator">);</span><span class="flow">
return</span> sDump<span class="operator">;
}</span>
QString IxClass<span class="operator">::</span>dumpClass<span class="operator">()</span><span class="keyword"> const</span><span class="operator">
{</span>
QString sDump<span class="operator">;</span>
sDump<span class="operator"> +=</span><span class="string"> "-- class '"</span><span class="operator"> +</span> m_sKey<span class="operator"> +</span><span class="string"> "' (name '"</span><span class="operator"> +</span> m_sName<span class="operator"> +</span><span class="string"> "', "</span><span class="operator">;</span>
sDump<span class="operator"> +=</span><span class="string"> "description '"</span><span class="operator"> +</span> m_sDescription<span class="operator"> +</span><span class="string"> "', version '"</span><span class="operator"> +</span> QString<span class="operator">::</span>number<span class="operator">(</span>m_lVersion<span class="operator">) +</span><span class="string"> "', "</span><span class="operator">;</span>
sDump<span class="operator"> +=</span><span class="string"> "base class '"</span><span class="operator"> + (</span>getBaseClass<span class="operator">() ?</span> getBaseClass<span class="operator">()-&gt;</span>getKey<span class="operator">() :</span><span class="string"> ""</span><span class="operator">) +</span><span class="string"> "')\n"</span><span class="operator">;</span><span class="type">
long</span> lCount<span class="operator"> = (</span>m_pDataMemberX<span class="operator"> ?</span> m_pDataMemberX<span class="operator">-&gt;</span>count<span class="operator">() :</span><span class="int"> 0</span><span class="operator">);</span>
sDump<span class="operator"> +=</span><span class="string"> "\t* list of registered properties ("</span><span class="operator"> +</span> QString<span class="operator">::</span>number<span class="operator">(</span>lCount<span class="operator">) +</span><span class="string"> ")\n"</span><span class="operator">;</span><span class="flow">
if</span><span class="operator"> (</span>m_pDataMemberX<span class="operator">)
{</span>
IxDataMember<span class="operator"> *</span> pId<span class="operator"> =</span><span class="keyword"> this</span><span class="operator">-&gt;</span>getId<span class="operator">();</span><span class="flow">
for</span><span class="operator"> (</span><span class="type">long</span> l<span class="operator"> =</span><span class="int"> 0</span><span class="operator">;</span> l<span class="operator"> &lt;</span> lCount<span class="operator">;</span> l<span class="operator">++)
{</span>
IxDataMember<span class="operator"> *</span> p<span class="operator"> =</span> m_pDataMemberX<span class="operator">-&gt;</span>get<span class="operator">(</span>l<span class="operator">);</span><span class="flow"> if</span><span class="operator"> (!</span> p<span class="operator">) {</span><span class="flow"> continue</span><span class="operator">; }</span>
IxSqlRelation<span class="operator"> *</span> pRelation<span class="operator"> =</span> p<span class="operator">-&gt;</span>getSqlRelation<span class="operator">();</span>
QString sInfos<span class="operator"> =</span> p<span class="operator">-&gt;</span>getKey<span class="operator">() + ((</span>p<span class="operator"> ==</span> pId<span class="operator">) ?</span> QString<span class="operator">(</span><span class="string">" (id)"</span><span class="operator">) :</span> QString<span class="operator">());</span>
sInfos<span class="operator"> += (</span>pRelation<span class="operator"> ? (</span>QString<span class="operator">(</span><span class="string">" ("</span><span class="operator">) +</span> pRelation<span class="operator">-&gt;</span>getDescription<span class="operator">() +</span> QString<span class="operator">(</span><span class="string">")"</span><span class="operator">)) :</span> QString<span class="operator">());</span>
sDump<span class="operator"> +=</span><span class="string"> "\t\t"</span><span class="operator"> +</span> sInfos<span class="operator"> +</span><span class="string"> "\n"</span><span class="operator">;
}
}</span>
lCount<span class="operator"> = (</span>m_pFctMemberX<span class="operator"> ?</span> m_pFctMemberX<span class="operator">-&gt;</span>count<span class="operator">() :</span><span class="int"> 0</span><span class="operator">);</span>
sDump<span class="operator"> +=</span><span class="string"> "\t* list of registered functions ("</span><span class="operator"> +</span> QString<span class="operator">::</span>number<span class="operator">(</span>lCount<span class="operator">) +</span><span class="string"> ")\n"</span><span class="operator">;</span><span class="flow">
if</span><span class="operator"> (</span>m_pFctMemberX<span class="operator">)
{</span>
_foreach_if<span class="operator">(</span>IxFunction_ptr p<span class="operator">, (*</span> m_pFctMemberX<span class="operator">), (</span>p<span class="operator">))
{</span> QString sKey<span class="operator"> =</span> p<span class="operator">-&gt;</span>getKey<span class="operator">();</span> sDump<span class="operator"> +=</span><span class="string"> "\t\t"</span><span class="operator"> +</span> sKey<span class="operator"> +</span><span class="string"> "\n"</span><span class="operator">; }
}</span>
qDebug<span class="operator">(</span><span class="string">"%s"</span><span class="operator">,</span> qPrintable<span class="operator">(</span>sDump<span class="operator">));</span><span class="flow">
return</span> sDump<span class="operator">;
}</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Si on utilise la m<>thode <i>qx::QxClassX::dumpAllClasses()</i> avec le <a href="./tutorial.html"
target="_blank">tutoriel qxBlog</a>, voici le r<>sultat obtenu :<br>
<br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="operator">[</span>QxOrm<span class="operator">]</span> start dump all registered classes<span class="operator"> (</span><span class="int">4</span><span class="operator">)
--</span><span class="keyword"> class</span><span class="char"> 'author'</span><span class="operator"> (</span>name<span class="char"> 'author'</span><span class="operator">,</span> description<span class="char"> ''</span><span class="operator">,</span> version<span class="char"> '0'</span><span class="operator">,</span> base<span class="keyword"> class</span><span class="char"> ''</span><span class="operator">)
*</span> list of registered properties<span class="operator"> (</span><span class="int">5</span><span class="operator">)</span>
author_id<span class="operator"> (</span>id<span class="operator">)</span>
name
birthdate
sex
list_blog<span class="operator"> (</span>relation one<span class="operator">-</span>to<span class="operator">-</span>many<span class="operator">)
*</span> list of registered functions<span class="operator"> (</span><span class="int">1</span><span class="operator">)</span>
age<span class="operator">
--</span><span class="keyword"> class</span><span class="char"> 'blog'</span><span class="operator"> (</span>name<span class="char"> 'blog'</span><span class="operator">,</span> description<span class="char"> ''</span><span class="operator">,</span> version<span class="char"> '0'</span><span class="operator">,</span> base<span class="keyword"> class</span><span class="char"> ''</span><span class="operator">)
*</span> list of registered properties<span class="operator"> (</span><span class="int">6</span><span class="operator">)</span>
blog_id<span class="operator"> (</span>id<span class="operator">)</span>
blog_text
date_creation
author_id<span class="operator"> (</span>relation many<span class="operator">-</span>to<span class="operator">-</span>one<span class="operator">)</span>
list_comment<span class="operator"> (</span>relation one<span class="operator">-</span>to<span class="operator">-</span>many<span class="operator">)</span>
list_category<span class="operator"> (</span>relation many<span class="operator">-</span>to<span class="operator">-</span>many<span class="operator">)
*</span> list of registered functions<span class="operator"> (</span><span class="int">0</span><span class="operator">)
--</span><span class="keyword"> class</span><span class="char"> 'comment'</span><span class="operator"> (</span>name<span class="char"> 'comment'</span><span class="operator">,</span> description<span class="char"> ''</span><span class="operator">,</span> version<span class="char"> '0'</span><span class="operator">,</span> base<span class="keyword"> class</span><span class="char"> ''</span><span class="operator">)
*</span> list of registered properties<span class="operator"> (</span><span class="int">4</span><span class="operator">)</span>
comment_id<span class="operator"> (</span>id<span class="operator">)</span>
comment_text
date_creation
blog_id<span class="operator"> (</span>relation many<span class="operator">-</span>to<span class="operator">-</span>one<span class="operator">)
*</span> list of registered functions<span class="operator"> (</span><span class="int">0</span><span class="operator">)
--</span><span class="keyword"> class</span><span class="char"> 'category'</span><span class="operator"> (</span>name<span class="char"> 'category'</span><span class="operator">,</span> description<span class="char"> ''</span><span class="operator">,</span> version<span class="char"> '0'</span><span class="operator">,</span> base<span class="keyword"> class</span><span class="char"> ''</span><span class="operator">)
*</span> list of registered properties<span class="operator"> (</span><span class="int">4</span><span class="operator">)</span>
category_id<span class="operator"> (</span>id<span class="operator">)</span>
name
description
list_blog<span class="operator"> (</span>relation many<span class="operator">-</span>to<span class="operator">-</span>many<span class="operator">)
*</span> list of registered functions<span class="operator"> (</span><span class="int">0</span><span class="operator">)
[</span>QxOrm<span class="operator">]</span> end dump all registered classes</pre>
</td>
</tr>
</tbody>
</table>
<br>
</div>
</div>
<p class="manual_p_title_1"><a class="manual_a_title_1" name="manual_80">Services : transf<73>rer la
couche de donn<6E>es persistante sur le r<>seau (module QxService)</a></p>
<div class="manual_div_content_1">
Le module <a href="../doxygen/html/group___qx_service.html" target="_blank"><b>QxService</b></a> de
la biblioth<74>que <b>QxOrm</b> permet de cr<63>er rapidement un <b>serveur d'applications C++</b>
performant (notion de <i>services</i> avec <i>demande</i> du client et <i>r<EFBFBD>ponse</i> du serveur).
<font style="background-color:yellow"><b><a href="./tutorial_2.html">Un tutoriel est disponible sur
le site QxOrm</a></b></font> afin de pr<70>senter un exemple d'utilisation du module <a
href="../doxygen/html/group___qx_service.html" target="_blank">QxService</a>.
Le module <a href="../doxygen/html/group___qx_service.html" target="_blank">QxService</a> est bas<61>
sur <a href="#manual_70">le moteur d'introspection</a> ainsi que <a href="#manual_60">le moteur de
s<>rialisation</a> de la biblioth<74>que QxOrm afin de transf<73>rer la couche de donn<6E>es persistante
sur le r<>seau et ex<65>cuter automatiquement les routines c<>t<EFBFBD> serveur.
<br><br>
<b>Remarque :</b> pour activer le module <b>QxService</b>, il faut d<>finir l'option de compilation
<font style="background-color:yellow"><b>_QX_ENABLE_QT_NETWORK</b></font> dans <a
href="#manual_220">le fichier de configuration <i>QxOrm.pri</i> (ou <i>QxOrm.cmake</i>)</a>.
Cette option de compilation ajoute une d<>pendance au binaire <a
href="http://doc.qt.io/qt-5/qtnetwork-index.html" target="_blank">QtNetwork</a> fourni avec la
biblioth<74>que Qt.
<br><br>
<b>Autre remarque :</b> l'application <b>QxEntityEditor</b> est livr<76>e avec le plugin
<i>QxEECppServicesExport</i> : ce plugin g<>n<EFBFBD>re automatiquement le code source n<>cessaire pour
transf<73>rer l'ensemble des entit<69>s d'un projet sur le r<>seau.
Une liste de m<>thodes client/serveur est g<>n<EFBFBD>r<EFBFBD>e automatiquement :
<ul>
<li><i>count() :</i> requ<71>te client/serveur pour compter le nombre d'<27>l<EFBFBD>ments (avec possibilit<69>
d'utiliser un filtre SQL) ;</li>
<li><i>fetchById() :</i> requ<71>te client/serveur pour alimenter les propri<72>t<EFBFBD>s d'une entit<69> en
fonction de son identifiant ;</li>
<li><i>fetchAll() :</i> requ<71>te client/serveur pour alimenter les propri<72>t<EFBFBD>s de toutes les
entit<69>s d'une table ;</li>
<li><i>fetchByQuery() :</i> requ<71>te client/serveur pour alimenter les propri<72>t<EFBFBD>s des entit<69>s
filtr<74>es par une requ<71>te SQL ;</li>
<li><i>insert() :</i> requ<71>te client/serveur pour ins<6E>rer les donn<6E>es d'une entit<69> ;</li>
<li><i>update() :</i> requ<71>te client/serveur pour mettre <20> jour les donn<6E>es d'une entit<69> ;</li>
<li><i>save() :</i> requ<71>te client/serveur pour sauvegarder les donn<6E>es d'une entit<69> (insertion
ou mise <20> jour) ;</li>
<li><i>deleteById() :</i> requ<71>te client/serveur pour supprimer une entit<69> en fonction de son
identifiant ;</li>
<li><i>deleteAll() :</i> requ<71>te client/serveur pour supprimer tous les <20>l<EFBFBD>ments de la table
mapp<70>e <20> une entit<69> ;</li>
<li><i>deleteByQuery() :</i> requ<71>te client/serveur pour supprimer tous les <20>l<EFBFBD>ments en fonction
d'une requ<71>te SQL ;</li>
<li><i>destroyById() :</i> requ<71>te client/serveur pour supprimer une entit<69> en fonction de son
identifiant (avec prise en compte de <a href="#manual_3400">la suppression logique</a>) ;
</li>
<li><i>destroyAll() :</i> requ<71>te client/serveur pour supprimer tous les <20>l<EFBFBD>ments de la table
mapp<70>e <20> une entit<69> (avec prise en compte de <a href="#manual_3400">la suppression
logique</a>) ;</li>
<li><i>destroyByQuery() :</i> requ<71>te client/serveur pour supprimer tous les <20>l<EFBFBD>ments en
fonction d'une requ<71>te SQL (avec prise en compte de <a href="#manual_3400">la suppression
logique</a>) ;</li>
<li><i>executeQuery() :</i> requ<71>te client/serveur pour ex<65>cuter <a href="#manual_3610">une
requ<71>te SQL personnalis<69>e ou proc<6F>dure stock<63>e</a> ;</li>
<li><i>exist() :</i> requ<71>te client/serveur pour tester l'existence d'une entit<69> en fonction de
son identifiant ;</li>
<li><i>isValid() :</i> requ<71>te client/serveur pour tester la validit<69> d'une entit<69> (<a
href="#manual_420">module QxValidator</a>).</li>
</ul>
Il est possible d'ajouter de nouveaux services ou de personnaliser les services g<>n<EFBFBD>r<EFBFBD>s
automatiquement par l'application <b>QxEntityEditor</b>.
<br><br>
L'objectif de ce chapite est de pr<70>senter les concepts <20> mettre en oeuvre pour utiliser le module
<a href="../doxygen/html/group___qx_service.html" target="_blank">QxService</a> :
<ul>
<li><a href="#manual_810">D<EFBFBD>finition des param<61>tres d'entr<74>e/sortie d'un service
(requ<71>te/r<>ponse)</a> ;</li>
<li><a href="#manual_820">D<EFBFBD>finition des fonctions publi<6C>es par un service</a> ;</li>
<li><a href="#manual_825">Liste des options disponibles c<>t<EFBFBD> serveur</a> ;</li>
<li><a href="#manual_826">Param<EFBFBD>trage de la connexion c<>t<EFBFBD> client</a> ;</li>
<li><a href="#manual_830">Gestion de l'authentification dans un service</a> ;</li>
<li><a href="#manual_840">Requ<EFBFBD>tes client/serveur asynchrones</a>.</li>
</ul>
<br>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_810">Param<EFBFBD>tres
d'entr<74>e/sortie d'un service (requ<71>te/r<>ponse)</a></p>
<div class="manual_div_content">
Chaque fonction publi<6C>e par un service dispose de param<61>tres d'entr<74>e (demande du client) et de
param<61>tres de sortie (r<>ponse du serveur).
Ces param<61>tres d'entr<74>e/sortie doivent h<>riter de l'interface <a
href="../doxygen/html/classqx_1_1service_1_1_ix_parameter"
target="_blank">qx::service::IxParameter</a> et doivent <20>tre enregistr<74>es dans le contexte
QxOrm (par la fonction <i>void qx::register_class&lt;T&gt;</i>).
<br><br>
<b>Par exemple :</b> voici un exemple de param<61>tres d'entr<74>e/sortie g<>n<EFBFBD>r<EFBFBD>s automatiquement par
l'application <b>QxEntityEditor</b> bas<61> sur la classe <i>blog</i> du <a href="./tutorial.html"
target="_blank">tutoriel qxBlog</a> :
<br><br>
<i>* Fichier blog.services.gen.h :</i><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">namespace</span> services<span class="operator"> {</span><span class="keyword">
typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>blog<span class="operator">&gt;</span> blog_ptr<span class="operator">;</span><span class="keyword">
typedef</span> qx<span class="operator">::</span>QxCollection<span class="operator">&lt;</span><span class="type">long</span><span class="operator">,</span> blog_ptr<span class="operator">&gt;</span> list_of_blog<span class="operator">;</span><span class="keyword">
typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>list_of_blog<span class="operator">&gt;</span> list_of_blog_ptr<span class="operator">;</span><span class="comment">
/* -- Service Input Parameters -- */</span><span class="keyword">
<font style="background-color:yellow">class</span> QXBLOG_SERVICES_EXPORT blog_input<span class="operator"> :</span><span class="keyword"> public</span> qx<span class="operator">::</span>service<span class="operator">::</span>IxParameter</font><span class="operator">
{</span><span class="keyword">
public</span><span class="operator">:</span>
blog_input<span class="operator">();</span><span class="keyword">
virtual</span><span class="operator"> ~</span>blog_input<span class="operator">();</span><span class="type">
long</span> id<span class="operator">;</span><span class="comment"> //!&lt; Id to fetch or delete
</span> blog_ptr instance<span class="operator">;</span><span class="comment"> //!&lt; Single instance to fetch, insert, update, delete or validate
</span> list_of_blog_ptr list<span class="operator">;</span><span class="comment"> //!&lt; List of instances to fetch, insert, update, delete or validate
</span> qx_query query<span class="operator">;</span><span class="comment"> //!&lt; Query to execute when fetching, updating or deleting
</span> QStringList columns<span class="operator">;</span><span class="comment"> //!&lt; List of columns to fetch or update
</span> QStringList relations<span class="operator">;</span><span class="comment"> //!&lt; List of relations to fetch
</span><span class="operator">
};</span><span class="keyword">
typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>services<span class="operator">::</span>blog_input<span class="operator">&gt;</span> blog_input_ptr<span class="operator">;</span><span class="comment">
/* -- Service Output Parameters -- */</span><span class="keyword">
<font style="background-color:yellow">class</span> QXBLOG_SERVICES_EXPORT blog_output<span class="operator"> :</span><span class="keyword"> public</span> qx<span class="operator">::</span>service<span class="operator">::</span>IxParameter</font><span class="operator">
{</span><span class="keyword">
public</span><span class="operator">:</span>
blog_output<span class="operator">();</span><span class="keyword">
virtual</span><span class="operator"> ~</span>blog_output<span class="operator">();</span>
blog_ptr instance<span class="operator">;</span><span class="comment"> //!&lt; Single instance from server
</span> list_of_blog_ptr list<span class="operator">;</span><span class="comment"> //!&lt; List of instances from server
</span> QSqlError error<span class="operator">;</span><span class="comment"> //!&lt; If a SQL error occurred, this output parameter is not empty
</span> qx<span class="operator">::</span>QxInvalidValueX invalid<span class="operator">;</span><span class="comment"> //!&lt; Check if a single instance (or a list of instances) is valid
</span> qx_query query<span class="operator">;</span><span class="comment"> //!&lt; Query which contains all results
</span><span class="type"> long</span> count<span class="operator">;</span><span class="comment"> //!&lt; Count how many items in database using a query or not
</span> qx_bool exist<span class="operator">;</span><span class="comment"> //!&lt; Check if a single instance (or a list of instances) exist in database
</span><span class="operator">
};</span><span class="keyword">
typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>services<span class="operator">::</span>blog_output<span class="operator">&gt;</span> blog_output_ptr<span class="operator">;
}</span><span class="comment"> // namespace services
</span>
QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QXBLOG_SERVICES<span class="operator">(</span>services<span class="operator">::</span>blog_input<span class="operator">,</span> qx<span class="operator">::</span>service<span class="operator">::</span>IxParameter<span class="operator">,</span><span class="int"> 0</span><span class="operator">,</span> services_blog_input<span class="operator">)</span>
QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QXBLOG_SERVICES<span class="operator">(</span>services<span class="operator">::</span>blog_output<span class="operator">,</span> qx<span class="operator">::</span>service<span class="operator">::</span>IxParameter<span class="operator">,</span><span class="int"> 0</span><span class="operator">,</span> services_blog_output<span class="operator">)</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<i>* Fichier blog.services.gen.cpp :</i><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QXBLOG_SERVICES<span class="operator">(</span>services<span class="operator">::</span>blog_input<span class="operator">,</span> services_blog_input<span class="operator">)</span>
QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QXBLOG_SERVICES<span class="operator">(</span>services<span class="operator">::</span>blog_output<span class="operator">,</span> services_blog_output<span class="operator">)</span><span class="keyword">
namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><font style="background-color:yellow"><span class="type">
void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>services<span class="operator">::</span>blog_input<span class="operator">&gt; &amp;</span> t<span class="operator">)</span></font>
<span class="operator">{</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> services<span class="operator">::</span>blog_input<span class="operator">::</span>id<span class="operator">,</span><span class="string"> "id"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> services<span class="operator">::</span>blog_input<span class="operator">::</span>instance<span class="operator">,</span><span class="string"> "instance"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> services<span class="operator">::</span>blog_input<span class="operator">::</span>list<span class="operator">,</span><span class="string"> "list"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> services<span class="operator">::</span>blog_input<span class="operator">::</span>query<span class="operator">,</span><span class="string"> "query"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> services<span class="operator">::</span>blog_input<span class="operator">::</span>columns<span class="operator">,</span><span class="string"> "columns"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> services<span class="operator">::</span>blog_input<span class="operator">::</span>relations<span class="operator">,</span><span class="string"> "relations"</span><span class="operator">);
}</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><font style="background-color:yellow"><span class="type">
void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>services<span class="operator">::</span>blog_output<span class="operator">&gt; &amp;</span> t<span class="operator">)</span></font>
<span class="operator">{</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> services<span class="operator">::</span>blog_output<span class="operator">::</span>instance<span class="operator">,</span><span class="string"> "instance"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> services<span class="operator">::</span>blog_output<span class="operator">::</span>list<span class="operator">,</span><span class="string"> "list"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> services<span class="operator">::</span>blog_output<span class="operator">::</span>error<span class="operator">,</span><span class="string"> "error"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> services<span class="operator">::</span>blog_output<span class="operator">::</span>invalid<span class="operator">,</span><span class="string"> "invalid"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> services<span class="operator">::</span>blog_output<span class="operator">::</span>query<span class="operator">,</span><span class="string"> "query"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> services<span class="operator">::</span>blog_output<span class="operator">::</span>count<span class="operator">,</span><span class="string"> "count"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> services<span class="operator">::</span>blog_output<span class="operator">::</span>exist<span class="operator">,</span><span class="string"> "exist"</span><span class="operator">);
}
}</span><span class="comment"> // namespace qx</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
<b>Remarque :</b> comme on peut le constater sur l'exemple ci-dessus, les param<61>tres
d'entr<74>e/sortie peuvent contenir des types complexes (des collections, des pointeurs, etc...).
Il est donc possible et tr<74>s simple de transf<73>rer des structures complexes sur le r<>seau avec le
module <a href="../doxygen/html/group___qx_service.html" target="_blank">QxService</a>.
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_820">D<EFBFBD>finir les fonctions
publi<6C>es par un service</a></p>
<div class="manual_div_content">
Chaque service enregistr<74> dans le module <a href="../doxygen/html/group___qx_service.html"
target="_blank">QxService</a> publie une liste de fonctions accessibles c<>t<EFBFBD> client (requ<71>tes
client/serveur).
Les services doivent h<>riter de la classe de base <a
href="../doxygen/html/classqx_1_1service_1_1_qx_service.html"
target="_blank">qx::service::QxService&lt;INPUT, OUTPUT&gt;</a> (les param<61>tres template
<i>INPUT</i> et <i>OUTPUT</i> correspondant <a href="#manual_810">aux param<61>tres
d'entr<74>e/sortie</a>) et doivent <20>tre enregistr<74>s dans le contexte QxOrm (par la fonction
<i>void qx::register_class&lt;T&gt;</i>).
<br><br>
<b>Par exemple :</b> voici un exemple de service g<>n<EFBFBD>r<EFBFBD> automatiquement par l'application
<b>QxEntityEditor</b> bas<61> sur la classe <i>blog</i> du <a href="./tutorial.html"
target="_blank">tutoriel qxBlog</a> :
<br><br>
<i>* Fichier blog.services.gen.h :</i><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">namespace</span> services<span class="operator"> {</span><span class="comment">
/* -- Service Definition -- */</span>
<font style="background-color:yellow"><span class="keyword">typedef</span> qx<span class="operator">::</span>service<span class="operator">::</span>QxService<span class="operator">&lt;</span> blog_input<span class="operator">,</span> blog_output<span class="operator"> &gt;</span> blog_base_class<span class="operator">;</span><span class="keyword">
class</span> QXBLOG_SERVICES_EXPORT blog_services<span class="operator"> :</span><span class="keyword"> public</span> blog_base_class</font><span class="operator">
{</span>
QX_REGISTER_FRIEND_CLASS<span class="operator">(</span>services<span class="operator">::</span>blog_services<span class="operator">)</span><span class="keyword">
public</span><span class="operator">:</span>
blog_services<span class="operator">();</span><span class="keyword">
virtual</span><span class="operator"> ~</span>blog_services<span class="operator">();</span><span class="keyword">
protected</span><span class="operator">:</span><span class="type">
void</span> fetchById_<span class="operator">();</span><span class="type">
void</span> fetchAll_<span class="operator">();</span><span class="type">
void</span> fetchByQuery_<span class="operator">();</span><span class="type">
void</span> insert_<span class="operator">();</span><span class="type">
void</span> update_<span class="operator">();</span><span class="type">
void</span> save_<span class="operator">();</span><span class="type">
void</span> deleteById_<span class="operator">();</span><span class="type">
void</span> deleteAll_<span class="operator">();</span><span class="type">
void</span> deleteByQuery_<span class="operator">();</span><span class="type">
void</span> destroyById_<span class="operator">();</span><span class="type">
void</span> destroyAll_<span class="operator">();</span><span class="type">
void</span> destroyByQuery_<span class="operator">();</span><span class="type">
void</span> executeQuery_<span class="operator">();</span><span class="type">
void</span> callQuery_<span class="operator">();</span><span class="type">
void</span> exist_<span class="operator">();</span><span class="type">
void</span> count_<span class="operator">();</span><span class="type">
void</span> isValid_<span class="operator">();</span><span class="pre">
#ifdef _QXBLOG_SERVICES_MODE_CLIENT
</span><span class="keyword">
public</span><span class="operator">:</span>
blog_ptr fetchById<span class="operator">(</span><span class="type">long</span> id<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
QSqlError fetchById<span class="operator">(</span>blog_ptr<span class="operator"> &amp;</span> p<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
QSqlError fetchById<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &amp;</span> lst<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
QSqlError fetchAll<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &amp;</span> lst<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
QSqlError fetchByQuery<span class="operator">(</span><span class="keyword">const</span> qx_query<span class="operator"> &amp;</span> query<span class="operator">,</span> list_of_blog_ptr<span class="operator"> &amp;</span> lst<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span>
<span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
QSqlError insert<span class="operator">(</span>blog_ptr<span class="operator"> &amp;</span> p<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
QSqlError insert<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &amp;</span> lst<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
QSqlError update<span class="operator">(</span>blog_ptr<span class="operator"> &amp;</span> p<span class="operator">,</span><span class="keyword"> const</span> qx_query<span class="operator"> &amp;</span> query<span class="operator"> =</span> qx_query<span class="operator">(),</span>
<span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
QSqlError update<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &amp;</span> lst<span class="operator">,</span><span class="keyword"> const</span> qx_query<span class="operator"> &amp;</span> query<span class="operator"> =</span> qx_query<span class="operator">(),</span>
<span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
QSqlError save<span class="operator">(</span>blog_ptr<span class="operator"> &amp;</span> p<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
QSqlError save<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &amp;</span> lst<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
QSqlError deleteById<span class="operator">(</span><span class="type">long</span> id<span class="operator">);</span>
QSqlError deleteById<span class="operator">(</span>blog_ptr<span class="operator"> &amp;</span> p<span class="operator">);</span>
QSqlError deleteById<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &amp;</span> lst<span class="operator">);</span>
QSqlError deleteAll<span class="operator">();</span>
QSqlError deleteByQuery<span class="operator">(</span><span class="keyword">const</span> qx_query<span class="operator"> &amp;</span> query<span class="operator">);</span>
QSqlError destroyById<span class="operator">(</span><span class="type">long</span> id<span class="operator">);</span>
QSqlError destroyById<span class="operator">(</span>blog_ptr<span class="operator"> &amp;</span> p<span class="operator">);</span>
QSqlError destroyById<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &amp;</span> lst<span class="operator">);</span>
QSqlError destroyAll<span class="operator">();</span>
QSqlError destroyByQuery<span class="operator">(</span><span class="keyword">const</span> qx_query<span class="operator"> &amp;</span> query<span class="operator">);</span>
QSqlError executeQuery<span class="operator">(</span>qx_query<span class="operator"> &amp;</span> query<span class="operator">,</span> blog_ptr<span class="operator"> &amp;</span> p<span class="operator">);</span>
QSqlError executeQuery<span class="operator">(</span>qx_query<span class="operator"> &amp;</span> query<span class="operator">,</span> list_of_blog_ptr<span class="operator"> &amp;</span> lst<span class="operator">);</span>
QSqlError callQuery<span class="operator">(</span>qx_query<span class="operator"> &amp;</span> query<span class="operator">);</span>
qx_bool exist<span class="operator">(</span>blog_ptr<span class="operator"> &amp;</span> p<span class="operator">);</span>
qx_bool exist<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &amp;</span> lst<span class="operator">);</span>
QSqlError count<span class="operator">(</span><span class="type">long</span><span class="operator"> &amp;</span> lCount<span class="operator">,</span><span class="keyword"> const</span> qx_query<span class="operator"> &amp;</span> query<span class="operator"> =</span> qx_query<span class="operator">());</span>
qx<span class="operator">::</span>QxInvalidValueX isValid<span class="operator">(</span>blog_ptr<span class="operator"> &amp;</span> p<span class="operator">);</span>
qx<span class="operator">::</span>QxInvalidValueX isValid<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &amp;</span> lst<span class="operator">);</span><span class="pre">
#endif // _QXBLOG_SERVICES_MODE_CLIENT
</span><span class="operator">
};</span><span class="keyword">
typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>services<span class="operator">::</span>blog_services<span class="operator">&gt;</span> blog_services_ptr<span class="operator">;
}</span><span class="comment"> // namespace services
</span>
QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QXBLOG_SERVICES<span class="operator">(</span>services<span class="operator">::</span>blog_services<span class="operator">,</span> qx<span class="operator">::</span>service<span class="operator">::</span>IxService<span class="operator">,</span><span class="int"> 0</span><span class="operator">,</span> services_blog_services<span class="operator">)</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<i>* Fichier blog.services.gen.cpp :</i><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QXBLOG_SERVICES<span class="operator">(</span>services<span class="operator">::</span>blog_services<span class="operator">,</span> services_blog_services<span class="operator">)</span><span class="keyword">
namespace</span> qx<span class="operator"> {</span>
<font style="background-color:yellow"><span class="keyword">template</span><span class="operator"> &lt;&gt;</span><span class="type">
void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>services<span class="operator">::</span>blog_services<span class="operator">&gt; &amp;</span> t<span class="operator">)</span></font>
<span class="operator">{</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>fetchById_<span class="operator">,</span><span class="string"> "fetchById"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>fetchAll_<span class="operator">,</span><span class="string"> "fetchAll"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>fetchByQuery_<span class="operator">,</span><span class="string"> "fetchByQuery"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>insert_<span class="operator">,</span><span class="string"> "insert"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>update_<span class="operator">,</span><span class="string"> "update"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>save_<span class="operator">,</span><span class="string"> "save"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>deleteById_<span class="operator">,</span><span class="string"> "deleteById"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>deleteAll_<span class="operator">,</span><span class="string"> "deleteAll"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>deleteByQuery_<span class="operator">,</span><span class="string"> "deleteByQuery"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>destroyById_<span class="operator">,</span><span class="string"> "destroyById"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>destroyAll_<span class="operator">,</span><span class="string"> "destroyAll"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>destroyByQuery_<span class="operator">,</span><span class="string"> "destroyByQuery"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>executeQuery_<span class="operator">,</span><span class="string"> "executeQuery"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>callQuery_<span class="operator">,</span><span class="string"> "callQuery"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>exist_<span class="operator">,</span><span class="string"> "exist"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>count_<span class="operator">,</span><span class="string"> "count"</span><span class="operator">);</span>
t<span class="operator">.</span>fct_0<span class="operator">&lt;</span><span class="type">void</span><span class="operator">&gt;(&amp;</span> services<span class="operator">::</span>blog_services<span class="operator">::</span>isValid_<span class="operator">,</span><span class="string"> "isValid"</span><span class="operator">);
}
}</span><span class="comment"> // namespace qx
// Then there is the implementation of all functions provided by the service...</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
<b>Remarque :</b> une fois d<>finies dans le contexte QxOrm, le client peut appeler les fonctions
publi<6C>es par le service : les routines c<>t<EFBFBD> serveur sont alors ex<65>cut<75>es automatiquement.
La s<>rialisation des donn<6E>es ainsi que la gestion de la couche r<>seau pour le transfert des
donn<6E>es sont g<>r<EFBFBD>es de mani<6E>re transparente par le module <a
href="../doxygen/html/group___qx_service.html" target="_blank">QxService</a>.
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_825">Liste des options
disponibles c<>t<EFBFBD> serveur</a></p>
<div class="manual_div_content">
Le serveur d'application C++ bas<61> sur le module <a
href="../doxygen/html/group___qx_service.html" target="_blank">QxService</a> dispose de
plusieurs param<61>tres accessibles par la classe singleton <a
href="../doxygen/html/classqx_1_1service_1_1_qx_connect.html"
target="_blank">qx::service::QxConnect</a> :
<ul>
<li><i>setPort() :</i> port d'<27>coute pour recevoir les requ<71>tes du client et envoyer les
r<>ponses du serveur ;</li>
<li><i>setThreadCount() :</i> nombre de threads disponibles c<>t<EFBFBD> serveur pour traiter les
demandes du client ;</li>
<li><i>setSerializationType() :</i> <a href="#manual_60">type de s<>rialisation</a> utilis<69>
pour envoyer les r<>ponses du serveur ;</li>
<li><i>setCompressData() :</i> permet de d<>finir si les donn<6E>es renvoy<6F>es par le serveur sont
compress<73>es ou non ;</li>
<li><i>setEncryptData() :</i> permet de d<>finir si les donn<6E>es renvoy<6F>es par le serveur sont
crypt<70>es ou non (avec possibilit<69> de renseigner une cl<63> de cryptage).</li>
</ul>
<br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_826">Param<EFBFBD>trage de la
connexion c<>t<EFBFBD> client</a></p>
<div class="manual_div_content">
La couche cliente bas<61>e sur le module <a href="../doxygen/html/group___qx_service.html"
target="_blank">QxService</a> dispose de plusieurs param<61>tres accessibles par la classe
singleton <a href="../doxygen/html/classqx_1_1service_1_1_qx_connect.html"
target="_blank">qx::service::QxConnect</a> :
<ul>
<li><i>setIp() :</i> adresse IP du serveur d'application C++ ;</li>
<li><i>setPort() :</i> port utilis<69> par le serveur d'application C++ ;</li>
<li><i>setSerializationType() :</i> <a href="#manual_60">type de s<>rialisation</a> utilis<69>
par la couche cliente pour envoyer les requ<71>tes du client au serveur ;</li>
<li><i>setCompressData() :</i> permet de d<>finir si les donn<6E>es envoy<6F>es au serveur sont
compress<73>es ou non ;</li>
<li><i>setEncryptData() :</i> permet de d<>finir si les donn<6E>es envoy<6F>es au serveur sont
crypt<70>es ou non (avec possibilit<69> de renseigner une cl<63> de cryptage).</li>
</ul>
<br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_830">Gestion de
l'authentification dans un service</a></p>
<div class="manual_div_content">
Il est classique d'impl<70>menter un contr<74>le au niveau du serveur pour v<>rifier l'utilisateur
connect<63> <20> la couche cliente.
L'interface <a href="../doxygen/html/classqx_1_1service_1_1_ix_service.html"
target="_blank">qx::service::IxService</a> (classe de base de tous les services enregistr<74>s
par le module <a href="../doxygen/html/group___qx_service.html" target="_blank">QxService</a>)
fournit des m<>thodes virtuelles qui peuvent <20>tre surcharg<72>es pour g<>rer cette probl<62>matique :
<ul>
<li><i>onBeforeProcess() :</i> m<>thode virtuelle appel<65>e syst<73>matiquement avant ex<65>cution de
la routine serveur ;</li>
<li><i>onAfterProcess() :</i> m<>thode virtuelle appel<65>e syst<73>matiquement apr<70>s ex<65>cution de
la routine serveur.</li>
</ul>
<br>
<b>Par exemple :</b> voici une classe de base nomm<6D>e <i>ParameterAuthentication</i> qui peut
<20>tre utilis<69>e par tous les param<61>tres d'entr<74>e/sortie, cette classe fournit 3 propri<72>t<EFBFBD>s
<i>login</i>, <i>password</i> et <i>token</i> :
<br><br>
<i>* Fichier ParameterAuthentication.h :</i><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">class</span> MY_DLL_EXPORT ParameterAuthentication<span class="operator"> :</span><span class="keyword"> public</span> qx<span class="operator">::</span>service<span class="operator">::</span>IxParameter<span class="operator">
{</span><span class="keyword">
public</span><span class="operator">:</span>
ParameterAuthentication<span class="operator">();</span><span class="keyword">
virtual</span><span class="operator"> ~</span>ParameterAuthentication<span class="operator">();</span>
QString login<span class="operator">;</span>
QString password<span class="operator">;</span>
QString token<span class="operator">;</span><span class="comment">
// etc..., put here all properties required by the authentication process
</span><span class="operator">
};</span><span class="keyword">
typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator">&lt;</span>ParameterAuthentication<span class="operator">&gt;</span> ParameterAuthentication_ptr<span class="operator">;</span>
QX_REGISTER_COMPLEX_CLASS_NAME_HPP_MY_DLL<span class="operator">(</span>ParameterAuthentication<span class="operator">,</span> qx<span class="operator">::</span>service<span class="operator">::</span>IxParameter<span class="operator">,</span><span class="int"> 0</span><span class="operator">,</span> ParameterAuthentication<span class="operator">)</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
<i>* Fichier ParameterAuthentication.cpp :</i><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>QX_REGISTER_COMPLEX_CLASS_NAME_CPP_MY_DLL<span class="operator">(</span>ParameterAuthentication<span class="operator">,</span> ParameterAuthentication<span class="operator">)</span><span class="keyword">
namespace</span> qx<span class="operator"> {</span><span class="keyword">
template</span><span class="operator"> &lt;&gt;</span><span class="type">
void</span> register_class<span class="operator">(</span>QxClass<span class="operator">&lt;</span>ParameterAuthentication<span class="operator">&gt; &amp;</span> t<span class="operator">)
{</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> ParameterAuthentication<span class="operator">::</span>login<span class="operator">,</span><span class="string"> "login"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> ParameterAuthentication<span class="operator">::</span>password<span class="operator">,</span><span class="string"> "password"</span><span class="operator">);</span>
t<span class="operator">.</span>data<span class="operator">(&amp;</span> ParameterAuthentication<span class="operator">::</span>token<span class="operator">,</span><span class="string"> "token"</span><span class="operator">);
}
}</span><span class="comment"> // namespace qx</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
Maintenant que l'on dispose d'une classe de base pour nos param<61>tres
(<i>ParameterAuthentication</i>), nous allons cr<63>er une classe de base utilis<69>e par tous nos
services nomm<6D>e <i>ServiceAuthentication&lt;INPUT, OUTPUT&gt;</i>.
Cette classe de base des services va surcharger la m<>thode virtuelle <i>onBeforeProcess()</i>
afin de g<>rer l'authentification avant ex<65>cution de la routine serveur :
<br><br>
<i>* Fichier ServiceAuthentication.h :</i><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="pre">#include "ParameterAuthentication.h"
</span><span class="keyword">
template</span><span class="operator"> &lt;</span><span class="keyword">class</span> INPUT<span class="operator">,</span><span class="keyword"> class</span> OUTPUT<span class="operator">&gt;</span><span class="keyword">
class</span> ServiceAuthentication<span class="operator"> :</span><span class="keyword"> public</span> qx<span class="operator">::</span>service<span class="operator">::</span>QxService<span class="operator">&lt;</span>INPUT<span class="operator">,</span> OUTPUT<span class="operator">&gt;
{</span><span class="keyword">
public</span><span class="operator">:</span>
ServiceAuthentication<span class="operator">(</span><span class="keyword">const</span> QString<span class="operator"> &amp;</span> sServiceName<span class="operator">) :</span> qx<span class="operator">::</span>service<span class="operator">::</span>QxService<span class="operator">&lt;</span>INPUT<span class="operator">,</span> OUTPUT<span class="operator">&gt;(</span>sServiceName<span class="operator">) { ; }</span><span class="keyword">
virtual</span><span class="operator"> ~</span>ServiceAuthentication<span class="operator">() { ; }</span>
<font style="background-color:yellow"><span class="keyword">virtual</span><span class="type"> void</span> onBeforeProcess<span class="operator">()</span></font>
<span class="operator">{</span><span class="comment">
// Here you can implement your own authentication control (checking login/password for example)
// You can get input authentication parameters like this :
</span> ParameterAuthentication_ptr pParams<span class="operator"> =</span> getInputParameter<span class="operator">();</span>
<i>pParams<span class="operator">-&gt;</span>login<span class="operator">,</span> pParams<span class="operator">-&gt;</span>password<span class="operator">,</span> etc<span class="operator">...</span></i><span class="comment">
// If authentication is not valid, then you can throw an exception (and stop process before executing service function)
</span><span class="flow"> throw</span> qx<span class="operator">::</span>exception<span class="operator">(</span><span class="string">"Authentication error !"</span><span class="operator">);
}
};</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
A pr<70>sent, nous disposons des classes de base <i>ParameterAuthentication</i> et
<i>ServiceAuthentication&lt;INPUT, OUTPUT&gt;</i> : toutes les classes de param<61>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<61>tres de l'utilisateur ne sont
pas valides.
<br><br>
<b>Remarque :</b> de la m<>me fa<66>on que pour g<>rer l'authentification, il est possible de mettre
en place des logs automatiques en surchargeant les m<>thodes virtuelles <i>onBeforeProcess()</i>
et <i>onAfterProcess()</i>.
<br><br>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_840">Requ<EFBFBD>tes client/serveur
asynchrones</a></p>
<div class="manual_div_content">
Par d<>faut, les requ<71>tes client/serveur sont synchrones : ce qui signifie que la couche cliente
attend la r<>ponse du serveur pour continuer son ex<65>cution.
Dans une interface graphique utilisateur (<i>GUI</i>), une requ<71>te client/serveur bloque
l'application (<i>freeze</i>) si elle est ex<65>cut<75>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 <a href="../doxygen/html/group___qx_service.html" target="_blank">QxService</a>
propose une solution simple pour effectuer des requ<71>tes asynchrones (qui ne bloquent donc pas
l'interface graphique de l'utilisateur) gr<67>ce <20> la classe <a
href="../doxygen/html/classqx_1_1service_1_1_qx_client_async.html"
target="_blank">qx::service::QxClientAsync</a>.
<br><br>
La classe <a href="../doxygen/html/classqx_1_1service_1_1_qx_client_async.html"
target="_blank">qx::service::QxClientAsync</a> utilise <a href="#manual_70">le moteur
d'introspection</a> de la biblioth<74>que QxOrm ainsi que le m<>canisme <a
href="http://doc.qt.io/qt-5/signalsandslots.html" target="_blank"><i>SIGNAL-SLOT</i> de
Qt</a>.
Elle prend en param<61>tre :
<ul>
<li>une instance de service ;</li>
<li>les param<61>tres d'entr<74>e/sortie du service ;</li>
<li>le nom de la routine serveur <20> ex<65>cuter (sous forme de chaine de caract<63>res) ;</li>
<li>une fonction <20> appeler une fois que la transaction est termin<69>e (connexion <20> l'<27>v<EFBFBD>nement
<i>signal</i> <i>finished()</i>).
</li>
</ul>
<br>
Voici l'exemple issu du <a href="./tutorial_2.html#tuto_302">tutoriel qxClientServer</a> qui
ex<65>cute une routine serveur de mani<6E>re asynchrone :
<br><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td title="main_dlg::onClickBtnDateTimeAsync(), main_dlg::onDateTimeAsyncFinished()">
<pre><span class="type">void</span> main_dlg<span class="operator">::</span>onClickBtnDateTimeAsync<span class="operator">()
{</span><span class="flow">
if</span><span class="operator"> (</span>m_pDateTimeAsync<span class="operator">) {</span> qDebug<span class="operator">(</span><span class="string">"[QxOrm] '%s' transaction is already running"</span><span class="operator">,</span><span class="string"> "server_infos::get_current_date_time"</span><span class="operator">);</span><span class="flow"> return</span><span class="operator">; }</span><span class="comment">
// Cr<43>ation d'une instance de service et appel <20> la m<>thode pour recevoir la date-heure courante du serveur (mode asynchrone)
</span> server_infos_ptr service<span class="operator"> =</span> server_infos_ptr<span class="operator">(</span><span class="keyword">new</span> server_infos<span class="operator">());</span>
m_pDateTimeAsync<span class="operator">.</span>reset<span class="operator">(</span><span class="keyword">new</span> qx<span class="operator">::</span>service<span class="operator">::</span>QxClientAsync<span class="operator">());</span>
QObject<span class="operator">::</span>connect<span class="operator">(</span>m_pDateTimeAsync<span class="operator">.</span>get<span class="operator">(),</span> SIGNAL<span class="operator">(</span>finished<span class="operator">()),</span><span class="keyword"> this</span><span class="operator">,</span> SLOT<span class="operator">(</span>onDateTimeAsyncFinished<span class="operator">()));</span>
m_pDateTimeAsync<span class="operator">-&gt;</span>setService<span class="operator">(</span>service<span class="operator">,</span><span class="string"> "get_current_date_time"</span><span class="operator">);</span>
m_pDateTimeAsync<span class="operator">-&gt;</span>start<span class="operator">();
}</span><span class="type">
void</span> main_dlg<span class="operator">::</span>onDateTimeAsyncFinished<span class="operator">()
{</span><span class="flow">
if</span><span class="operator"> (!</span> m_pDateTimeAsync<span class="operator"> || !</span> m_pDateTimeAsync<span class="operator">-&gt;</span>getService<span class="operator">()) {</span><span class="flow"> return</span><span class="operator">; }</span>
updateLastTransactionLog<span class="operator">(</span>m_pDateTimeAsync<span class="operator">-&gt;</span>getService<span class="operator">()-&gt;</span>getTransaction<span class="operator">());</span>
m_pDateTimeAsync<span class="operator">.</span>reset<span class="operator">();
}</span></pre>
</td>
</tr>
</tbody>
</table>
<br><br>
<b>Remarque :</b> l'exemple ci-dessus montre comment effectuer une requ<71>te asynchrone avec les
actions suivantes :
<ul>
<li>cr<EFBFBD>ation d'une instance d'un service (de type <i>server_infos_ptr</i> pour cet exemple) ;
</li>
<li>cr<EFBFBD>ation d'une instance de type <i>qx::service::QxClientAsync</i> ;</li>
<li>connexion <20> l'<27>v<EFBFBD>nement <i>finished</i> (pour indiquer qu'une r<>ponse du serveur vient
d'arriver) ;</li>
<li>passage de l'instance du service et de la m<>thode <20> appeler (sous forme de chaine de
caract<63>res) <20> l'objet <i>qx::service::QxClientAsync</i> ;</li>
<li>d<EFBFBD>marrage de la transaction avec l'appel de la m<>thode <i>start()</i>.</li>
</ul>
<br>
</div>
</div>
<p class="manual_p_title_1"><a class="manual_a_title_1" name="manual_90">Moteur mod<6F>le/vue (module
QxModelView)</a></p>
<div class="manual_div_content_1">
Le module <b><a href="../doxygen/html/group___qx_model_view.html"
target="_blank">QxModelView</a></b> permet d'utiliser <a
href="http://doc.qt.io/qt-5/modelview.html" target="_blank">le moteur model/view de Qt</a> avec
toutes les classes enregistr<74>es dans le contexte QxOrm :
<ul>
<li><a href="#manual_940">QML</a> : toute propri<72>t<EFBFBD> enregistr<74>e dans le contexte QxOrm est
accessible en QML : le module <b><a href="../doxygen/html/group___qx_model_view.html"
target="_blank">QxModelView</a></b> permet ainsi de faciliter l'int<6E>raction entre QML
et les bases de donn<6E>es ;</li>
<li><a href="#manual_950">Qt widgets</a> : utilisation de <i>QTableView</i> ou <i>QListView</i>
par exemple pour afficher/modifier le contenu d'une table de la base de donn<6E>es.</li>
</ul>
L'interface <a href="../doxygen/html/classqx_1_1_ix_model.html" target="_blank">qx::IxModel</a>
propose une base commune pour tous les mod<6F>les li<6C>s aux classes persistantes d<>clar<61>es dans le
contexte QxOrm.
Les m<>thodes de cette classe pr<70>fix<69>es par '<i>qx</i>' appellent les fonctions du <a
href="../doxygen/html/namespaceqx_1_1dao.html" target="_blank">namespace qx::dao</a> et
communiquent donc directement avec la base de donn<6E>es.
L'interface <a href="../doxygen/html/classqx_1_1_ix_model.html" target="_blank">qx::IxModel</a>
fournit <20>galement des m<>thodes d<>finies <b>Q_INVOKABLE</b> et sont donc accessibles directement en
<a href="#manual_940">QML</a> :
<ul>
<li><i>qxCount_() :</i> compte le nombre d'<27>l<EFBFBD>ments dans la table de la base de donn<6E>es associ<63>e
au mod<6F>le (avec possibilit<69> d'indiquer un filtre SQL) ;</li>
<li><i>qxFetchById_() :</i> alimente le mod<6F>le en fonction de l'identifiant pass<73> en param<61>tre ;
</li>
<li><i>qxFetchAll_() :</i> alimente le mod<6F>le avec tous les <20>l<EFBFBD>ments contenus dans la table de
la base de donn<6E>es associ<63>e au mod<6F>le ;</li>
<li><i>qxFetchByQuery_() :</i> alimente le mod<6F>le avec les <20>l<EFBFBD>ments de la table de la base de
donn<6E>es associ<63>e au mod<6F>le en fonction d'une requ<71>te SQL ;</li>
<li><i>qxFetchRow_() :</i> alimente (met <20> jour) une ligne du mod<6F>le (chaque ligne du mod<6F>le
dispose de son propre identifiant de base de donn<6E>es) ;</li>
<li><i>qxInsert_() :</i> ins<6E>re l'int<6E>gralit<69> du mod<6F>le en base de donn<6E>es ;</li>
<li><i>qxInsertRow_() :</i> ins<6E>re une ligne du mod<6F>le en base de donn<6E>es ;</li>
<li><i>qxUpdate_() :</i> met <20> jour l'int<6E>gralit<69> du mod<6F>le en base de donn<6E>es ;</li>
<li><i>qxUpdateRow_() :</i> met <20> jour une ligne du mod<6F>le en base de donn<6E>es ;</li>
<li><i>qxSave_() :</i> sauvegarde l'int<6E>gralit<69> du mod<6F>le en base de donn<6E>es (insertion ou mise
<20> jour) ;</li>
<li><i>qxSaveRow_() :</i> sauvegarde une ligne du mod<6F>le en base de donn<6E>es (insertion ou mise <20>
jour) ;</li>
<li><i>qxDeleteById_() :</i> supprime un <20>l<EFBFBD>ment de la base de donn<6E>es en fonction de
l'identifiant pass<73> en param<61>tre ;</li>
<li><i>qxDeleteAll_() :</i> supprime tous les <20>l<EFBFBD>ments de la table de la base de donn<6E>es
associ<63>e au mod<6F>le ;</li>
<li><i>qxDeleteByQuery_() :</i> supprime les <20>l<EFBFBD>ments de la table de la base de donn<6E>es associ<63>e
au mod<6F>le en fonction d'une requ<71>te SQL ;</li>
<li><i>qxDeleteRow_() :</i> supprime une ligne du mod<6F>le de la base de donn<6E>es (chaque ligne du
mod<6F>le dispose de son propre identifiant de base de donn<6E>es) ;</li>
<li><i>qxDestroyById_() :</i> supprime un <20>l<EFBFBD>ment de la base de donn<6E>es en fonction de
l'identifiant pass<73> en param<61>tre (avec prise en compte de <a href="#manual_3400">la
suppression logique</a>) ;</li>
<li><i>qxDestroyAll_() :</i> supprime tous les <20>l<EFBFBD>ments de la table de la base de donn<6E>es
associ<63>e au mod<6F>le (avec prise en compte de <a href="#manual_3400">la suppression
logique</a>) ;</li>
<li><i>qxDestroyByQuery_() :</i> supprime les <20>l<EFBFBD>ments de la table de la base de donn<6E>es
associ<63>e au mod<6F>le en fonction d'une requ<71>te SQL (avec prise en compte de <a
href="#manual_3400">la suppression logique</a>) ;</li>
<li><i>qxDestroyRow_() :</i> supprime une ligne du mod<6F>le de la base de donn<6E>es (chaque ligne du
mod<6F>le dispose de son propre identifiant de base de donn<6E>es), avec prise en compte de <a
href="#manual_3400">la suppression logique</a> ;</li>
<li><i>qxExecuteQuery_() :</i> alimente le mod<6F>le en fonction d'une <a
href="#manual_3610">requ<EFBFBD>te SQL personnalis<69>e ou proc<6F>dure stock<63>e</a> ;</li>
<li><i>qxExist_() :</i> teste l'existence d'un <20>l<EFBFBD>ment en fonction de l'identifiant pass<73> en
param<61>tre ;</li>
<li><i>qxValidate_() :</i> teste la validit<69> de l'int<6E>gralit<69> du mod<6F>le (<a
href="#manual_420">module QxValidator</a>) ;</li>
<li><i>qxValidateRow_() :</i> teste la validit<69> d'une ligne du mod<6F>le (<a
href="#manual_420">module QxValidator</a>).</li>
</ul>
<br />
<b>Remarque :</b> le projet de test <i>qxBlogModelView</i> pr<70>sent dans le dossier <i>./test/</i>
du package QxOrm montre comment cr<63>er rapidement un mod<6F>le et l'associer au moteur
<i>model/view</i> de Qt (d'abord dans <a href="#manual_950">un widget Qt</a>, puis dans <a
href="#manual_940">une vue QML</a>).
<br /><br />
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_910">D<EFBFBD>finir un mod<6F>le
"simple" (sans relation)</a></p>
<div class="manual_div_content">
Toute classe enregistr<74>e dans le contexte QxOrm peut <20>tre utilis<69>e en tant que mod<6F>le afin
d'alimenter des vues.
La classe de base <a href="../doxygen/html/classqx_1_1_ix_model.html"
target="_blank">qx::IxModel</a> des mod<6F>les QxOrm h<>rite de la classe Qt <a
href="http://doc.qt.io/qt-5/qabstractitemmodel.html" target="_blank">QAbstractItemModel</a> :
les mod<6F>les QxOrm sont donc enti<74>rement compatibles avec <a
href="http://doc.qt.io/qt-5/modelview.html" target="_blank">le moteur model/view de Qt</a>.
<br /><br />
Une seule ligne de code est suffisante pour instancier un mod<6F>le QxOrm :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre> <font style="background-color:yellow">qx<span class="operator">::</span>IxModel<span class="operator"> *</span> pModel<span class="operator"> =</span><span class="keyword"> new</span> qx<span class="operator">::</span>QxModel<span class="operator">&lt;</span>MyClass<span class="operator">&gt;();</span></font> </pre>
</td>
</tr>
</tbody>
</table>
<br />
<b>Remarque :</b> le mod<6F>le cr<63><72> avec cette ligne de code expose automatiquement toutes les
propri<72>t<EFBFBD>s enregistr<74>es dans le contexte QxOrm au <a href="http://doc.qt.io/qt-5/modelview.html"
target="_blank">moteur model/view</a>.
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_920">Mod<EFBFBD>les avec relations
(notion de mod<6F>les imbriqu<71>s)</a></p>
<div class="manual_div_content">
Adapter les relations entre classe (<i>1-n</i>, <i>n-1</i> ou <i>n-n</i>) au moteur model/view
de Qt est complexe : la solution propos<6F>e par la biblioth<74>que QxOrm est l'utilisation de <a
href="http://christophe-dumez.developpez.com/tutoriels/qt/qml/exposer-modeles-imbriques/"
target="_blank">mod<EFBFBD>les imbriqu<71>s</a>.
Pour plus de d<>tails sur la notion de mod<6F>les imbriqu<71>s, <a
href="http://christophe-dumez.developpez.com/tutoriels/qt/qml/exposer-modeles-imbriques/"
target="_blank">un tutoriel est disponible sur le site developpez.com</a>.
<br /><br />
Pour utiliser les relations (<i>1-n</i>, <i>n-1</i> ou <i>n-n</i>) avec le <a
href="../doxygen/html/group___qx_model_view.html" target="_blank">module QxModelView</a>, il
est important de comprendre qu'il peut y avoir <b>une hi<68>rarchie entre mod<6F>les</b> (un mod<6F>le
parent peut avoir plusieurs mod<6F>les enfants associ<63>s, c'est la notion de <a
href="http://christophe-dumez.developpez.com/tutoriels/qt/qml/exposer-modeles-imbriques/"
target="_blank">mod<EFBFBD>les imbriqu<71>s</a>).
<br /><br />
Afin de pouvoir travailler avec des relations (mod<6F>les imbriqu<71>s), il est n<>cessaire de cr<63>er
des classes mod<6F>les qui h<>ritent de : <a href="../doxygen/html/classqx_1_1_ix_model.html"
target="_blank">qx::QxModel&lt;T&gt;</a>.
Ainsi, toutes les propri<72>t<EFBFBD>s simples (non relation) sont automatiquement expos<6F>es aux vues
(gr<67>ce <20> la classe de base), il reste <20> <20>crire uniquement les accesseurs pour acc<63>der aux
relations.
L'application <b>QxEntityEditor</b> est livr<76>e avec le plugin <i>QxEECppModelViewExport</i> :
<b>ce plugin g<>n<EFBFBD>re automatiquement le code source pour pouvoir travailler avec des mod<6F>les
imbriqu<71>s</b>.
<br /><br />
Voici un exemple de code g<>n<EFBFBD>r<EFBFBD> par l'application <b>QxEntityEditor</b> afin de cr<63>er un mod<6F>le
bas<61> sur la classe <i>blog</i> (voir <a href="./tutorial.html" target="_blank">le tutoriel
<i>qxBlog</i></a> pour plus de d<>tails).
La classe <i>blog</i> dispose de 3 relations : <i>author (n-1)</i>, <i>list_of_comment (1-n)</i>
et <i>list_of_category (n-n)</i> :
<br /><br />
<i>* Fichier blog.model_view.gen.h :</i><br>
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">namespace</span> model_view<span class="operator"> {</span><span class="keyword">
typedef</span> qx<span class="operator">::</span>QxModel<span class="operator">&lt;</span>blog<span class="operator">&gt;</span> blog_model_base_class<span class="operator">;</span><span class="keyword">
class</span> QXBLOG_MODEL_VIEW_EXPORT blog_model<span class="operator"> :</span><span class="keyword"> public</span> blog_model_base_class<span class="operator">
{</span>
Q_OBJECT<span class="keyword">
public</span><span class="operator">:</span>
blog_model<span class="operator">(</span>QObject<span class="operator"> *</span> parent<span class="operator"> =</span><span class="int"> 0</span><span class="operator">);</span>
blog_model<span class="operator">(</span>qx<span class="operator">::</span>IxModel<span class="operator"> *</span> other<span class="operator">,</span> QObject<span class="operator"> *</span> parent<span class="operator">);</span><span class="keyword">
virtual</span><span class="operator"> ~</span>blog_model<span class="operator">();</span>
<font style="background-color:yellow">Q_INVOKABLE QObject<span class="operator"> *</span> author<span class="operator">(</span><span class="type">int</span> row<span class="operator">,</span><span class="type"> bool</span> bLoadFromDatabase<span class="operator"> =</span><span class="bool"> false</span><span class="operator">,</span><span class="keyword"> const</span> QString<span class="operator"> &amp;</span> sAppendRelations<span class="operator"> =</span> QString<span class="operator">());</span></font>
<font style="background-color:yellow">Q_INVOKABLE QObject<span class="operator"> *</span> list_of_comment<span class="operator">(</span><span class="type">int</span> row<span class="operator">,</span><span class="type"> bool</span> bLoadFromDatabase<span class="operator"> =</span><span class="bool"> false</span><span class="operator">,</span><span class="keyword"> const</span> QString<span class="operator"> &amp;</span> sAppendRelations<span class="operator"> =</span> QString<span class="operator">());</span></font>
<font style="background-color:yellow">Q_INVOKABLE QObject<span class="operator"> *</span> list_of_category<span class="operator">(</span><span class="type">int</span> row<span class="operator">,</span><span class="type"> bool</span> bLoadFromDatabase<span class="operator"> =</span><span class="bool"> false</span><span class="operator">,</span><span class="keyword"> const</span> QString<span class="operator"> &amp;</span> sAppendRelations<span class="operator"> =</span> QString<span class="operator">());</span></font>
<span class="comment">/* List of properties exposed by the model (3) :
- blog_id
- title
- text
*/</span><span class="keyword">
protected</span><span class="operator">:</span><span class="keyword">
<font style="background-color:yellow">virtual</span><span class="type"> void</span> syncNestedModel<span class="operator">(</span><span class="type">int</span> row<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relation<span class="operator">);</span></font><span class="keyword">
<font style="background-color:yellow">virtual</span><span class="type"> void</span> syncAllNestedModel<span class="operator">(</span><span class="keyword">const</span> QStringList<span class="operator"> &amp;</span> relation<span class="operator">);</span></font><span class="operator">
};
}</span><span class="comment"> // namespace model_view</span></pre>
</td>
</tr>
</tbody>
</table>
<br />
<i>* Fichier blog.model_view.gen.cpp :</i><br>
<div style="width:1000px; height:300px; overflow:auto; background-color:white;">
<pre><span class="keyword">namespace</span> model_view<span class="operator"> {</span>
blog_model<span class="operator">::</span>blog_model<span class="operator">(</span>QObject<span class="operator"> *</span> parent<span class="comment"> /* = 0 */</span><span class="operator">) :</span> blog_model_base_class<span class="operator">(</span>parent<span class="operator">) { ; }</span>
blog_model<span class="operator">::</span>blog_model<span class="operator">(</span>qx<span class="operator">::</span>IxModel<span class="operator"> *</span> other<span class="operator">,</span> QObject<span class="operator"> *</span> parent<span class="operator">) :</span> blog_model_base_class<span class="operator">(</span>other<span class="operator">,</span> parent<span class="operator">) { ; }</span>
blog_model<span class="operator">::~</span>blog_model<span class="operator">() { ; }</span>
<font style="background-color:yellow">QObject<span class="operator"> *</span> blog_model<span class="operator">::</span>author<span class="operator">(</span><span class="type">int</span> row<span class="operator">,</span><span class="type"> bool</span> bLoadFromDatabase<span class="comment"> /* = false */</span><span class="operator">,</span><span class="keyword"> const</span> QString<span class="operator"> &amp;</span> sAppendRelations<span class="comment"> /* = QString() */</span><span class="operator">)</span></font><span class="operator">
{</span>
QString sRelation<span class="operator"> =</span><span class="string"> "author"</span><span class="operator">;</span>
qx<span class="operator">::</span>IxModel<span class="operator"> *</span> pChild<span class="operator"> = (</span>bLoadFromDatabase<span class="operator"> ?</span> NULL<span class="operator"> :</span><span class="keyword"> this</span><span class="operator">-&gt;</span>getChild<span class="operator">(</span>row<span class="operator">,</span> sRelation<span class="operator">));</span><span class="flow">
if</span><span class="operator"> (</span>pChild<span class="operator">) {</span><span class="flow"> return</span><span class="keyword"> static_cast</span><span class="operator">&lt;</span>QObject<span class="operator"> *&gt;(</span>pChild<span class="operator">); }</span><span class="flow">
if</span><span class="operator"> ((</span>row<span class="operator"> &lt;</span><span class="int"> 0</span><span class="operator">) || (</span>row<span class="operator"> &gt;=</span><span class="keyword"> this</span><span class="operator">-&gt;</span>m_model<span class="operator">.</span>count<span class="operator">())) {</span> qAssert<span class="operator">(</span><span class="bool">false</span><span class="operator">);</span><span class="flow"> return</span> NULL<span class="operator">; }</span>
blog_model_base_class<span class="operator">::</span>type_ptr ptr<span class="operator"> =</span><span class="keyword"> this</span><span class="operator">-&gt;</span>m_model<span class="operator">.</span>getByIndex<span class="operator">(</span>row<span class="operator">);</span><span class="flow">
if</span><span class="operator"> (!</span> ptr<span class="operator">) {</span> qAssert<span class="operator">(</span><span class="bool">false</span><span class="operator">);</span><span class="flow"> return</span> NULL<span class="operator">; }</span><span class="type">
long</span> id<span class="operator"> =</span> ptr<span class="operator">-&gt;</span>getblog_id<span class="operator">();</span>
blog<span class="operator">::</span>type_author value<span class="operator"> =</span> ptr<span class="operator">-&gt;</span>getauthor<span class="operator">();</span><span class="flow">
if</span><span class="operator"> (</span>bLoadFromDatabase<span class="operator">)
{</span><span class="flow">
if</span><span class="operator"> (!</span> sAppendRelations<span class="operator">.</span>isEmpty<span class="operator">() &amp;&amp; !</span> sAppendRelations<span class="operator">.</span>startsWith<span class="operator">(</span><span class="string">"-&gt;"</span><span class="operator">) &amp;&amp; !</span> sAppendRelations<span class="operator">.</span>startsWith<span class="operator">(</span><span class="string">"&gt;&gt;"</span><span class="operator">)) {</span> sRelation<span class="operator"> +=</span><span class="string"> "-&gt;"</span><span class="operator"> +</span> sAppendRelations<span class="operator">; }</span><span class="flow">
else if</span><span class="operator"> (!</span> sAppendRelations<span class="operator">.</span>isEmpty<span class="operator">()) {</span> sRelation<span class="operator"> +=</span> sAppendRelations<span class="operator">; }</span>
blog tmp<span class="operator">;</span>
tmp<span class="operator">.</span>setblog_id<span class="operator">(</span>id<span class="operator">);</span><span class="keyword">
this</span><span class="operator">-&gt;</span>m_lastError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_id_with_relation<span class="operator">(</span>sRelation<span class="operator">,</span> tmp<span class="operator">);</span><span class="flow">
if</span><span class="operator"> (</span><span class="keyword">this</span><span class="operator">-&gt;</span>m_lastError<span class="operator">.</span>isValid<span class="operator">()) {</span><span class="flow"> return</span> NULL<span class="operator">; }</span>
value<span class="operator"> =</span> tmp<span class="operator">.</span>getauthor<span class="operator">();</span>
ptr<span class="operator">-&gt;</span>setauthor<span class="operator">(</span>value<span class="operator">);
}</span>
model_view<span class="operator">::</span>author_model<span class="operator"> *</span> pNewChild<span class="operator"> =</span> NULL<span class="operator">;</span>
pChild<span class="operator"> =</span> qx<span class="operator">::</span>model_view<span class="operator">::</span>create_nested_model_with_type<span class="operator">(</span><span class="keyword">this</span><span class="operator">,</span> QModelIndex<span class="operator">(),</span> value<span class="operator">,</span> pNewChild<span class="operator">);</span><span class="flow">
if</span><span class="operator"> (</span>pChild<span class="operator">) {</span><span class="keyword"> this</span><span class="operator">-&gt;</span>insertChild<span class="operator">(</span>row<span class="operator">,</span><span class="string"> "author"</span><span class="operator">,</span> pChild<span class="operator">); }</span><span class="flow">
return</span><span class="keyword"> static_cast</span><span class="operator">&lt;</span>QObject<span class="operator"> *&gt;(</span>pChild<span class="operator">);
}</span>
<font style="background-color:yellow">QObject<span class="operator"> *</span> blog_model<span class="operator">::</span>list_of_comment<span class="operator">(</span><span class="type">int</span> row<span class="operator">,</span><span class="type"> bool</span> bLoadFromDatabase<span class="comment"> /* = false */</span><span class="operator">,</span><span class="keyword"> const</span> QString<span class="operator"> &amp;</span> sAppendRelations<span class="comment"> /* = QString() */</span><span class="operator">)</span></font><span class="operator">
{</span>
QString sRelation<span class="operator"> =</span><span class="string"> "list_of_comment"</span><span class="operator">;</span>
qx<span class="operator">::</span>IxModel<span class="operator"> *</span> pChild<span class="operator"> = (</span>bLoadFromDatabase<span class="operator"> ?</span> NULL<span class="operator"> :</span><span class="keyword"> this</span><span class="operator">-&gt;</span>getChild<span class="operator">(</span>row<span class="operator">,</span> sRelation<span class="operator">));</span><span class="flow">
if</span><span class="operator"> (</span>pChild<span class="operator">) {</span><span class="flow"> return</span><span class="keyword"> static_cast</span><span class="operator">&lt;</span>QObject<span class="operator"> *&gt;(</span>pChild<span class="operator">); }</span><span class="flow">
if</span><span class="operator"> ((</span>row<span class="operator"> &lt;</span><span class="int"> 0</span><span class="operator">) || (</span>row<span class="operator"> &gt;=</span><span class="keyword"> this</span><span class="operator">-&gt;</span>m_model<span class="operator">.</span>count<span class="operator">())) {</span> qAssert<span class="operator">(</span><span class="bool">false</span><span class="operator">);</span><span class="flow"> return</span> NULL<span class="operator">; }</span>
blog_model_base_class<span class="operator">::</span>type_ptr ptr<span class="operator"> =</span><span class="keyword"> this</span><span class="operator">-&gt;</span>m_model<span class="operator">.</span>getByIndex<span class="operator">(</span>row<span class="operator">);</span><span class="flow">
if</span><span class="operator"> (!</span> ptr<span class="operator">) {</span> qAssert<span class="operator">(</span><span class="bool">false</span><span class="operator">);</span><span class="flow"> return</span> NULL<span class="operator">; }</span><span class="type">
long</span> id<span class="operator"> =</span> ptr<span class="operator">-&gt;</span>getblog_id<span class="operator">();</span>
blog<span class="operator">::</span>type_list_of_comment value<span class="operator"> =</span> ptr<span class="operator">-&gt;</span>getlist_of_comment<span class="operator">();</span><span class="flow">
if</span><span class="operator"> (</span>bLoadFromDatabase<span class="operator">)
{</span><span class="flow">
if</span><span class="operator"> (!</span> sAppendRelations<span class="operator">.</span>isEmpty<span class="operator">() &amp;&amp; !</span> sAppendRelations<span class="operator">.</span>startsWith<span class="operator">(</span><span class="string">"-&gt;"</span><span class="operator">) &amp;&amp; !</span> sAppendRelations<span class="operator">.</span>startsWith<span class="operator">(</span><span class="string">"&gt;&gt;"</span><span class="operator">)) {</span> sRelation<span class="operator"> +=</span><span class="string"> "-&gt;"</span><span class="operator"> +</span> sAppendRelations<span class="operator">; }</span><span class="flow">
else if</span><span class="operator"> (!</span> sAppendRelations<span class="operator">.</span>isEmpty<span class="operator">()) {</span> sRelation<span class="operator"> +=</span> sAppendRelations<span class="operator">; }</span>
blog tmp<span class="operator">;</span>
tmp<span class="operator">.</span>setblog_id<span class="operator">(</span>id<span class="operator">);</span><span class="keyword">
this</span><span class="operator">-&gt;</span>m_lastError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_id_with_relation<span class="operator">(</span>sRelation<span class="operator">,</span> tmp<span class="operator">);</span><span class="flow">
if</span><span class="operator"> (</span><span class="keyword">this</span><span class="operator">-&gt;</span>m_lastError<span class="operator">.</span>isValid<span class="operator">()) {</span><span class="flow"> return</span> NULL<span class="operator">; }</span>
value<span class="operator"> =</span> tmp<span class="operator">.</span>getlist_of_comment<span class="operator">();</span>
ptr<span class="operator">-&gt;</span>setlist_of_comment<span class="operator">(</span>value<span class="operator">);
}</span>
model_view<span class="operator">::</span>comment_model<span class="operator"> *</span> pNewChild<span class="operator"> =</span> NULL<span class="operator">;</span>
pChild<span class="operator"> =</span> qx<span class="operator">::</span>model_view<span class="operator">::</span>create_nested_model_with_type<span class="operator">(</span><span class="keyword">this</span><span class="operator">,</span> QModelIndex<span class="operator">(),</span> value<span class="operator">,</span> pNewChild<span class="operator">);</span><span class="flow">
if</span><span class="operator"> (</span>pChild<span class="operator">) {</span><span class="keyword"> this</span><span class="operator">-&gt;</span>insertChild<span class="operator">(</span>row<span class="operator">,</span><span class="string"> "list_of_comment"</span><span class="operator">,</span> pChild<span class="operator">); }</span><span class="flow">
return</span><span class="keyword"> static_cast</span><span class="operator">&lt;</span>QObject<span class="operator"> *&gt;(</span>pChild<span class="operator">);
}</span>
<font style="background-color:yellow">QObject<span class="operator"> *</span> blog_model<span class="operator">::</span>list_of_category<span class="operator">(</span><span class="type">int</span> row<span class="operator">,</span><span class="type"> bool</span> bLoadFromDatabase<span class="comment"> /* = false */</span><span class="operator">,</span><span class="keyword"> const</span> QString<span class="operator"> &amp;</span> sAppendRelations<span class="comment"> /* = QString() */</span><span class="operator">)</span></font><span class="operator">
{</span>
QString sRelation<span class="operator"> =</span><span class="string"> "list_of_category"</span><span class="operator">;</span>
qx<span class="operator">::</span>IxModel<span class="operator"> *</span> pChild<span class="operator"> = (</span>bLoadFromDatabase<span class="operator"> ?</span> NULL<span class="operator"> :</span><span class="keyword"> this</span><span class="operator">-&gt;</span>getChild<span class="operator">(</span>row<span class="operator">,</span> sRelation<span class="operator">));</span><span class="flow">
if</span><span class="operator"> (</span>pChild<span class="operator">) {</span><span class="flow"> return</span><span class="keyword"> static_cast</span><span class="operator">&lt;</span>QObject<span class="operator"> *&gt;(</span>pChild<span class="operator">); }</span><span class="flow">
if</span><span class="operator"> ((</span>row<span class="operator"> &lt;</span><span class="int"> 0</span><span class="operator">) || (</span>row<span class="operator"> &gt;=</span><span class="keyword"> this</span><span class="operator">-&gt;</span>m_model<span class="operator">.</span>count<span class="operator">())) {</span> qAssert<span class="operator">(</span><span class="bool">false</span><span class="operator">);</span><span class="flow"> return</span> NULL<span class="operator">; }</span>
blog_model_base_class<span class="operator">::</span>type_ptr ptr<span class="operator"> =</span><span class="keyword"> this</span><span class="operator">-&gt;</span>m_model<span class="operator">.</span>getByIndex<span class="operator">(</span>row<span class="operator">);</span><span class="flow">
if</span><span class="operator"> (!</span> ptr<span class="operator">) {</span> qAssert<span class="operator">(</span><span class="bool">false</span><span class="operator">);</span><span class="flow"> return</span> NULL<span class="operator">; }</span><span class="type">
long</span> id<span class="operator"> =</span> ptr<span class="operator">-&gt;</span>getblog_id<span class="operator">();</span>
blog<span class="operator">::</span>type_list_of_category value<span class="operator"> =</span> ptr<span class="operator">-&gt;</span>getlist_of_category<span class="operator">();</span><span class="flow">
if</span><span class="operator"> (</span>bLoadFromDatabase<span class="operator">)
{</span><span class="flow">
if</span><span class="operator"> (!</span> sAppendRelations<span class="operator">.</span>isEmpty<span class="operator">() &amp;&amp; !</span> sAppendRelations<span class="operator">.</span>startsWith<span class="operator">(</span><span class="string">"-&gt;"</span><span class="operator">) &amp;&amp; !</span> sAppendRelations<span class="operator">.</span>startsWith<span class="operator">(</span><span class="string">"&gt;&gt;"</span><span class="operator">)) {</span> sRelation<span class="operator"> +=</span><span class="string"> "-&gt;"</span><span class="operator"> +</span> sAppendRelations<span class="operator">; }</span><span class="flow">
else if</span><span class="operator"> (!</span> sAppendRelations<span class="operator">.</span>isEmpty<span class="operator">()) {</span> sRelation<span class="operator"> +=</span> sAppendRelations<span class="operator">; }</span>
blog tmp<span class="operator">;</span>
tmp<span class="operator">.</span>setblog_id<span class="operator">(</span>id<span class="operator">);</span><span class="keyword">
this</span><span class="operator">-&gt;</span>m_lastError<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_id_with_relation<span class="operator">(</span>sRelation<span class="operator">,</span> tmp<span class="operator">);</span><span class="flow">
if</span><span class="operator"> (</span><span class="keyword">this</span><span class="operator">-&gt;</span>m_lastError<span class="operator">.</span>isValid<span class="operator">()) {</span><span class="flow"> return</span> NULL<span class="operator">; }</span>
value<span class="operator"> =</span> tmp<span class="operator">.</span>getlist_of_category<span class="operator">();</span>
ptr<span class="operator">-&gt;</span>setlist_of_category<span class="operator">(</span>value<span class="operator">);
}</span>
model_view<span class="operator">::</span>category_model<span class="operator"> *</span> pNewChild<span class="operator"> =</span> NULL<span class="operator">;</span>
pChild<span class="operator"> =</span> qx<span class="operator">::</span>model_view<span class="operator">::</span>create_nested_model_with_type<span class="operator">(</span><span class="keyword">this</span><span class="operator">,</span> QModelIndex<span class="operator">(),</span> value<span class="operator">,</span> pNewChild<span class="operator">);</span><span class="flow">
if</span><span class="operator"> (</span>pChild<span class="operator">) {</span><span class="keyword"> this</span><span class="operator">-&gt;</span>insertChild<span class="operator">(</span>row<span class="operator">,</span><span class="string"> "list_of_category"</span><span class="operator">,</span> pChild<span class="operator">); }</span><span class="flow">
return</span><span class="keyword"> static_cast</span><span class="operator">&lt;</span>QObject<span class="operator"> *&gt;(</span>pChild<span class="operator">);
}</span><span class="type">
<font style="background-color:yellow">void</span> blog_model<span class="operator">::</span>syncNestedModel<span class="operator">(</span><span class="type">int</span> row<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &amp;</span> relation<span class="operator">)</span></font><span class="operator">
{</span>
Q_UNUSED<span class="operator">(</span>relation<span class="operator">);</span>
qx<span class="operator">::</span>IxModel<span class="operator"> *</span> pNestedModel<span class="operator"> =</span> NULL<span class="operator">;</span><span class="flow">
if</span><span class="operator"> ((</span>row<span class="operator"> &lt;</span><span class="int"> 0</span><span class="operator">) || (</span>row<span class="operator"> &gt;=</span><span class="keyword"> this</span><span class="operator">-&gt;</span>m_model<span class="operator">.</span>count<span class="operator">())) {</span><span class="flow"> return</span><span class="operator">; }</span>
blog_model_base_class<span class="operator">::</span>type_ptr ptr<span class="operator"> =</span><span class="keyword"> this</span><span class="operator">-&gt;</span>m_model<span class="operator">.</span>getByIndex<span class="operator">(</span>row<span class="operator">);</span><span class="flow">
if</span><span class="operator"> (!</span> ptr<span class="operator">) {</span><span class="flow"> return</span><span class="operator">; }</span>
pNestedModel<span class="operator"> =</span><span class="keyword"> this</span><span class="operator">-&gt;</span>getChild<span class="operator">(</span>row<span class="operator">,</span><span class="string"> "author"</span><span class="operator">);</span><span class="flow">
if</span><span class="operator"> (</span>pNestedModel<span class="operator">)
{</span><span class="keyword">
this</span><span class="operator">-&gt;</span>syncNestedModelRecursive<span class="operator">(</span>pNestedModel<span class="operator">,</span> relation<span class="operator">);</span>
blog<span class="operator">::</span>type_author value<span class="operator">;</span>
qx<span class="operator">::</span>model_view<span class="operator">::</span>sync_nested_model<span class="operator">(</span>pNestedModel<span class="operator">,</span> value<span class="operator">);</span>
ptr<span class="operator">-&gt;</span>setauthor<span class="operator">(</span>value<span class="operator">);
}</span>
pNestedModel<span class="operator"> =</span><span class="keyword"> this</span><span class="operator">-&gt;</span>getChild<span class="operator">(</span>row<span class="operator">,</span><span class="string"> "list_of_comment"</span><span class="operator">);</span><span class="flow">
if</span><span class="operator"> (</span>pNestedModel<span class="operator">)
{</span><span class="keyword">
this</span><span class="operator">-&gt;</span>syncNestedModelRecursive<span class="operator">(</span>pNestedModel<span class="operator">,</span> relation<span class="operator">);</span>
blog<span class="operator">::</span>type_list_of_comment value<span class="operator">;</span>
qx<span class="operator">::</span>model_view<span class="operator">::</span>sync_nested_model<span class="operator">(</span>pNestedModel<span class="operator">,</span> value<span class="operator">);</span>
ptr<span class="operator">-&gt;</span>setlist_of_comment<span class="operator">(</span>value<span class="operator">);
}</span>
pNestedModel<span class="operator"> =</span><span class="keyword"> this</span><span class="operator">-&gt;</span>getChild<span class="operator">(</span>row<span class="operator">,</span><span class="string"> "list_of_category"</span><span class="operator">);</span><span class="flow">
if</span><span class="operator"> (</span>pNestedModel<span class="operator">)
{</span><span class="keyword">
this</span><span class="operator">-&gt;</span>syncNestedModelRecursive<span class="operator">(</span>pNestedModel<span class="operator">,</span> relation<span class="operator">);</span>
blog<span class="operator">::</span>type_list_of_category value<span class="operator">;</span>
qx<span class="operator">::</span>model_view<span class="operator">::</span>sync_nested_model<span class="operator">(</span>pNestedModel<span class="operator">,</span> value<span class="operator">);</span>
ptr<span class="operator">-&gt;</span>setlist_of_category<span class="operator">(</span>value<span class="operator">);
}
}</span><span class="type">
<font style="background-color:yellow">void</span> blog_model<span class="operator">::</span>syncAllNestedModel<span class="operator">(</span><span class="keyword">const</span> QStringList<span class="operator"> &amp;</span> relation<span class="operator">)</span></font><span class="operator">
{</span><span class="flow">
if</span><span class="operator"> (</span><span class="keyword">this</span><span class="operator">-&gt;</span>m_lstChild<span class="operator">.</span>count<span class="operator">() &lt;=</span><span class="int"> 0</span><span class="operator">) {</span><span class="flow"> return</span><span class="operator">; }</span><span class="flow">
for</span><span class="operator"> (</span><span class="type">long</span> l<span class="operator"> =</span><span class="int"> 0</span><span class="operator">;</span> l<span class="operator"> &lt;</span><span class="keyword"> this</span><span class="operator">-&gt;</span>m_model<span class="operator">.</span>count<span class="operator">();</span> l<span class="operator">++)
{</span><span class="keyword"> this</span><span class="operator">-&gt;</span>syncNestedModel<span class="operator">(</span><span class="keyword">static_cast</span><span class="operator">&lt;</span><span class="type">int</span><span class="operator">&gt;(</span>l<span class="operator">),</span> relation<span class="operator">); }
}
}</span><span class="comment"> // namespace model_view</span></pre>
</div>
<br /><br />
<b>Remarque :</b> comme on peut le constater sur l'exemple ci-dessus, le code source <20> <20>crire
pour travailler avec des mod<6F>les imbriqu<71>s est verbeux.
<b>Afin de travailler avec les relations, il est donc fortement recommand<6E> d'utiliser
l'application QxEntityEditor afin de g<>n<EFBFBD>rer le code source automatiquement.</b>
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_940">Int<EFBFBD>raction avec les vues
QML</a></p>
<div class="manual_div_content">
Voici un exemple en QML (en Qt5, le module <b><a
href="../doxygen/html/group___qx_model_view.html" target="_blank">QxModelView</a></b>
<20>tant <20>galement compatible avec Qt4) qui utilise la table '<i>author</i>' d<>finie dans <a
href="./tutorial.html" target="_blank">le tutoriel <i>qxBlog</i></a> (le code source de cet
exemple QML est disponible dans le projet de test <i>qxBlogModelView</i> pr<70>sent dans le package
QxOrm) :<br />
<br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Create a model and fetch all data from database
</span>qx<span class="operator">::</span>IxModel<span class="operator"> *</span> pModel<span class="operator"> =</span><span class="keyword"> new</span> qx<span class="operator">::</span>QxModel<span class="operator">&lt;</span>author<span class="operator">&gt;();</span>
pModel<span class="operator">-&gt;</span>qxFetchAll<span class="operator">();</span><span class="comment">
// Associate the model to a QML view and display it
</span>QQuickView qmlView<span class="operator">;</span>
qmlView<span class="operator">.</span>rootContext<span class="operator">()-&gt;</span>setContextProperty<span class="operator">(</span><span class="string">"myModel"</span><span class="operator">,</span> pModel<span class="operator">);</span>
qmlView<span class="operator">.</span>setSource<span class="operator">(</span>QUrl<span class="operator">(</span><span class="string">"qrc:/documents/main.qml"</span><span class="operator">));</span>
qmlView<span class="operator">.</span>show<span class="operator">();</span></pre>
</td>
</tr>
</tbody>
</table>
<br />
Et voici le contenu du fichier '<i>main.qml</i>' :<br />
<br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>import QtQuick<span class="float"> 2.1</span>
import QtQuick<span class="operator">.</span>Controls<span class="float"> 1.0</span>
Item<span class="operator"> {</span>
width<span class="operator">:</span><span class="int"> 400</span>
height<span class="operator">:</span><span class="int"> 300</span>
Row<span class="operator"> {</span>
height<span class="operator">:</span><span class="int"> 20</span>
spacing<span class="operator">:</span><span class="int"> 20</span>
Button<span class="operator"> {</span>
text<span class="operator">:</span><span class="string"> "Clear"</span>
onClicked<span class="operator">:</span> myModel<span class="operator">.</span>clear<span class="operator">()
}</span>
Button<span class="operator"> {</span>
text<span class="operator">:</span><span class="string"> "Fetch All"</span>
onClicked<span class="operator">:</span> myModel<span class="operator">.</span>qxFetchAll_<span class="operator">()
}</span>
Button<span class="operator"> {</span>
text<span class="operator">:</span><span class="string"> "Save"</span>
onClicked<span class="operator">:</span> myModel<span class="operator">.</span>qxSave_<span class="operator">()
}
}</span>
ListView<span class="operator"> {</span>
y<span class="operator">:</span><span class="int"> 30</span>
height<span class="operator">:</span><span class="int"> 270</span>
model<span class="operator">:</span> myModel
delegate<span class="operator">:</span> Row<span class="operator"> {</span>
height<span class="operator">:</span><span class="int"> 20</span>
spacing<span class="operator">:</span><span class="int"> 10</span>
Text<span class="operator"> {</span> text<span class="operator">:</span><span class="string"> "id: "</span><span class="operator"> +</span> author_id<span class="operator"> }</span>
TextField<span class="operator"> {</span>
text<span class="operator">:</span> name
onTextChanged<span class="operator">:</span> name<span class="operator"> =</span> text<span class="operator">
}
}
}
}</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Ce qui donne le r<>sultat suivant <20> l'ex<65>cution :<br />
<br />
<img alt="qx_model_view_02" src="./resource/qx_model_view_02.png" border="0" /><br />
<br />
<b>Remarque :</b> comme on peut le constater dans le fichier '<i>main.qml</i>', les propri<72>t<EFBFBD>s
'<i>author_id</i>' et '<i>name</i>' du mod<6F>le '<i>author</i>' (variable <i>myModel</i>) sont
accessibles automatiquement en lecture/<2F>criture (car elles ont <20>t<EFBFBD> enregistr<74>es dans le contexte
QxOrm).<br />
De plus, l'interface <a href="../doxygen/html/classqx_1_1_ix_model.html"
target="_blank">qx::IxModel</a> propose une liste de m<>thodes accessibles en QML (utilisation
de <i>Q_INVOKABLE</i>) pour communiquer directement avec la base de donn<6E>es : ainsi, le bouton
'<i>Save</i>' de l'<27>cran ci-dessus enregistre le mod<6F>le en base de donn<6E>es depuis QML.
<br /><br />
<b>Autre remarque :</b> <a href="#manual_920">un plugin de l'application <b>QxEntityEditor</b>
permet de g<>n<EFBFBD>rer automatiquement le code des mod<6F>les pour la gestion des relations</a>. Il
est ainsi possible de travailler avec des <a
href="http://christophe-dumez.developpez.com/tutoriels/qt/qml/exposer-modeles-imbriques/"
target="_blank">mod<EFBFBD>les imbriqu<71>s</a> (pour plus de d<>tails sur la notion de mod<6F>les
imbriqu<71>s, <a
href="http://christophe-dumez.developpez.com/tutoriels/qt/qml/exposer-modeles-imbriques/"
target="_blank">rendez-vous sur ce tutoriel du site developpez.com</a>).
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_950">Int<EFBFBD>raction avec les vues
QtWidget</a></p>
<div class="manual_div_content">
Voici un exemple de cr<63>ation d'un mod<6F>le pour afficher/modifier les donn<6E>es de la table
'<i>author</i>' (voir le <a href="./tutorial.html" target="_blank">tutoriel <i>qxBlog</i></a>
pour la d<>finition de la classe '<i>author</i>') dans un <a
href="http://doc.qt.io/qt-5/qtableview.html" target="_blank">QTableView</a> (le code source
de cet exemple est disponible dans le projet de test <i>qxBlogModelView</i> pr<70>sent dans le
package QxOrm) :<br />
<br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre> <span class="comment">// Create a model and fetch all data from database
</span>qx<span class="operator">::</span>IxModel<span class="operator"> *</span> pModel<span class="operator"> =</span><span class="keyword"> new</span> qx<span class="operator">::</span>QxModel<span class="operator">&lt;</span>author<span class="operator">&gt;();</span>
pModel<span class="operator">-&gt;</span>qxFetchAll<span class="operator">();</span><span class="comment">
// Associate the model to a QTableView and display it
</span>QTableView tableView<span class="operator">;</span>
tableView<span class="operator">.</span>setModel<span class="operator">(</span>pModel<span class="operator">);</span>
tableView<span class="operator">.</span>show<span class="operator">();</span></pre>
</td>
</tr>
</tbody>
</table>
<br>
Ce qui donne le r<>sultat suivant <20> l'ex<65>cution :<br />
<br />
<img alt="qx_model_view_01" src="./resource/qx_model_view_01.png" border="0" /><br />
<br />
<b>Remarque :</b> Qt propose par d<>faut plusieurs vues <i>QtWidget</i> qui peuvent <20>tre mapp<70>es
sur un mod<6F>le, par exemple : <a href="http://doc.qt.io/qt-5/qlistview.html"
target="_blank">QListView</a>, <a href="http://doc.qt.io/qt-5/qtableview.html"
target="_blank">QTableView</a>, <a href="http://doc.qt.io/qt-5/qtreeview.html"
target="_blank">QTreeView</a>.
Il est <20>galement possible d'utiliser la classe <a
href="http://doc.qt.io/qt-4.8/qdatawidgetmapper.html" target="_blank">QDataWidgetMapper</a>
pour cr<63>er ses propres formulaires bas<61>s sur des mod<6F>les (<a
href="http://qt-quarterly.developpez.com/qq-21/widget-correspondance-donnees/"
target="_blank">un tutoriel est disponible sur le site developpez.com</a>).
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_960">Connexion d'un mod<6F>le au
module QxService</a></p>
<div class="manual_div_content">
Le module <a href="../doxygen/html/group___qx_model_view.html" target="_blank">QxModelView</a>
fournit la classe template : <a href="../doxygen/html/classqx_1_1_qx_model_service.html"
target="_blank">qx::QxModelService&lt;T, S&gt;</a> (qui h<>rite de : <a
href="../doxygen/html/classqx_1_1_qx_model.html" target="_blank">qx::QxModel&lt;T&gt;</a>
&gt;&gt; <a href="../doxygen/html/classqx_1_1_ix_model.html" target="_blank">qx::IxModel</a>
&gt;&gt; <a href="http://doc.qt.io/qt-5/qabstractitemmodel.html"
target="_blank">QAbstractItemModel</a>).
Cette classe dispose de 2 param<61>tres template :
<ul>
<li><i>T</i> : classe enregistr<74>e dans le contexte QxOrm dont toutes les propri<72>t<EFBFBD>s sont
expos<6F>es au <a href="http://doc.qt.io/qt-5/modelview.html" target="_blank">moteur
model/view de Qt</a> ;</li>
<li><i>S</i> : classe de services du <a href="#manual_80">module QxService</a> pour
acc<63>der/enregistrer les donn<6E>es du mod<6F>le (requ<71>tes client/serveur).</li>
</ul>
Les donn<6E>es propos<6F>es par le mod<6F>le sont ainsi issues de requ<71>tes client/serveur gr<67>ce au <a
href="#manual_80">module QxService</a> (elles ne proviennent pas directement de la base de
donn<6E>es).
La classe de services <i>S</i> doit proposer les m<>thodes suivantes :
<ul>
<li><i>count() :</i> requ<71>te client/serveur pour compter le nombre d'<27>l<EFBFBD>ments (avec
possibilit<69> d'utiliser un filtre SQL) ;</li>
<li><i>fetchById() :</i> requ<71>te client/serveur pour alimenter les propri<72>t<EFBFBD>s du mod<6F>le en
fonction de son identifiant ;</li>
<li><i>fetchAll() :</i> requ<71>te client/serveur pour alimenter les propri<72>t<EFBFBD>s du mod<6F>le qui
contiendra tous les <20>l<EFBFBD>ments d'une table ;</li>
<li><i>fetchByQuery() :</i> requ<71>te client/serveur pour alimenter les propri<72>t<EFBFBD>s du mod<6F>le
qui contiendra les <20>l<EFBFBD>ments filtr<74>s par une requ<71>te SQL ;</li>
<li><i>insert() :</i> requ<71>te client/serveur pour ins<6E>rer les donn<6E>es du mod<6F>le ;</li>
<li><i>update() :</i> requ<71>te client/serveur pour mettre <20> jour les donn<6E>es du mod<6F>le ;</li>
<li><i>save() :</i> requ<71>te client/serveur pour sauvegarder les donn<6E>es du mod<6F>le (insertion
ou mise <20> jour) ;</li>
<li><i>deleteById() :</i> requ<71>te client/serveur pour supprimer un mod<6F>le en fonction de son
identifiant ;</li>
<li><i>deleteAll() :</i> requ<71>te client/serveur pour supprimer tous les <20>l<EFBFBD>ments de la table
mapp<70>e au mod<6F>le ;</li>
<li><i>deleteByQuery() :</i> requ<71>te client/serveur pour supprimer tous les <20>l<EFBFBD>ments en
fonction d'une requ<71>te SQL ;</li>
<li><i>destroyById() :</i> requ<71>te client/serveur pour supprimer un mod<6F>le en fonction de son
identifiant (avec prise en compte de <a href="#manual_3400">la suppression logique</a>) ;
</li>
<li><i>destroyAll() :</i> requ<71>te client/serveur pour supprimer tous les <20>l<EFBFBD>ments de la table
mapp<70>e au mod<6F>le (avec prise en compte de <a href="#manual_3400">la suppression
logique</a>) ;</li>
<li><i>destroyByQuery() :</i> requ<71>te client/serveur pour supprimer tous les <20>l<EFBFBD>ments en
fonction d'une requ<71>te SQL (avec prise en compte de <a href="#manual_3400">la suppression
logique</a>) ;</li>
<li><i>executeQuery() :</i> requ<71>te client/serveur pour ex<65>cuter <a href="#manual_3610">une
requ<71>te SQL personnalis<69>e ou proc<6F>dure stock<63>e</a> ;</li>
<li><i>exist() :</i> requ<71>te client/serveur pour tester l'existence du mod<6F>le en fonction de
son identifiant ;</li>
<li><i>isValid() :</i> requ<71>te client/serveur pour tester la validit<69> du mod<6F>le (<a
href="#manual_420">module QxValidator</a>).</li>
</ul>
<br />
<b>Remarque :</b> l'application <b>QxEntityEditor</b> est livr<76>e avec les plugins
<i>QxEECppServicesExport</i> et <i>QxEECppModelViewExport</i> : ces plugins g<>n<EFBFBD>rent
automatiquement tout le code source n<>cessaire pour travailler avec des mod<6F>les qui utilisent le
<a href="#manual_80">module QxService</a>.
<b>Afin de travailler avec la classe <a href="../doxygen/html/classqx_1_1_qx_model_service.html"
target="_blank">qx::QxModelService&lt;T, S&gt;</a>, il est donc fortement recommand<6E>
d'utiliser l'application QxEntityEditor afin de g<>n<EFBFBD>rer le code source automatiquement.</b>
<br /><br />
</div>
</div>
<p class="manual_p_title_1"><a class="manual_a_title_1" name="manual_95">QxOrm et MongoDB (C++ ODM
Object Document Mapper)</a></p>
<div class="manual_div_content_1">
En plus des bases de donn<6E>es relationnelles classiques (MySQL, PostgreSQL, SQLite, Oracle,
Microsoft SQLServer, MariaDB, etc...), la biblioth<74>que QxOrm supporte <20>galement <a
href="https://www.mongodb.com/" target="_blank">la base de donn<6E>es MongoDB</a>.
<br /><br />
<a href="https://fr.wikipedia.org/wiki/MongoDB" target="_blank">D<EFBFBD>finition du site Wikipedia :</a>
MongoDB est un syst<73>me de gestion de base de donn<6E>es orient<6E>e documents, r<>partissable sur un
nombre quelconque d'ordinateurs et ne n<>cessitant pas de sch<63>ma pr<70>d<EFBFBD>fini des donn<6E>es.
Il fait partie de la mouvance <b>NoSQL</b>.
<br /><br />
La base de donn<6E>es MongoDB pr<70>sente de nombreux avantages par rapport <20> une base de donn<6E>es
relationnelle classique (liste non exhaustive) :
<ul>
<li><b>Aucun sch<63>ma <20> d<>finir</b> : il est inutile d'avoir <20> maintenir des tables et colonnes
(donc fini les scripts complexes pour migrer la base de donn<6E>es d'une version <20> une autre).
Les <i>Collections</i> MongoDB peuvent contenir des <i>Documents</i> avec diff<66>rents champs,
diff<66>rentes tailles, etc... Concernant QxOrm, <20>a signifie que vous pouvez faire <20>voluer vos
classes C++ sans vous soucier d'un sch<63>ma DDL <20> maintenir d'une version <20> une autre (convient
parfaitement dans <b>un environnement de d<>veloppement AGILE</b> par exemple) ;</li>
<li><b>Les donn<6E>es sont stock<63>es dans un format BSON (correspond <20> du JSON)</b> : facile <20> lire
m<>me avec des structures de donn<6E>es complexes ;</li>
<li><b>Moteur de requ<71>tes (JSON)</b> tr<74>s puissant avec possibilit<69> de positionner des index sur
n'importe qu'elle propri<72>t<EFBFBD> d'un <i>Document</i> ;</li>
<li>La base de donn<6E>es est <b>gratuite</b>, et propose un <b>support pour les professionnels</b>
;</li>
<li>Depuis la version 3.6 de MongoDB : le moteur de requ<71>tes permet de cr<63>er des
<b>jointures</b> (entre <i>Documents</i>) de mani<6E>re <20>quivalente <20> une base de donn<6E>es
relationnelle classique.
</li>
</ul>
<br />
L'utilisation de la biblioth<74>que QxOrm avec MongoDB est tr<74>s proche des bases de donn<6E>es
relationnelles classiques.
Toutes les fonctionnalit<69>s de la biblioth<74>que QxOrm sont support<72>es avec MongoDB : donc tout ce qui
se trouve dans ce manuel utilisateur est disponible avec une base de donn<6E>es MongoDB.
Les principales diff<66>rences <20> prendre en compte sont :
<ul>
<li>Il est conseill<6C> de d<>finir en C++ une cl<63> primaire de type QString. Il n'y a pas de notion
de cl<63> num<75>rique auto-incr<63>ment<6E>e : <a
href="https://docs.mongodb.com/manual/reference/method/ObjectId/" target="_blank">MongoDB
utilise un type ObjectId</a> qui peut <20>tre mapp<70> en C++ avec QString et g<>n<EFBFBD>r<EFBFBD>
automatiquement (il est <20>galement possible de d<>finir son propre type C++ pour mapper un
ObjectId MongoDB).</li>
<li><a href="#manual_992">Les requ<71>tes ne sont pas au format SQL mais au format JSON</a>.</li>
</ul>
<br />
<b>Remarque :</b> le package QxOrm fournit un projet de test nomm<6D> <b>qxBlogMongoDB</b> (dans le
dossier <i>./test/</i>).
Ce projet montre comment se connecter <20> une base de donn<6E>es MongoDB avec la biblioth<74>que QxOrm.
<br /><br />
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_980">Pr<EFBFBD>-requis : driver
<i>libmongoc</i> et <i>libbson</i></a></p>
<div class="manual_div_content">
Le module <a href="http://doc.qt.io/qt-5/qtsql-index.html" target="_blank">QtSql</a> fourni par
le framework Qt sur lequel est bas<61> la biblioth<74>que QxOrm ne propose pas de connecteur <20> une
base de donn<6E>es MongoDB.
La biblioth<74>que QxOrm a donc besoin de 2 d<>pendances suppl<70>mentaires pour se connecter <20> une
base MongoDB :
<ul>
<li><a href="http://mongoc.org/libmongoc/current/" target="_blank">MongoDB C Driver
(libmongoc)</a></li>
<li><a href="http://mongoc.org/libbson/current/index.html" target="_blank">Libbson</a> : a
cross-platform BSON library for C</li>
</ul>
<br />
<a href="http://mongoc.org/libmongoc/current/installing.html" target="_blank">Un guide
d'installation est disponible</a> pour installer ces 2 biblioth<74>ques sur votre environnement
de d<>veloppement.
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_981">Param<EFBFBD>trage du fichier
<i>QxOrm.pri</i> (ou <i>QxOrm.cmake</i>)</a></p>
<div class="manual_div_content">
Une fois que les biblioth<74>ques <i>libmongoc</i> et <i>libbson</i> sont correctement install<6C>es
sur votre environnement de d<>veloppement, il est n<>cessaire de param<61>trer le fichier de
configuration <i>QxOrm.pri</i> (ou <i>QxOrm.cmake</i>) en activant l'option de compilation
<b>_QX_ENABLE_MONGODB</b>.
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>#######################################
# MongoDB Driver Library Dependencies #
#######################################
# If you enable <font style="background-color:yellow">_QX_ENABLE_MONGODB</font> option, then QxOrm library will be able to use mongoc driver to store all QxOrm registered classes in a MongoDB database
# When _QX_ENABLE_MONGODB compilation option is defined, you must provide following paths to manage mongoc library dependencies :
# - a BSON_INCLUDE environment variable to define where bson library source code is located (or a QX_BSON_INCLUDE_PATH qmake variable)
# - a MONGOC_INCLUDE environment variable to define where mongoc library source code is located (or a QX_MONGOC_INCLUDE_PATH qmake variable)
# - a BSON_LIB environment variable to define where bson library is located (or a QX_BSON_LIB_PATH qmake variable)
# - a MONGOC_LIB environment variable to define where mongoc library is located (or a QX_MONGOC_LIB_PATH qmake variable)</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<b>Remarque :</b> une fois l'option de compilation <b>_QX_ENABLE_MONGODB</b> activ<69>e, il est
possible de compiler et ex<65>cuter le projet de test <b>qxBlogMongoDB</b> du dossier
<i>./test/</i> afin de valider l'environnement de d<>veloppement.
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_982">Connexion <20> la base de
donn<6E>es MongoDB</a></p>
<div class="manual_div_content">
Voici un exemple de param<61>trage pour se connecter <20> une base de donn<6E>es MongoDB :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Parameters to connect to MongoDB database</span>
qx::QxSqlDatabase * pDatabase = qx::QxSqlDatabase::getSingleton();
pDatabase->setDriverName(<font style="background-color:yellow"><span class="string">"QXMONGODB"</span></font>);
pDatabase->setDatabaseName(<span class="string">"qxBlog"</span>);
pDatabase->setHostName(<span class="string">"localhost"</span>);
pDatabase->setPort(<span class="int">27017</span>);
pDatabase->setUserName(<span class="string">""</span>);
pDatabase->setPassword(<span class="string">""</span>);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_983">D<EFBFBD>finition d'une classe
persistante MongoDB (Collection) dans le contexte QxOrm (mapping)</a></p>
<div class="manual_div_content">
D<>clarer une classe persistante MongoDB dans le contexte QxOrm est <20>quivalent <20> <a
href="#manual_300">d<EFBFBD>clarer une classe persistante pour une base de donn<6E>es relationnelle
classique</a>.
Voici un exemple du projet de test <i>qxBlogMongoDB</i> :
<br /><br />
Fichier <i>blog.h</i> :
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="pre">#ifndef _QX_BLOG_BLOG_H_
#define _QX_BLOG_BLOG_H_</span>
<span class="keyword">#include</span> <span class="string">"author.h"</span>
<span class="keyword">#include</span> <span class="string">"comment.h"</span>
<span class="keyword">#include</span> <span class="string">"category.h"</span>
<span class="keyword">class</span> QX_BLOG_DLL_EXPORT blog
{
<span class="keyword">public:</span>
<span class="comment">// -- properties</span>
<font style="background-color:yellow">QString m_id;</font>
QString m_text;
QDateTime m_dt_creation;
author_ptr m_author;
list_comment m_commentX;
list_category m_categoryX;
<span class="comment">// -- contructor, virtual destructor</span>
blog() { ; }
virtual ~blog() { ; }
};
<font style="background-color:yellow">QX_REGISTER_PRIMARY_KEY(blog, QString)</font>
QX_REGISTER_HPP_QX_BLOG(blog, qx::trait::no_base_class_defined, 0)
<span class="keyword">typedef</span> std::shared_ptr&lt;blog&gt; blog_ptr;
<span class="keyword">typedef</span> std::vector&lt;blog_ptr&gt; list_blog;
<span class="pre">#endif</span> <span class="comment">// _QX_BLOG_BLOG_H_</span></pre>
</td>
</tr>
</tbody>
</table>
<br />
Fichier <i>blog.cpp</i> :
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">#include</span> <span class="string">"../include/precompiled.h"</span>
<span class="keyword">#include</span> <span class="string">"../include/blog.h"</span>
<span class="keyword">#include</span> <span class="string">&lt;QxOrm_Impl.h&gt;</span>
QX_REGISTER_CPP_QX_BLOG(blog)
<span class="keyword">namespace</span> qx {
<span class="keyword">template</span> &lt;&gt; void register_class(QxClass&lt;blog&gt; & t)
{
t.id(& blog::m_id, <span class="string">"blog_id"</span>);
t.data(& blog::m_text, <span class="string">"blog_text"</span>);
t.data(& blog::m_dt_creation, <span class="string">"date_creation"</span>);
<font style="background-color:yellow">t.data(& blog::m_categoryX, <span class="string">"list_category"</span>); <span class="comment">// Embedded relationship</span></font>
<font style="background-color:yellow">t.relationManyToOne(& blog::m_author, <span class="string">"author_id"</span>); <span class="comment">// Referenced relationship</span></font>
<font style="background-color:yellow">t.relationOneToMany(& blog::m_commentX, <span class="string">"list_comment"</span>, <span class="string">"blog_id"</span>); <span class="comment">// Referenced relationship</span></font>
}}</pre>
</td>
</tr>
</tbody>
</table>
<br />
<b>Remarque :</b> l'exemple ci-dessus montre comment d<>finir :
<ul>
<li><a href="#manual_9830">la cl<63> primaire <i>QString</i> (MongoDB ObjectId)</a> ;</li>
<li><a href="#manual_9930">les relations Embedded vs Referenced</a>.</li>
</ul>
<br />
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9830">Gestion des cl<63>s
primaires ObjectId</a></p>
<div class="manual_div_content">
Comme indiqu<71> en jaune dans l'exemple pr<70>c<EFBFBD>dent, il est conseill<6C> de d<>finir en C++ une cl<63>
primaire de type QString.
Il n'y a pas de notion de cl<63> num<75>rique auto-incr<63>ment<6E>e : <a
href="https://docs.mongodb.com/manual/reference/method/ObjectId/" target="_blank">MongoDB
utilise un type ObjectId</a> qui peut <20>tre mapp<70> en C++ avec QString et g<>n<EFBFBD>r<EFBFBD>
automatiquement (il est <20>galement possible de d<>finir son propre type C++ pour mapper un
ObjectId MongoDB).
<br /><br />
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_984">Ins<EFBFBD>rer une instance C++
(Document) dans la base de donn<6E>es MongoDB (INSERT)</a></p>
<div class="manual_div_content">
Voici un exemple d'insertion de document avec g<>n<EFBFBD>ration automatique de la cl<63> primaire (de type
<a href="https://docs.mongodb.com/manual/reference/method/ObjectId/" target="_blank">MongoDB
ObjectId</a>) :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Insert one author without id</span>
author_ptr author_1 = std::make_shared&lt;author&gt;();
author_1->m_name = <span class="string">"author_1"</span>;
author_1->m_sex = author::male;
author_1->m_birthdate = QDate(1998, 07, 12);
daoError = qx::dao::insert(author_1);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
Voici un exemple d'insertion de document avec une cl<63> primaire personnalis<69>e :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Insert one author with a custom id</span>
author_ptr author_2 = std::make_shared&lt;author&gt;();
<font style="background-color:yellow">author_2->m_id = <span class="string">"my_custom_id_author_2"</span>;</font>
author_2->m_name = <span class="string">"author_2"</span>;
author_2->m_sex = author::female;
author_2->m_birthdate = QDate(2003, 02, 28);
daoError = qx::dao::insert(author_2);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9840">Ins<EFBFBD>rer une liste
d'instances C++ (plusieurs Documents) dans la base de donn<6E>es MongoDB (INSERT)</a></p>
<div class="manual_div_content">
Voici un exemple d'insertion de plusieurs documents dans la base de donn<6E>es MongoDB :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Insert many authors with/without ids</span>
QList&lt;author&gt; authorX;
author author_3; author_3.m_name = <span class="string">"author_3"</span>; author_3.m_sex = author::female; author_3.m_birthdate = QDate(1968, 05, 01);
author author_4; author_4.m_id = <span class="string">"my_custom_id_author_4"</span>; author_4.m_name = <span class="string">"author_4"</span>; author_4.m_sex = author::male;
author author_5; author_5.m_name = <span class="string">"author_5"</span>; author_5.m_sex = author::female; author_5.m_birthdate = QDate(1978, 03, 03);
authorX.append(author_3); authorX.append(author_4); authorX.append(author_5);
daoError = qx::dao::insert(authorX);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<b>Remarque :</b> <a href="#manual_390">QxOrm supporte plusieurs types C++ de listes /
collections</a>.
<br /><br />
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_986">Mettre <20> jour une
instance C++ (Document) dans la base de donn<6E>es MongoDB (UPDATE)</a></p>
<div class="manual_div_content">
Voici un exemple de mise <20> jour d'un document dans la base MongoDB :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Update one author</span>
author author_4;
author_4.m_id = <span class="string">"my_custom_id_author_4"</span>;
author_4.m_name = <span class="string">"author_4_modified"</span>;
daoError = qx::dao::update(author_4);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9860">Mettre <20> jour une
liste d'instances C++ (plusieurs Documents) dans la base de donn<6E>es MongoDB (UPDATE)</a>
</p>
<div class="manual_div_content">
Voici un exemple de mise <20> jour de plusieurs documents dans la base MongoDB :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Update many authors</span>
QList&lt;author&gt; authorX;
author_3.m_name = <span class="string">"author_3_modified_twice"</span>; authorX.append(author_3);
author_2->m_name = <span class="string">"author_2_modified"</span>; authorX.append(* author_2);
author_1->m_name = <span class="string">"author_1_modified"</span>; authorX.append(* author_1);
daoError = qx::dao::update(authorX);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<b>Remarque :</b> <a href="#manual_390">QxOrm supporte plusieurs types C++ de listes /
collections</a>.
<br /><br />
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_988">Supprimer une instance
C++ (Document) de la base de donn<6E>es MongoDB (DELETE)</a></p>
<div class="manual_div_content">
Voici un exemple de suppression d'un document de la base MongoDB :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Delete one author by id</span>
author_ptr pAuthor = std::make_shared&lt;author&gt;();
pAuthor->m_id = <span class="string">"my_custom_id_author_4"</span>;
daoError = qx::dao::delete_by_id(pAuthor);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9880">Supprimer une liste
d'instances C++ (plusieurs Documents) de la base de donn<6E>es MongoDB (DELETE)</a></p>
<div class="manual_div_content">
Voici un exemple de suppression de plusieurs documents de la base MongoDB par identifiant
(cl<63> primaire) :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Delete many authors by id</span>
QList&lt;author&gt; authorX;
author_3.m_id = <span class="string">"id_author_3"</span>; authorX.append(author_3);
author_2->m_id = <span class="string">"id_author_2"</span>; authorX.append(* author_2);
author_1->m_id = <span class="string">"id_author_1"</span>; authorX.append(* author_1);
daoError = qx::dao::delete_by_id(authorX);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
Voici un exemple de suppression de plusieurs documents de la base MongoDB par <a
href="#manual_992">requ<EFBFBD>te JSON</a> :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Delete authors by query (all male)</span>
qx_query query{ { <span class="string">"sex"</span>, author::male } };
daoError = qx::dao::delete_by_query&lt;author&gt;(query);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
Pour supprimer tous les documents de la Collection <i>author</i> :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Delete all authors</span>
daoError = qx::dao::delete_all&lt;author&gt;();</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<b>Remarque :</b> <a href="#manual_390">QxOrm supporte plusieurs types C++ de listes /
collections</a>.
<br /><br />
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_990">R<EFBFBD>cup<EFBFBD>rer une instance
C++ (Document) de la base de donn<6E>es MongoDB (FETCH)</a></p>
<div class="manual_div_content">
Voici un exemple pour r<>cup<75>rer (FETCH) un document de la base MongoDB par identifiant (cl<63>
primaire) :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Fetch one author by id</span>
author_ptr pAuthor = std::make_shared&lt;author&gt;();
pAuthor->m_id = <span class="string">"my_custom_id_author_2"</span>;
daoError = qx::dao::fetch_by_id(pAuthor);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9900">R<EFBFBD>cup<EFBFBD>rer une liste
d'instances C++ (plusieurs Documents) de la base de donn<6E>es MongoDB (FETCH)</a></p>
<div class="manual_div_content">
Voici un exemple pour r<>cup<75>rer (FETCH) plusieurs documents de la base MongoDB par
identifiant (cl<63> primaire) :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Fetch many authors by id</span>
QList&lt;author&gt; authorX;
author_3.m_id = <span class="string">"id_author_3"</span>; authorX.append(author_3);
author_2->m_id = <span class="string">"id_author_2"</span>; authorX.append(* author_2);
author_1->m_id = <span class="string">"id_author_1"</span>; authorX.append(* author_1);
daoError = qx::dao::fetch_by_id(authorX);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
Voici un exemple pour r<>cup<75>rer (FETCH) plusieurs documents de la base MongoDB par <a
href="#manual_992">requ<EFBFBD>te JSON</a> :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Fetch many authors by query (only female)</span>
list_author list_of_female_author;
qx_query query{ { <span class="string">"sex"</span>, author::female } };
daoError = qx::dao::fetch_by_query(query, list_of_female_author);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
Voici un exemple pour r<>cup<75>rer (FETCH) tous les documents de la Collection <i>author</i> de
la base MongoDB :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Fetch all authors</span>
list_author allAuthors;
daoError = qx::dao::fetch_all(allAuthors);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
Voici un exemple pour r<>cup<75>rer (FETCH) tous les documents de la Collection <i>author</i> de
la base MongoDB (en s<>lectionnant les propri<72>t<EFBFBD>s/colonnes <20> r<>cup<75>rer) :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Fetch all authors (with only 'date_creation' and 'name' properties)</span>
list_author allAuthors;
QStringList columns = QStringList() &lt;&lt; <span class="string">"date_creation"</span> &lt;&lt; <span class="string">"name"</span>;
daoError = qx::dao::fetch_all(allAuthors, NULL, columns);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<b>Remarque :</b> <a href="#manual_390">QxOrm supporte plusieurs types C++ de listes /
collections</a>.
<br /><br />
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_992">Requ<EFBFBD>tes JSON</a></p>
<div class="manual_div_content">
La principale diff<66>rence entre une base de donn<6E>es relationnelle classique et MongoDB est la
fa<66>on de requ<71>ter les donn<6E>es : <20> la place du SQL, MongoDB propose <a
href="https://docs.mongodb.com/manual/tutorial/query-documents/" target="_blank">un moteur de
requ<71>te JSON</a>.
<br /><br />
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9920">Utilisation de la
classe qx::QxSqlQuery (ou son alias qx_query)</a></p>
<div class="manual_div_content">
La classe <a href="#manual_3600">qx::QxSqlQuery (ou son alias qx_query)</a> utilis<69>e pour
construire du SQL classique est <20>galement compatible pour construire des requ<71>tes JSON
MongoDB.
Cette classe utilise <a href="https://fr.cppreference.com/w/cpp/utility/initializer_list"
target="_blank">la fonctionnalit<69> C++11 std::initializer_list</a> afin d'<27>crire les
requ<71>tes en C++ avec une syntaxe proche du JSON (il est <20>galement possible d'<27>crire la
requ<71>te sous forme de cha<68>ne de caract<63>res).
Par exemple :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Fetch many authors by query (only female)</span>
list_author list_of_female_author;
qx_query query <font style="background-color:yellow">{ { <span class="string">"sex"</span>, author::female } }</font>;
daoError = qx::dao::fetch_by_query(query, list_of_female_author);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9921">Utiliser le moteur
d'aggregation MongoDB</a></p>
<div class="manual_div_content">
La base de donn<6E>es MongoDB fournit <20>galement <a
href="https://docs.mongodb.com/manual/aggregation/" target="_blank">un puissant moteur
d'aggregation</a> qui <20>tend encore plus les possibilit<69>s pour requ<71>ter les donn<6E>es.
Voici comment utiliser ce moteur d'aggregation avec la classe <a
href="#manual_3600">qx::QxSqlQuery (ou son alias qx_query)</a>, le 1er param<61>tre du
constructeur doit <20>tre <20>gal <20> <i>aggregate</i> :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Fetch by query using MongoDB aggregation framework (only female)</span>
list_author list_of_female_author;
qx_query queryAggregate(<font style="background-color:yellow"><span class="string">"aggregate"</span></font>,
<span class="string">"[ { \"$match\" : { \"sex\" : " + QString::number(static_cast&lt;int&gt;(author::female)) + " } } ]"</span>);
daoError = qx::dao::fetch_by_query(queryAggregate, list_of_female_author);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9922">Ajouter des
propri<72>t<EFBFBD>s <20> la requ<71>te de type : 'sort', 'limit', 'skip', etc...</a></p>
<div class="manual_div_content">
Il est souvent n<>cessaire de limiter les donn<6E>es, ou bien de les trier par exemple.
Pour effectuer ces op<6F>rations, la base de donn<6E>es MongoDB utilise <a
href="https://docs.mongodb.com/manual/reference/method/db.collection.find/"
target="_blank">la notion de projection</a>.
Voici un exemple d'utilisation avec la classe <a href="#manual_3600">qx::QxSqlQuery (ou son
alias qx_query)</a>, avec une QStringList en constructeur (ou bien 2<>me param<61>tre <a
href="https://fr.cppreference.com/w/cpp/utility/initializer_list"
target="_blank">std::initializer_list</a>) :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Fetch by query (only female) adding 'sort', 'limit', 'skip', etc... commands (see second query QStringList parameter)</span>
list_of_female_author.clear();
qx_query queryOpts(QStringList() &lt;&lt; <span class="string">"{ \"sex\" : " + QString::number(static_cast<int>(author::female)) + " }"</span>
&lt;&lt; <font style="background-color:yellow"><span class="string">"{ \"sort\" : { \"sex\" : -1 }, \"limit\" : 2 }"</span></font>);
daoError = qx::dao::fetch_by_query(queryOpts, list_of_female_author);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9923">Ex<EFBFBD>cuter une requ<71>te
personnalis<69>e</a></p>
<div class="manual_div_content">
Il est possible d'ex<65>cuter une requ<71>te personnalis<69>e avec <a href="#manual_3610">la fonction
qx::dao::call_query()</a>.
Le r<>sultat de la requ<71>te peut <20>tre facilement convertie en <a
href="http://doc.qt.io/qt-5/qvariant.html" target="_blank">QVariantMap ou bien
QList&lt;QVariantMap&gt;</a> (si la requ<71>te retourne un curseur) afin de faciliter la
lecture des r<>sultats de la requ<71>te personnalis<69>e.
Voici quelques exemples d'appels de requ<71>tes personnalis<69>es :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Drop database</span>
qx_query dropDB(<span class="string">"{ \"dropDatabase\" : 1 }"</span>);
QSqlError daoError = qx::dao::call_query(dropDB);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Call a custom query and get JSON response as QVariantMap</span>
qx_query customQuery(<span class="string">"{ \"find\": \"author\", \"filter\": { } }"</span>);
daoError = qx::dao::call_query(customQuery); qAssert(! daoError.isValid());
QString responseCustomQuery = customQuery.response().toString();
QVariantMap responseCustomQueryAsJson;
qx::serialization::json::from_string(responseCustomQueryAsJson, responseCustomQuery);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Call a custom query with cursor and get JSON response as QList&lt;QVariantMap&gt;</span>
qx_query customQueryCursor(<font style="background-color:yellow"><span class="string">"cursor"</span></font>, <span class="string">"{ \"find\": \"author\", \"filter\": { } }"</span>);
daoError = qx::dao::call_query(customQueryCursor); qAssert(! daoError.isValid());
QString responseCustomQueryCursor = customQueryCursor.response().toString();
QList&lt;QVariantMap&gt; responseCustomQueryCursorAsJson;
qx::serialization::json::from_string(responseCustomQueryCursorAsJson, responseCustomQueryCursor);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_993">Moteur de relations
(n<>cessite une version MongoDB 3.6 ou +)</a></p>
<div class="manual_div_content">
<a href="#manual_380">Le moteur de relations de la biblioth<74>que QxOrm</a> est compatible avec la
base de donn<6E>es MongoDB (version 3.6 minimale).
QxOrm est donc capable de r<>cup<75>rer les donn<6E>es d'un Document sur plusieurs Collections en une
seule requ<71>te.
<br /><br />
Voici un exemple pour r<>cup<75>rer un Document et toutes ses relations sur 1 niveau de profondeur
(parent > enfants) :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Fetch blog with all relations : 'author', 'comment' and 'category' (MongoDB version 3.6+ is required for relationships)</span>
blog_ptr blog = std::make_shared&lt;blog&gt;();
blog->m_id = <span class="string">"id_blog_1"</span>;
daoError = qx::dao::fetch_by_id_with_all_relation(blog);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
Voici un exemple pour r<>cup<75>rer un Document et toutes ses relations sur 4 niveaux de profondeur
(utilisation de la syntaxe <i>*-&gt;*-&gt;*-&gt;*</i>) :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Fetch blog with many relations using "*-&gt;*-&gt;*-&gt;*" (4 levels of relationships)</span>
blog_ptr blog = std::make_shared&lt;blog&gt;();
blog->m_id = <span class="string">"id_blog_1"</span>;
daoError = qx::dao::fetch_by_id_with_relation(<span class="string">"*-&gt;*-&gt;*-&gt;*"</span>, blog);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
Voici un exemple pour r<>cup<75>rer un Document en s<>lectionnant les relations et propri<72>t<EFBFBD>s <20>
alimenter (utilisation de la syntaxe <i>{ &lt;col_1&gt;, &lt;col_2&gt;, etc... }</i>) :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Fetch relations defining fields to fetch with syntax { col_1, col_2, etc... }</span>
list_blog lstBlogComplexRelation;
QStringList relations = QStringList() &lt;&lt; <span class="string">"{ blog_text }"</span> &lt;&lt; <span class="string">"author_id { name, birthdate }"</span> &lt;&lt; <span class="string">"list_comment { comment_text } -&gt; blog_id -&gt; *"</span>;
daoError = qx::dao::fetch_all_with_relation(relations, lstBlogComplexRelation);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
Voici un exemple pour r<>cup<75>rer un Document en s<>lectionnant les relations et les propri<72>t<EFBFBD>s <20>
ne pas alimenter (utilisation de la syntaxe <i>-{ &lt;col_1&gt;, &lt;col_2&gt;, etc... }</i>) :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Fetch relations defining columns to remove before fetching with syntax -{ col_1, col_2, etc... }</span>
list_blog lstBlogComplexRelation2;
QStringList relations = QStringList() &lt;&lt; <span class="string">"-{ blog_text }"</span> &lt;&lt; <span class="string">"author_id -{ name, birthdate }"</span> &lt;&lt; <span class="string">"list_comment -{ comment_text } -&gt; blog_id -&gt; *"</span>;
daoError = qx::dao::fetch_all_with_relation(relations, lstBlogComplexRelation2);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9930">Relations : Embedded
vs Referenced</a></p>
<div class="manual_div_content">
Un des points forts de la base de donn<6E>es MongoDB est de pouvoir stocker des structures
complexes de donn<6E>es (on n'est pas limit<69> <20> une structure en tableau table/colonne des bases
de donn<6E>es relationnelles classiques).
Un Document MongoDB peut donc contenir un objet et plusieurs sous-objets (notion de
hi<68>rarchie dans la structure du Document).
Inclure un sous-objet dans un m<>me Document pr<70>sente des avantages (aucune jointure par
exemple, donc plus rapide <20> r<>cup<75>rer) et inconv<6E>nients (un m<>me objet peut <20>tre dupliqu<71>
plusieurs fois dans la base).
Il est donc important de r<>fl<66>chir sur la strat<61>gie <20> adopter pour stocker les donn<6E>es.
<br /><br />
La biblioth<74>que QxOrm supporte les 2 fa<66>ons de proc<6F>der :
<ul>
<li><b>Embedded relation</b> : le sous-objet est inclu dans le Document ;</li>
<li><b>Referenced relation</b> : cr<63><72> une jointure comme dans une base de donn<6E>es
relationnelle classique.</li>
</ul>
<br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">namespace</span> qx {
<span class="keyword">template</span> &lt;&gt; void register_class(QxClass&lt;blog&gt; & t)
{
t.id(& blog::m_id, <span class="string">"blog_id"</span>);
t.data(& blog::m_text, <span class="string">"blog_text"</span>);
t.data(& blog::m_dt_creation, <span class="string">"date_creation"</span>);
<font style="background-color:yellow">t.data(& blog::m_categoryX, <span class="string">"list_category"</span>); <span class="comment">// Embedded relationship</span></font>
<font style="background-color:yellow">t.relationManyToOne(& blog::m_author, <span class="string">"author_id"</span>); <span class="comment">// Referenced relationship</span></font>
<font style="background-color:yellow">t.relationOneToMany(& blog::m_commentX, <span class="string">"list_comment"</span>, <span class="string">"blog_id"</span>); <span class="comment">// Referenced relationship</span></font>
}}</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_994">Cr<EFBFBD>ation automatique des
index</a></p>
<div class="manual_div_content">
La biblioth<74>que QxOrm fournit une m<>thode pour g<>n<EFBFBD>rer automatiquement les index (cette fonction
peut <20>tre appel<65>e en d<>but de programme, par exemple dans le <i>main</i>) :
<ul>
<li>les index li<6C>s aux relations entre les Collections (pour optimiser les jointures) ;</li>
<li>les index d<>finis par la m<>thode <a
href="../doxygen/html/classqx_1_1_ix_data_member.html"
target="_blank">qx::IxDataMember::setIndex()</a> (dans la fonction
<i>qx::register_class()</i>).
</li>
</ul>
<br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// To optimize queries : create automatically indexes based on relationships and properties marked as 'index'</span>
daoError = qx::dao::mongodb::QxMongoDB_Helper::autoCreateIndexes(true);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
</div>
</div>
<p class="manual_p_title_1"><a class="manual_a_title_1" name="manual_96">Serveur web HTTP/HTTPS
(module QxHttpServer)</a></p>
<div class="manual_div_content_1">
La biblioth<74>que QxOrm fournit un serveur web compatible HTTP 1.1 autonome (aucune n<>cessit<69>
d'installer une application tierce comme <a href="https://httpd.apache.org/"
target="_blank">Apache</a> ou <a href="https://nginx.org/en/" target="_blank">Nginx</a>),
performant (multi-thread) et simple d'utilisation : il s'agit du <b>module QxHttpServer</b> (bas<61>
sur le <a href="#manual_80">module QxService</a>).
<br /><br />
Le <b>module QxHttpServer</b> supporte de nombreuses fonctionnalit<69>s :
<ul>
<li><a href="#manual_9961">Connexions s<>curis<69>es SSL/TLS</a></li>
<li><a href="#manual_997">Routage dynamique des URL (d<>finir les endpoints / dispatcher)</a>
</li>
<li><a href="#manual_998">Sessions (stockage par client c<>t<EFBFBD> serveur)</a></li>
<li><a href="#manual_999">Cookies</a></li>
<li><a href="#manual_851">Gestion des fichiers statiques</a></li>
<li><a href="#manual_852">Encodage de transfert en bloc (chunked responses)</a></li>
<li><a href="#manual_853">Requ<EFBFBD>tes par les API JSON (module QxRestApi)</a></li>
</ul>
Combin<69> avec le <a href="#manual_97">module QxRestApi</a> (qui propose une API JSON pour requ<71>ter
les donn<6E>es persistantes), le <b>module QxHttpServer</b> est particuli<6C>rement adapt<70> pour
d<>velopper des applications web modernes.
Par exemple, des applications web de type <a
href="https://en.wikipedia.org/wiki/Single-page_application" target="_blank">SPA (<i>Single-Page
Applications</i>)</a> avec les c<>l<EFBFBD>bres frameworks Javascript comme <a
href="https://angularjs.org/" target="_blank">AngularJS</a>, <a href="https://reactjs.org/"
target="_blank">React</a>, <a href="https://www.meteor.com/" target="_blank">Meteor.js</a>,
etc...
<br /><br />
<b>Remarque :</b> pour activer le module <b>QxHttpServer</b>, il faut d<>finir l'option de
compilation <font style="background-color:yellow"><b>_QX_ENABLE_QT_NETWORK</b></font> dans <a
href="#manual_220">le fichier de configuration <i>QxOrm.pri</i> (ou <i>QxOrm.cmake</i>)</a>.
Cette option de compilation ajoute une d<>pendance au binaire <a
href="http://doc.qt.io/qt-5/qtnetwork-index.html" target="_blank">QtNetwork</a> fourni avec la
biblioth<74>que Qt.
<br /><br />
<b>Autre remarque :</b> le package QxOrm est livr<76> avec <a href="#manual_972">le projet de test
<b><i>qxBlogRestApi</i></b></a>.
Ce projet de test est une application web avec de nombreux exemples pour requ<71>ter les donn<6E>es
persistantes depuis une page web (HTML et Javascript).
<br /><br />
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_995">Hello World !</a></p>
<div class="manual_div_content">
Voici le code source d'une application web bas<61>e sur le <b>module QxHttpServer</b> (cette
application renvoie <i>Hello World !</i> au navigateur web client) :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">#include</span> &lt;<span class="string">QtCore/qcoreapplication.h</span>&gt;
<span class="keyword">#include</span> &lt;<span class="string">QxOrm.h</span>&gt;
<span class="type">int</span> main(<span class="type">int</span> argc, <span class="type">char *</span> argv[])
{
<span class="type">QCoreApplication</span> app(argc, argv);
<span class="comment">// HTTP server settings</span>
<span class="type">qx::service::QxConnect *</span> serverSettings = qx::service::QxConnect::getSingleton();
serverSettings-&gt;setPort(<span class="int">9642</span>); <span class="comment">// HTTP server listening port</span>
serverSettings-&gt;setKeepAlive(<span class="int">5000</span>); <span class="comment">// Keep-alive connection with client during 5s, then socket is disconnected and thread becomes available for other clients</span>
serverSettings-&gt;setThreadCount(<span class="int">50</span>); <span class="comment">// Number of threads waiting for client's requests,</span>
<span class="comment">// which means also how many requests can be handled simultaneously (in parallel) by HTTP server</span>
<span class="comment">// Create a QxOrm HTTP server instance</span>
<span class="type">qx::QxHttpServer</span> httpServer;
<span class="comment">// Define all HTTP server routes (dispatcher) to handle requests</span>
<span class="comment">// Each callback is executed in a dedicated thread, so QxOrm HTTP server can handle several requests in parallel</span>
httpServer.dispatch(<span class="string">"GET"</span>, <span class="string">"/"</span>, [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
response.data() = <span class="string">"Hello World !"</span>;
});
<span class="comment">// Start HTTP server</span>
httpServer.startServer();
<span class="comment">// Start event loop</span>
return app.exec();
}</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<b>R<EFBFBD>sultat :</b> ouvrir un navigateur web (Chrome, Firefox, Safari, Internet Explorer, Opera,
etc...) et aller <20> l'adresse <u><b>http://localhost:9642/</b></u>, l'<27>cran suivant doit
apparaitre :
<br /><br />
<img alt="QxHttpServer Hello World !" src="./resource/qx_http_server_hello_world_01.png"
border="0">
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_996">Param<EFBFBD>trage du serveur
web HTTP</a></p>
<div class="manual_div_content">
Les param<61>tres du serveur web HTTP sont accessibles avec la classe singleton <a
href="../doxygen/html/classqx_1_1service_1_1_qx_connect.html"
target="_blank">qx::service::QxConnect</a> :
<ul>
<li><i>setPort()</i> : port d'<27>coute du serveur web (un serveur web classique <20>coute sur le
port 80 mais vous pouvez d<>finir une autre valeur) ;</li>
<li><i>setThreadCount()</i> : nombre de threads utilis<69>s par le serveur web pour traiter les
requ<71>tes HTTP (nombre de connexions clientes simultan<61>es) ;</li>
<li><i>setMaxWait()</i> : temps d'attente maximum en milli-secondes (par exemple lecture ou
<20>criture sur la socket), la valeur -1 signifie attendre ind<6E>finiment ;</li>
<li><i>setCompressData()</i> : si le client HTTP supporte la compression GZIP, alors les
r<>ponses de type texte (fichier HTML / Javascript / CSS, flux JSON, etc...) seront
compress<73>s au format GZIP ;</li>
<li><i>setKeepAlive()</i> : la socket avec le client reste connect<63>e pendant le laps de temps
d<>fini par cette fonction (en milli-secondes), la valeur -1 signifie ne jamais se
d<>connecter ;</li>
<li><i>setSessionTimeOut()</i> : indique le temps d'attente (en milli-secondes) avant de
d<>truire <a href="#manual_998">une session (stockage par client c<>t<EFBFBD> serveur)</a> non
utilis<69>e.</li>
</ul>
<br />
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9961">Connexions s<>curis<69>es
SSL/TLS</a></p>
<div class="manual_div_content">
La classe singleton <a href="../doxygen/html/classqx_1_1service_1_1_qx_connect.html"
target="_blank">qx::service::QxConnect</a> fournit <20>galement des param<61>tres pour g<>rer les
connexions s<>curis<69>es HTTPS (SSL et/ou TLS).<br />
Voici un exemple de param<61>trage de connexions s<>curis<69>es avec certificat serveur et autorit<69>
de certification (voir <a href="#manual_972">le projet de test
<b><i>qxBlogRestApi</i></b></a> pour tester ce code) :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="comment">// Certificates created with this tutorial : https://deliciousbrains.com/ssl-certificate-authority-for-local-https-development/</span>
QFile::copy(<span class="string">":/documents/cert_qxorm_ca.pem"</span>, appPath.filePath(<span class="string">"files/cert_qxorm_ca.pem"</span>));
QFile::copy(<span class="string">":/documents/cert_qxorm_server.crt"</span>, appPath.filePath(<span class="string">"files/cert_qxorm_server.crt"</span>));
QFile::copy(<span class="string">":/documents/cert_qxorm_server.key"</span>, appPath.filePath(<span class="string">"files/cert_qxorm_server.key"</span>));
<span class="type">QFile</span> fileCertCA(appPath.filePath(<span class="string">"files/cert_qxorm_ca.pem"</span>));
fileCertCA.open(QIODevice::ReadOnly);
<span class="type">QList&lt;QSslCertificate&gt;</span> certCA; certCA &lt;&lt; QSslCertificate(fileCertCA.readAll());
<span class="type">QFile</span> fileCertServerPublic(appPath.filePath(<span class="string">"files/cert_qxorm_server.crt"</span>));
fileCertServerPublic.open(QIODevice::ReadOnly);
<span class="type">QSslCertificate</span> certServerPublic(fileCertServerPublic.readAll());
<span class="type">QFile</span> fileCertServerPrivate(appPath.filePath(<span class="string">"files/cert_qxorm_server.key"</span>));
fileCertServerPrivate.open(QIODevice::ReadOnly);
<span class="type">QSslKey</span> certServerPrivate(fileCertServerPrivate.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "qxorm");
<span class="type">qx::service::QxConnect *</span> serverSettings = qx::service::QxConnect::getSingleton();
serverSettings-&gt;setSSLEnabled(true);
serverSettings-&gt;setSSLCACertificates(certCA);
serverSettings-&gt;setSSLLocalCertificate(certServerPublic);
serverSettings-&gt;setSSLPrivateKey(certServerPrivate);
</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<b>Remarque :</b> par d<>faut, toutes les erreurs SSL sont ignor<6F>es (erreurs souvent li<6C>es <20>
des probl<62>mes de certificats).
Pour adapter votre serveur web <20> vos normes de s<>curit<69>, vous pouvez utiliser les fonctions
suivantes (classe singleton <a href="../doxygen/html/classqx_1_1service_1_1_qx_connect.html"
target="_blank">qx::service::QxConnect</a>) :
<ul>
<li><i>setSSLIgnoreErrors(QList&lt;QSslError&gt; lst) :</i> liste des erreurs SSL <20>
ignorer (par d<>faut toutes les erreurs sont ignor<6F>es), voir <a
href="https://doc.qt.io/Qt-5/qsslerror.html" target="_blank">la classe Qt
QSslError</a> ;</li>
<li><i>setSSLPeerVerifyName(const QString & s) :</i> voir <a
href="https://doc.qt.io/qt-5/qsslsocket.html" target="_blank">la classe Qt
QSslSocket</a> pour plus de d<>tails ;</li>
<li><i>setSSLPeerVerifyMode(QSslSocket::PeerVerifyMode e) :</i> voir <a
href="https://doc.qt.io/qt-5/qsslsocket.html" target="_blank">la classe Qt
QSslSocket</a> pour plus de d<>tails ;</li>
<li><i>setSSLPeerVerifyDepth(int i) :</i> voir <a
href="https://doc.qt.io/qt-5/qsslsocket.html" target="_blank">la classe Qt
QSslSocket</a> pour plus de d<>tails ;</li>
<li><i>setSSLConfiguration(QSslConfiguration cfg) :</i> voir <a
href="https://doc.qt.io/qt-5/qsslconfiguration.html" target="_blank">la classe Qt
QSslConfiguration</a> pour plus de d<>tails ;</li>
<li><i>setSSLProtocol(QSsl::SslProtocol e) :</i> voir <a
href="https://doc.qt.io/qt-5/qssl.html#SslProtocol-enum" target="_blank">la liste
des protocoles SSL et TLS dans la documentation Qt</a>.</li>
</ul>
<br />
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_997">Routage des URL (d<>finir
les endpoints / dispatcher)</a></p>
<div class="manual_div_content">
Le <b>module QxHttpServer</b> fournit un moteur de routage des URL (<i>dispatcher</i>) pour
d<>finir les fonctions (ou lambda) <20> ex<65>cuter en fonction des param<61>tres de la requ<71>te HTTP
(m<>thode HTTP <i>GET, POST, DELETE, etc...</i> + URL demand<6E>e).<br />
Les fonctions (ou lambda) doivent avoir cette signature : <font style="background-color:yellow">
<b>void myRequestHandler(qx::QxHttpRequest & request, qx::QxHttpResponse & response);</b>
</font>
<br /><br />
La classe <a href="../doxygen/html/classqx_1_1_qx_http_server.html"
target="_blank"><i>qx::QxHttpServer</i></a> (ou son alias <a
href="../doxygen/html/classqx_1_1_qx_http_server.html"
target="_blank"><i>qx_http_server</i></a>) dispose des m<>thodes suivantes :
<ul>
<li><i>setCustomRequestHandler()</i> : d<>fini une fonction (ou lambda) ex<65>cut<75>e si aucune
autre fonction n'a <20>t<EFBFBD> trouv<75>e par le dispatcher ;</li>
<li><i>dispatch()</i> : le 1er param<61>tre correspond <20> la m<>thode HTTP (<i>GET, POST, DELETE,
etc...</i>), le 2<>me param<61>tre correspond <20> l'URL demand<6E>e (ou son pattern), le 3<>me
param<61>tre correpond <20> la fonction (ou lambda) <20> ex<65>cuter ;</li>
<li><i>beforeDispatching()</i> : fonction (ou lambda) ex<65>cut<75>e avant de traiter la requ<71>te
HTTP (peut <20>tre utile par exemple pour tracer des logs, ou bien mettre en place un syst<73>me
d'authentification) ;</li>
<li><i>afterDispatching()</i> : fonction (ou lambda) ex<65>cut<75>e apr<70>s le traitement de la
requ<71>te HTTP (peut <20>tre utile par exemple pour tracer des logs) ;</li>
<li><i>clearDispatcher()</i> : n<>ttoie toutes les r<>gles de routage (seule la fonction ou
lambda d<>finie par <i>setCustomRequestHandler()</i> sera ex<65>cut<75>e).</li>
</ul>
<b>Remarque :</b> le dispatcher est thread-safe, vous pouvez donc red<65>finir les r<>gles de
routage des URL m<>me si le serveur web est en cours d'ex<65>cution.
<br /><br />
<b>Autre remarque :</b> chaque fonction (ou lambda) est <font style="background-color:yellow">
<b>ex<EFBFBD>cut<EFBFBD>e dans son propre thread</b>
</font>.
Le serveur web HTTP fourni par la biblioth<74>que QxOrm peut donc g<>rer plusieurs requ<71>tes HTTP
simultan<61>ment.
<br /><br />
<b>Exemple n<>1 :</b> cette r<>gle de routage intercepte toutes les requ<71>tes de type <i>GET</i>
dont l'URL commence par <i>/files/</i>, et renvoie comme r<>ponse le contenu d'un fichier
statique stock<63> sur le serveur (<i>QDir::currentPath()</i> indique le r<>pertoire parent du
stockage des fichiers statiques, et <i>5000</i> correspond <20> la taille pour l'envoi des fichiers
par bloc / chunked response, ce dernier param<61>tre <20>tant optionnel) :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>httpServer.dispatch(<span class="string">"GET"</span>, <span class="string">"/files/*"</span>, [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
qx::QxHttpServer::buildResponseStaticFile(request, response, QDir::currentPath(), <span class="int">5000</span>);
});</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<b>Exemple n<>2 :</b> cette r<>gle de routage intercepte toutes les requ<71>tes de type <i>POST</i>
dont l'URL est <i>/qx</i>, et utilise le <a href="#manual_97">module QxRestApi</a> (qui propose
une API JSON pour requ<71>ter les donn<6E>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 (<i>Single-Page Applications</i>) avec les c<>l<EFBFBD>bres
frameworks Javascript comme <a href="https://angularjs.org/" target="_blank">AngularJS</a>, <a
href="https://reactjs.org/" target="_blank">React</a>, <a href="https://www.meteor.com/"
target="_blank">Meteor.js</a>, etc...
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>httpServer.dispatch(<span class="string">"POST"</span>, <span class="string">"/qx"</span>, [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
qx::QxHttpServer::buildResponseQxRestApi(request, response);
});</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<b>Exemple n<>3 :</b> cette r<>gle de routage intercepte toutes les requ<71>tes de type <i>GET</i>
dont l'URL est <i>/test_big_json</i>, et construit une r<>ponse de type JSON contenant un tableau
de 10000 <20>l<EFBFBD>ments :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>httpServer.dispatch(<span class="string">"GET"</span>, <span class="string">"/test_big_json"</span>, [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
<span class="comment">// To compare with this benchmark : https://blog.binaryspaceship.com/2017/cpp-rest-api-frameworks-benchmark/</span>
<span class="comment">// This is more a JSON benchmark than HTTP server benchmark (RapidJSON is faster than Qt QJson engine)</span>
QJsonArray arr; Q_UNUSED(request);
for (int i = 0; i &lt; 10000; ++i)
{
QJsonObject item;
item.insert(<span class="string">"id"</span>, QString::number(i));
item.insert(<span class="string">"name"</span>, QString("Hello World"));
item.insert(<span class="string">"type"</span>, QString("application"));
arr.append(item);
}
QJsonDocument doc(arr);
response.headers().insert(<span class="string">"Content-Type"</span>, <span class="string">"application/json; charset=utf-8"</span>);
response.data() = doc.toJson(QJsonDocument::Compact);
});</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<b>Remarque :</b> l'ordre dans lequel est appel<65> les diff<66>rentes fonctions <i>dispatch()</i> est
important.
Le 1er <20>l<EFBFBD>ment trouv<75> par le dispatcher correspondant <20> l'URL demand<6E>e est ex<65>cut<75> (et ignore
les <20>l<EFBFBD>ments suivants).
Il est donc indispensable de d<>finir en 1er les URL les plus sp<73>cifiques, jusqu'aux URL les plus
g<>n<EFBFBD>riques (par exemple, le pattern correspondant <20> toutes les URL est <i>/*</i>).
<br /><br />
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9962">Routage dynamique des
URL</a></p>
<div class="manual_div_content">
Le dispatcher du <b>module QxHttpServer</b> supporte <20>galement la notion de routage dynamique
des URL.<br />
Il est possible de d<>finir des variables au niveau de l'URL <20> router avec la syntaxe : <font
style="background-color:yellow"><i>&lt;var_name:var_type&gt;</i></font> (<i>var_type</i>
<20>tant optionnel, et peut prendre comme valeur : <i>int, long, float, double, string</i>).
<br /><br />
Le routage dynamique est particuli<6C>rement utile pour mettre en place une API REST.<br />
Par exemple, le pattern <i>/blog/&lt;blog_id:int&gt;</i> associ<63> <20> la m<>thode HTTP <i>GET</i>
peut <20>tre utilis<69> pour r<>cup<75>rer les donn<6E>es d'un blog en fonction de son identifiant de type
num<75>rique (<i>fetch_by_id</i>).
<br /><br />
Il faut voir l'URL comme une liste de segments dont le s<>parateur est le caract<63>re
<i>/</i>.<br />
Le dispatcher v<>rifie que chaque segment de l'URL demand<6E>e correspond au pattern utilis<69> afin
de valider une fonction (ou lambda) <20> ex<65>cuter.<br />
Pour r<>cup<75>rer les valeurs des param<61>tres de l'URL, il faut utiliser : <font
style="background-color:yellow"><i>request.dispatchParams().value("var_name")</i></font>
(retourne un <i>QVariant</i>).
<br /><br />
<b>Exemple :</b> la r<>gle de routage suivante intercepte toutes les requ<71>tes de type
<i>GET</i> dont l'URL commence <i>/params/</i>, suivi par un segment qui sera associ<63> <20> la
variable <i>var1</i>, suivi par un segment de type num<75>rique associ<63> <20> la variable
<i>var2</i>.
Elle construit une r<>ponse qui renvoie la valeur des 2 param<61>tres <i>var1</i> et <i>var2</i>.
Si le navigateur web appelle l'URL <i>/params/abc/123/</i> alors la fonction (ou lambda) sera
ex<65>cut<75>e, par contre si le navigateur web appelle l'URL <i>/params/abc/def/</i> alors la
fonction (ou lambda) ne sera pas ex<65>cut<75>e (car <i>def</i> n'est pas num<75>rique) et cherchera
un autre <20>l<EFBFBD>ment du dispatcher :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>httpServer.dispatch(<span class="string">"GET"</span>, <font style="background-color:yellow"><span class="string">"/params/&lt;var1&gt;/&lt;var2:int&gt;"</span></font>, [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
response.data() = <span class="string">"Test URL dispatch parameters :\r\n"</span>;
response.data() += <span class="string">" - var1 = "</span> + request.dispatchParams().value(<span class="string">"var1"</span>).toByteArray() + <span class="string">"\r\n"</span>;
response.data() += <span class="string">" - var2 = "</span> + request.dispatchParams().value(<span class="string">"var2"</span>).toByteArray() + <span class="string">"\r\n"</span>;
});</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<b>Remarque :</b> il est <20>galement possible de d<>finir une expression r<>guli<6C>re pour router
les URL avec la syntaxe : <font style="background-color:yellow">
<i>&lt;var_name:{my_reg_exp}&gt;</i>
</font>.
<br /><br />
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_856">R<EFBFBD>cup<EFBFBD>rer les param<61>tres
de la requ<71>te HTTP</a></p>
<div class="manual_div_content">
La classe <a href="../doxygen/html/classqx_1_1_qx_http_request.html"
target="_blank"><i>qx::QxHttpRequest</i></a> (ou son alias <a
href="../doxygen/html/classqx_1_1_qx_http_request.html"
target="_blank"><i>qx_http_request</i></a>) contient tous les param<61>tres d'appel de la
requ<71>te HTTP :
<ul>
<li><i>QUrl & url() :</i> URL demand<6E>e par le navigateur web client ;</li>
<li><i>QString & command() :</i> m<>thode HTTP utilis<69>e par le navigateur web client (<i>GET,
POST, PUT, DELETE, etc...</i>) ;</li>
<li><i>QString & version() :</i> version HTTP fournie par le navigateur web client (en
g<>n<EFBFBD>ral <i>HTTP/1.1</i>) ;</li>
<li><i>QByteArray & data() :</i> contenu de la requ<71>te HTTP ;</li>
<li><i>QByteArray header(const QByteArray & key) :</i> permet de r<>cup<75>rer la valeur d'un
en-t<>te HTTP fourni par le navigateur web client (par exemple :
<i>request.header("Accept-Encoding")</i>) ;
</li>
<li><i>QxHttpCookie cookie(const QByteArray & name) :</i> permet de r<>cup<75>rer la valeur d'un
<a href="#manual_999">cookie HTTP</a> fourni par le navigateur web client ;
</li>
<li><i>QString param(const QString & key) :</i> permet de r<>cup<75>rer la valeur d'un param<61>tre
de la requ<71>te HTTP (fourni soit dans l'URL, soit dans le contenu si l'en-t<>te HTTP
<i>'content-type'</i> est <i>'application/x-www-form-urlencoded'</i>) ;
</li>
<li><i>QHash&lt;QString, QVariant&gt; & dispatchParams() :</i> liste des param<61>tres
dynamiques de l'URL calcul<75>s par <a href="#manual_9962">le routage/dispatcher</a> ;</li>
<li><i>QString & sourceAddress() :</i> adresse IP du navigateur web client ;</li>
<li><i>long & sourcePort() :</i> port utilis<69> par le navigateur web client ;</li>
<li><i>QString guid() :</i> identifiant unique de la requ<71>te HTTP <20> usage interne uniquement
(peut <20>tre utilis<69> pour tracer des logs par exemple).</li>
</ul>
<br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_857">G<EFBFBD>n<EFBFBD>rer la r<>ponse
HTTP</a></p>
<div class="manual_div_content">
La classe <a href="../doxygen/html/classqx_1_1_qx_http_response.html"
target="_blank"><i>qx::QxHttpResponse</i></a> (ou son alias <a
href="../doxygen/html/classqx_1_1_qx_http_response.html"
target="_blank"><i>qx_http_response</i></a>) permet de g<>n<EFBFBD>rer la r<>ponse HTTP avec les
m<>thodes suivantes :
<ul>
<li><i>int & status() :</i> code retour de la r<>ponse HTTP (par d<>faut 200) ;</li>
<li><i>QByteArray & data() :</i> contenu de la r<>ponse HTTP ;</li>
<li><i>QByteArray header(const QByteArray & key) :</i> renseigne un en-t<>te HTTP <20> envoyer au
navigateur web client (par d<>faut, les en-t<>tes suivants sont cr<63><72>s : <i>Server</i>,
<i>Date</i>, <i>Content-Type</i> et <i>Connection</i>) ;
</li>
<li><i>QxHttpCookie cookie(const QByteArray & name) :</i> renseigne un <a
href="#manual_999">cookie HTTP</a> <20> envoyer au navigateur web client ;</li>
<li><i>qx_bool writeChunked(const QByteArray & data) :</i> permet d'envoyer le <a
href="#manual_852">contenu de la r<>ponse par bloc (chunked responses)</a>.</li>
</ul>
<br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_998">Sessions (stockage par
client c<>t<EFBFBD> serveur)</a></p>
<div class="manual_div_content">
Les sessions HTTP sont un m<>canisme c<>t<EFBFBD> serveur web permettant de stocker des donn<6E>es
sp<73>cifiques <20> un client.
Ces donn<6E>es sont accessibles pendant un laps de temps pour toutes les requ<71>tes envoy<6F>es par le
client.
Lors du 1er acc<63>s <20> une session pour un client, un <a href="#manual_999">cookie HTTP</a>
contenant un identifiant unique est g<>n<EFBFBD>r<EFBFBD> et associ<63> automatiquement <20> la r<>ponse HTTP.
Par la suite, toutes les requ<71>tes HTTP envoy<6F>es par le client contiendront automatiquement un <a
href="#manual_999">cookie HTTP</a> avec l'identifiant unique calcul<75> pr<70>c<EFBFBD>demment.
Lorsqu'une session n'est plus utilis<69>e pendant un certain laps de temps, alors elle est d<>truite
automatiquement.
<br /><br />
La classe <a href="../doxygen/html/classqx_1_1_qx_http_session.html"
target="_blank"><i>qx::QxHttpSession</i></a> (ou son alias <a
href="../doxygen/html/classqx_1_1_qx_http_session.html"
target="_blank"><i>qx_http_session</i></a>) repr<70>sente une session HTTP c<>t<EFBFBD> serveur.<br />
Une session est accessible avec le singleton <a
href="../doxygen/html/classqx_1_1_qx_http_session_manager.html"
target="_blank">qx::QxHttpSessionManager</a> :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>httpServer.dispatch(<span class="string">"GET"</span>, <span class="string">"/"</span>, [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
<span class="comment">// If this is the first time to access to session, then a cookie is created automatically and attached to the response</span>
<span class="comment">// Then each request sent by web browser will contain a cookie with the session id</span>
<span class="comment">// The session expires on server side after qx::service::QxConnect::setSessionTimeOut() milliseconds</span>
<font style="background-color:yellow">qx::QxHttpSession_ptr session = qx::QxHttpSessionManager::getSession(request, response);</font>
if (session) { session-&gt;set(<span class="string">"last_request_per_user"</span>, QDateTime::currentDateTime()); }
});</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<b>Remarque :</b> la classe <a href="../doxygen/html/classqx_1_1_qx_http_session.html"
target="_blank"><i>qx::QxHttpSession</i></a> contient une hash-map (<i>QHash&lt;QByteArray,
QVariant&gt;</i>) pouvant stocker n'importe quelle valeur.
<br /><br />
<b>Autre remarque :</b> la dur<75>e (en milli-secondes) pour supprimer une session non utilis<69>e est
param<61>tr<74>e par la m<>thode : <a href="../doxygen/html/classqx_1_1service_1_1_qx_connect.html"
target="_blank">qx::service::QxConnect::setSessionTimeOut()</a>.
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_999">Cookies</a></p>
<div class="manual_div_content">
Les <a href="https://en.wikipedia.org/wiki/HTTP_cookie" target="_blank">cookies HTTP</a> sont un
m<>canisme d'<27>change de donn<6E>es entre client HTTP et serveur HTTP.<br />
Les cookies sont utilis<69>s par exemple pour :
<ul>
<li><a href="#manual_998">g<EFBFBD>rer les sessions HTTP</a> ;</li>
<li>m<EFBFBD>moriser l'information sur l'utilisateur d'un site, dans le but de lui montrer un
contenu appropri<72> dans le futur. Par exemple, un serveur web peut envoyer un cookie
contenant le dernier nom d'utilisateur utilis<69> pour se connecter <20> ce site web, afin que
ce nom d'utilisateur puisse <20>tre pr<70>-rempli lors des prochaines visites ;</li>
<li><a href="https://en.wikipedia.org/wiki/HTTP_cookie" target="_blank">voir Wikipedia pour
d'autres cas d'utilisation</a>.</li>
</ul>
<br />
Les classes <a href="#manual_856">qx::QxHttpRequest</a> et <a
href="#manual_857">qx::QxHttpResponse</a> disposent des m<>thodes n<>cessaires pour lire les
cookies envoy<6F>s par le navigateur web client ou bien g<>n<EFBFBD>rer un cookie dans la r<>ponse HTTP.
Par exemple :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="type">qx::QxHttpCookie</span> cookie;
cookie.name = <span class="string">"my_http_cookie"</span>;
cookie.value = <span class="string">"my_value"</span>;
response.cookies().insert(cookie.name, cookie);</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_851">Gestion des fichiers
statiques</a></p>
<div class="manual_div_content">
La classe <a href="../doxygen/html/classqx_1_1_qx_http_server.html"
target="_blank"><i>qx::QxHttpServer</i></a> (ou son alias <a
href="../doxygen/html/classqx_1_1_qx_http_server.html"
target="_blank"><i>qx_http_server</i></a>) dispose d'une m<>thode statique permettant
d'envoyer au navigateur web client des fichiers stock<63>s sur le serveur (par exemple : fichiers
HTML, Javascript, CSS, images PNG, JPEG, vid<69>os, etc...).
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>httpServer.dispatch(<span class="string">"GET"</span>, <span class="string">"/files/*"</span>, [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
<font style="background-color:yellow">qx::QxHttpServer::buildResponseStaticFile(request, response, QDir::currentPath(), 5000);</font>
});</pre>
</td>
</tr>
</tbody>
</table>
<br />
<ul>
<li>Le 3<>me param<61>tre (dans l'exemple <i>QDir::currentPath()</i>) repr<70>sente le r<>pertoire
parent o<> sont stock<63>s les fichiers statiques sur le serveur web ;</li>
<li>Le 4<>me param<61>tre (dans l'exemple <i>5000</i>) est optionnel et correspond <20> la taille
pour <a href="#manual_852">l'envoi des fichiers par bloc (chunked response)</a>. Ce
param<61>tre peut <20>tre utile pour envoyer des fichiers volumineux (streaming).</li>
</ul>
<br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_852">Encodage de transfert en
bloc (chunked responses)</a></p>
<div class="manual_div_content">
<a href="https://en.wikipedia.org/wiki/Chunked_transfer_encoding" target="_blank">D<EFBFBD>finition du
site Wikipedia :</a> Chunked transfer encoding (ou Encodage de transfert en bloc) est un
m<>canisme de transfert de donn<6E>es de la version 1.1 du protocole Hypertext Transfer Protocol
(HTTP), qui permet <20> un serveur ou <20> un client de commencer <20> transmettre des donn<6E>es par blocs
sans avoir <20> conna<6E>tre <20> l'avance la taille totale des donn<6E>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<6F>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<6E>es de ce bloc.
Le transfert total d'un fichier encod<6F> par blocs se termine par un bloc final au contenu nul.
<br /><br />
L'introduction de l'encodage de transfert en bloc du protocole HTTP 1.1 a fourni un certain
nombre d'avantages :
<ul>
<li>Permettre <20> un serveur de maintenir une connexion HTTP persistante pour un contenu g<>n<EFBFBD>r<EFBFBD>
dynamiquement.</li>
<li>Permettre <20> l'exp<78>diteur d'envoyer des en-t<>tes suppl<70>mentaires apr<70>s le corps du
message. Sans l'encodage de transfert en bloc, l'exp<78>diteur devrait tamponner le contenu
jusqu'<27> ce qu'il soit compl<70>t<EFBFBD> afin de calculer une valeur et l'envoyer avant le contenu.
</li>
</ul>
<br />
La classe <a href="#manual_857">qx::QxHttpResponse</a> dispose de la m<>thode <i>qx_bool
writeChunked(const QByteArray & data)</i>.
Cette m<>thode permet d'envoyer la r<>ponse par bloc.
Elle est utilis<69>e par exemple pour <a href="#manual_851">envoyer des fichiers statiques
volumineux</a> :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">while</span> (! file.atEnd())
{
<span class="keyword">if</span> (! <font style="background-color:yellow">response.writeChunked(file.read(chunkedSize))</font>) { <span class="keyword">return</span>; }
}</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
<b>Remarque :</b> le 1er appel de la m<>thode <i>response.writeChunked()</i> 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
<i>response.writeChunked()</i> pour la 1<>re fois.
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_853">Requ<EFBFBD>tes par les API JSON
(module QxRestApi)</a></p>
<div class="manual_div_content">
Le <a href="#manual_97">module QxRestApi</a> propose une API JSON g<>n<EFBFBD>rique pour requ<71>ter les
donn<6E>es persistantes (op<6F>rations CRUD, requ<71>tes complexes avec plusieurs niveaux de relations,
possibilit<69> de d<>finir un format de sortie JSON, appels dynamiques <20> des fonctions natives C++,
validation d'instances, requ<71>tes personnalis<69>es <20> la base de donn<6E>es).
<br /><br />
Ce manuel utilisateur dispose d'un chapitre entier d<>di<64> au <a href="#manual_97">module
QxRestApi</a> : il contient notamment de nombreux exemples d'utilisation.
En combinant le <a href="#manual_97">module QxRestApi</a> et le <b>module QxHttpServer</b> :
vous avez tous les outils n<>cessaires pour d<>velopper des applications web modernes.
Par exemple, des applications web de type <a
href="https://en.wikipedia.org/wiki/Single-page_application" target="_blank">SPA
(<i>Single-Page Applications</i>)</a> avec les c<>l<EFBFBD>bres frameworks Javascript comme <a
href="https://angularjs.org/" target="_blank">AngularJS</a>, <a href="https://reactjs.org/"
target="_blank">React</a>, <a href="https://www.meteor.com/" target="_blank">Meteor.js</a>,
etc...
<br /><br />
<b>Remarque :</b> le package QxOrm contient un <a href="#manual_972">projet de test
<b><i>qxBlogRestApi</i></b></a>.
Ce projet pr<70>sente la cr<63>ation d'un serveur web HTTP avec QxOrm, et <20>galement l'<27>criture de la
partie cliente en HTML + Javascript (avec utilisation de <a href="https://jquery.com/"
target="_blank">jQuery</a>).
<br /><br />
Par exemple, voici la fonction Javascript utilis<69>e pour envoyer les requ<71>tes JSON (m<>thode POST)
depuis le client (navigateur web) vers le serveur web HTTP QxOrm (toutes les requ<71>tes sont
envoy<6F>es <20> la m<>me adresse <i>/qx</i>) :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">function</span> sendRequest(request) {
<font style="background-color:yellow">$.post(<span class="string">"/qx"</span>, request</font>, function(data, status, xhr) {
$(<span class="string">"#txtResponse"</span>).val(JSON.stringify(data, null, 3));
}, <span class="string">"json"</span>).fail(function(error) {
alert(<span class="string">"An error occurred sending request to QxOrm HTTP server : "</span> + error);
});
}</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
C<>t<EFBFBD> serveur, la r<>ception et le traitement de ces requ<71>tes est tr<74>s simple : la classe <a
href="../doxygen/html/classqx_1_1_qx_http_server.html"
target="_blank"><i>qx::QxHttpServer</i></a> (ou son alias <a
href="../doxygen/html/classqx_1_1_qx_http_server.html"
target="_blank"><i>qx_http_server</i></a>) dispose de la m<>thode statique
<b>qx::QxHttpServer::buildResponseQxRestApi()</b> :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre>httpServer.dispatch(<span class="string">"POST"</span>, <span class="string">"/qx"</span>, [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
<font style="background-color:yellow">qx::QxHttpServer::buildResponseQxRestApi(request, response);</font>
});</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
Voici un exemple de requ<71>te JSON envoy<6F>e par le navigateur web, elle r<>cup<75>re la liste des tous
les blogs de la base de donn<6E>es (<i>fetch_all</i>) :
<br /><br />
<div class="json_pretty">
<pre>{
"request_id": "2b393e4c-a00c-45dc-a279-e9d76f1c55cf",
"action": "fetch_all",
"entity": "blog"
}</pre>
</div>
<br /><br />
Voici la r<>ponse JSON renvoy<6F>e par le serveur web HTTP contenant la liste de blogs :
<br /><br />
<div class="json_pretty">
<pre>{
"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"
}</pre>
</div>
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_854">WebSocket</a></p>
<div class="manual_div_content">
<a href="https://en.wikipedia.org/wiki/WebSocket" target="_blank">D<EFBFBD>finition du site Wikipedia
:</a> le protocole WebSocket vise <20> d<>velopper un canal de communication full-duplex sur un
socket TCP pour les navigateurs et les serveurs web et permet :
<ul>
<li>la notification au client d'un changement d'<27>tat du serveur ;</li>
<li>l'envoi de donn<6E>es en mode <20> pousser <20> (m<>thode Push) du serveur vers le client (sans que
ce dernier ait <20> effectuer une requ<71>te).</li>
</ul>
<br />
La biblioth<74>que QxOrm est bas<61>e sur le framework Qt qui dispose d<>j<EFBFBD> d'une <a
href="https://doc.qt.io/qt-5/qtwebsockets-index.html" target="_blank">impl<EFBFBD>mentation
WebSocket</a>.<br />
La mise en place d'un serveur web avec les WebSockets Qt est tr<74>s simple : <a
href="https://doc.qt.io/qt-5/echoserver.html" target="_blank">il y a plusieurs exemples dans
la documentation Qt</a>.
<br /><br />
Il est donc tout <20> fait possible d'impl<70>menter un serveur web avec :
<ul>
<li>un port d'<27>coute d<>di<64> <20> toutes les connexions HTTP (utilisant le module
<b>QxHttpServer</b>) ;
</li>
<li>un autre port d'<27>coute d<>di<64> <20> toutes les connexions WebSockets (utilisant le module <a
href="https://doc.qt.io/qt-5/qtwebsockets-index.html" target="_blank">QtWebSockets</a>
fourni par Qt).</li>
</ul>
<br />
<b>Remarque :</b> une connexion WebSocket <20>tant g<>n<EFBFBD>ralement cr<63><72>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<62>me.
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_855">Performance (test<73> avec
Apache Benchmark)</a></p>
<div class="manual_div_content">
Voici les r<>sultats d'un test de performance r<>alis<69> avec les param<61>tres suivants :
<ul>
<li>Syst<EFBFBD>me d'exploitation : <i>Windows 2010 64bits</i> ;</li>
<li>Processeur : <i>Intel Core i7-6820HQ @ 2.70GHz</i> (laptop) ;</li>
<li>Version Qt : <i>5.1.1</i> (en mode release) ;</li>
<li>Version QxOrm : <i>1.4.6</i> (compil<69> en mode release avec Visual Studio 2012, avec les
param<61>tres par d<>faut, aucune optimisation particuli<6C>re) ;</li>
<li>Serveur web HTTP : <a href="#manual_972">projet de test <i>qxBlogRestApi</i></a> ;</li>
<li>Outil de test : <a href="https://httpd.apache.org/docs/2.4/programs/ab.html"
target="_blank">Apache Benchmark</a> ;</li>
<li>Simulation de 20000 requ<71>tes avec 50 clients connect<63>s simultan<61>ment : <i><b>ab -n 20000
-c 50 -k http://localhost:9642/params/abc/123</b></i></li>
</ul>
<br />
Le r<>sultat indique que le serveur web HTTP QxOrm peut g<>rer <font
style="background-color:yellow">plus de 12000 requ<71>tes par seconde</font> :
<br /><br />
<img alt="QxHttpServer performance" src="./resource/qx_http_server_apache_bench_test_01.png"
border="0">
<br /><br />
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_8550">Am<EFBFBD>liorer les
performances avec epoll dispatcher sous Linux</a></p>
<div class="manual_div_content">
Sous Linux, il est possible d'ameliorer significativement les performances du serveur web
HTTP en utilisant <a href="https://en.wikipedia.org/wiki/Epoll" target="_blank">le m<>canisme
epoll pour g<>rer les socket</a>.
Par d<>faut, le framework Qt utilise un autre m<>canisme (<i>select</i>) plus lent, mais donne
la possibilit<69> de d<>finir une autre gestion d'<27>v<EFBFBD>nements.
Plusieurs biblioth<74>ques existent, par exemple :
<ul>
<li><a href="https://github.com/sjinks/qt_eventdispatcher_epoll"
target="_blank">qt_eventdispatcher_epoll</a></li>
<li><a href="https://github.com/connectedtable/qeventdispatcher_epoll"
target="_blank">qeventdispatcher_epoll</a></li>
</ul>
<br />
La classe <a href="../doxygen/html/classqx_1_1_qx_http_server.html"
target="_blank"><i>qx::QxHttpServer</i></a> (ou son alias <a
href="../doxygen/html/classqx_1_1_qx_http_server.html"
target="_blank"><i>qx_http_server</i></a>) dispose de la m<>thode suivante pour d<>finir des
<20>v<EFBFBD>nements bas<61>s sur epoll (<28> appeler avant le d<>marrage du serveur web) :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre> httpServer.setEventDispatcher(new QEventDispatcherEpoll()); </pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
</div>
</div>
</div>
<p class="manual_p_title_1"><a class="manual_a_title_1" name="manual_97">API REST JSON (module
QxRestApi)</a></p>
<div class="manual_div_content_1">
Le <b>module QxRestApi</b> est une API JSON pour g<>rer (de fa<66>on g<>n<EFBFBD>rique) la couche de donn<6E>es
persistantes (base de donn<6E>es) ou <a href="#manual_961">appeler des fonctions natives C++</a>
(enregistr<74>es dans le contexte QxOrm).
Le <b>module QxRestApi</b> est bas<61> sur un m<>canisme requ<71>te/r<>ponse : envoi d'une requ<71>te au
format JSON et r<>ception d'une r<>ponse au format JSON.
Le <b>module QxRestApi</b> est particuli<6C>rement adapt<70> pour d<>velopper des <a
href="https://en.wikipedia.org/wiki/Representational_state_transfer" target="_blank">services
REST</a>.
<br /><br />
Le <b>module QxRestApi</b> supporte les fonctionnalit<69>s suivantes :
<ul>
<li>op<EFBFBD>rations CRUD ;</li>
<li>requ<EFBFBD>tes complexes avec plusieurs niveaux de relations ;</li>
<li>possibilit<EFBFBD> de d<>finir un format de sortie JSON ;</li>
<li>appels dynamiques <20> des fonctions natives C++ ;</li>
<li>validation d'instances ;</li>
<li>requ<EFBFBD>tes personnalis<69>es <20> la base de donn<6E>es ou proc<6F>dures stock<63>es.</li>
</ul>
<br />
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_971">Principe de
fonctionnement</a></p>
<div class="manual_div_content">
Le <b>module QxRestApi</b> est tr<74>s simple d'utilisation : la classe <a
href="../doxygen/html/classqx_1_1_qx_rest_api.html" target="_blank"><i>qx::QxRestApi</i></a>
permet d'utiliser les API JSON avec une seule m<>thode : <i><b>processRequest()</b></i>.<br />
<b>Pr<EFBFBD>requis :</b> pour pouvoir utiliser le <b>module QxRestApi</b>, les classes enregistr<74>es
dans le contexte QxOrm doivent <font style="background-color:yellow"><a
href="#manual_450">impl<EFBFBD>menter l'interface <b>qx::IxPersistable</b></a></font>.
<br /><br />
La structure d'une requ<71>te JSON est g<>n<EFBFBD>rique et contient les <20>l<EFBFBD>ments suivants :
<br /><br />
<div style="width: 1000px; max-height: 300px; overflow: auto; background-color: white;">
<pre>{
<span class="string">"request_id"</span> : <span class="comment">// [optional] unique identifier generated by client to associate response to request (if provided by caller, then the response will contain the same unique identifier)</span>
<span class="string">"action"</span> : <span class="comment">// [required] what is the action to execute on the server</span>
<span class="string">"entity"</span> : <span class="comment">// [optional or required depending on action] C++ class registered in QxOrm context</span>
<span class="string">"data"</span> : <span class="comment">// [optional or required depending on action] data in JSON format needed to execute action</span>
<span class="string">"columns"</span> : <span class="comment">// [optional] list of columns to fetch or update (if empty, means all columns)</span>
<span class="string">"relations"</span> : <span class="comment">// [optional] list of relationships to fetch or save</span>
<span class="string">"query"</span> : <span class="comment">// [optional or required depending on action] query to execute on database</span>
<span class="string">"output_format"</span> : <span class="comment">// [optional] output fields for the response (filter), if empty then response will contain all fields</span>
<span class="string">"fct"</span> : <span class="comment">// [required only with action 'call_entity_function'] used to call C++ native functions</span>
<span class="string">"save_mode"</span> : <span class="comment">// [optional] used only with action 'save' to define insert or update or check both insert/update</span>
}</pre>
</div>
<br /><br />
La r<>ponse JSON contient les <20>l<EFBFBD>ments suivants :
<br /><br />
<div style="width: 1000px; max-height: 300px; overflow: auto; background-color: white;">
<pre>{
<span class="string">"request_id"</span> : <span class="comment">// unique identifier generated by client's request (if any)</span>
<span class="string">"data"</span> : <span class="comment">// contain the response data</span>
<span class="string">"error"</span> : <span class="comment">// if an error occured, then contain a code and description of the error</span>
}</pre>
</div>
<br /><br />
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9710">Cas d'utilisation</a>
</p>
<div class="manual_div_content">
Plusieurs langages de programmation supporte le JSON nativement (Javascript, PHP, Python,
etc...).
Le <b>module QxRestApi</b> permet ainsi une interop<6F>rabilit<69> entre la biblioth<74>que QxOrm et
d'autres applications utilisant d'autres technologies (autre que C++/Qt par exemple).
<br /><br />
Le <b>module QxRestApi</b> peut <20>tre utilis<69> :
<ul>
<li>D<EFBFBD>veloppement d'un serveur web HTTP (avec le <a href="#manual_96">module
QxHttpServer</a> par exemple) ;</li>
<li>Publication de <a href="https://en.wikipedia.org/wiki/Representational_state_transfer"
target="_blank">web-services REST</a> ;</li>
<li>Acc<EFBFBD>s aux donn<6E>es persistantes depuis une application QML (voir le <a
href="#manual_972">projet de test qxBlogRestApi</a>) ;</li>
<li>Possibilit<EFBFBD> de scripter l'application C++ (avec un langage comme Python par exemple).
</li>
</ul>
<br />
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_972">Projet de test
qxBlogRestApi (QML et serveur web HTTP)</a></p>
<div class="manual_div_content">
Le package QxOrm est livr<76> avec un projet de test nomm<6D> <b>qxBlogRestApi</b> (dans le dossier
<i>./test/qxBlogRestApi/</i>).<br />
Ce projet de test montre 2 cas d'utilisation du <b>module QxRestApi</b> :
<ul>
<li>La 1<>re fen<65>tre correspond <20> <font style="background-color:yellow">une application QML
</font> qui utilise le moteur JS int<6E>gr<67> <20> QML pour requ<71>ter les donn<6E>es persistantes ou
appeler des fonctions natives C++ :
<br /><br /><img alt="QxHttpServer performance"
src="./resource/qx_blog_rest_api_qml_01.png" border="0"><br /><br /><br />
</li>
<li>La 2<>me fen<65>tre d<>marre <font style="background-color:yellow">un serveur web HTTP</font>
bas<61> sur le <a href="#manual_96">module QxHttpServer</a>, puis ouvre le navigateur web par
d<>faut <20> l'adresse correspondante (HTML + Javascript avec <a href="https://jquery.com/"
target="_blank">jQuery</a>) :
<br /><br /><img alt="QxHttpServer performance"
src="./resource/qx_blog_rest_api_http_01.png" border="0"><br />
</li>
</ul>
<br />
Ces 2 fen<65>tres sont d<>velopp<70>es avec 2 technologies diff<66>rentes (QML versus HTML + Javascript),
mais proposent exactement les m<>mes fonctionnalit<69>s :
<ul>
<li>En haut <20> gauche de l'<27>cran : zone permettant d'<27>crire la requ<71>te JSON <20> envoyer au
<b>module QxRestApi</b> ;
</li>
<li>Juste en dessous de la requ<71>te JSON : un bouton permettant d'envoyer la requ<71>te JSON au
<b>module QxRestApi</b> ;
</li>
<li>En bas <20> gauche de l'<27>cran : une liste d'exemples de requ<71>tes JSON pr<70>tes <20> <20>tre
ex<65>cut<75>es (un click dans cette liste alimente automatiquement la requ<71>te JSON <20> envoyer au
<b>module QxRestApi</b>) ;
</li>
<li>A droite de l'<27>cran : la r<>ponse JSON fournie par le <b>module QxRestApi</b> apr<70>s
traitement de la requ<71>te.</li>
</ul>
<br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_973">R<EFBFBD>cup<EFBFBD>ration de donn<6E>es
(fetch/count/exist)</a></p>
<div class="manual_div_content">
Ce chapitre d<>taille les diff<66>rentes m<>thodes pour r<>cup<75>rer les donn<6E>es issues de la base de
donn<6E>es :
<ul>
<li>R<EFBFBD>cup<EFBFBD>rer tous les <20>l<EFBFBD>ments d'une table et <20>ventuellement <a href="#manual_3840">les
relations associ<63>es</a> (<a href="#manual_9731">fetch_all</a>) ;</li>
<li>R<EFBFBD>cup<EFBFBD>rer les donn<6E>es d'un <20>l<EFBFBD>ment d'une table en fonction de son identifiant unique (<a
href="#manual_9732">fetch_by_id</a>) ;</li>
<li>R<EFBFBD>cup<EFBFBD>rer les <20>l<EFBFBD>ments d'une table <a href="#manual_360">filtr<EFBFBD>s par une requ<71>te</a> (<a
href="#manual_9733">fetch_by_query</a>) ;</li>
<li>Compter les <20>l<EFBFBD>ments d'une table avec ou sans requ<71>te (<a href="#manual_9734">count</a>)
;</li>
<li>Tester l'existence d'un ou plusieurs <20>l<EFBFBD>ments d'une table en fonction de l'identifiant
(<a href="#manual_9735">exist</a>).</li>
</ul>
<br />
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9731">fetch_all</a></p>
<div class="manual_div_content">
L'action <b><i>fetch_all</i></b> permet de r<>cup<75>rer tous les <20>l<EFBFBD>ments d'une table de la base
de donn<6E>es (et <20>ventuellement <a href="#manual_3840">les relations associ<63>es sur plusieurs
niveaux</a>).
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>1 --</b></font> r<>cup<75>rer tous les
blogs (sous forme de liste) :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "5e988bac-c812-4cb1-b0d8-6a2c9dc4478b",
"action": "fetch_all",
"entity": "blog"
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>2 --</b></font> r<>cup<75>rer tous les
blogs (sous forme de collection type <i>hash-map</i> avec cl<63>/valeur) :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "ad400135-19fd-40e0-8034-201be6a2ff7a",
"action": "fetch_all",
"entity": "blog",
"data": [
{
"key": "",
"value": ""
}
]
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>3 --</b></font> r<>cup<75>rer tous les
blogs et <a href="#manual_3840">toutes les relations associ<63>es sur 2 niveaux</a> :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "cf9ea2a8-3e41-438f-9a48-bbc8593d2b99",
"action": "fetch_all",
"entity": "blog",
"relations": [
"*-&gt;*"
]
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>4 --</b></font> r<>cup<75>rer tous les
blogs et <a href="#manual_3840">plusieurs relations associ<63>es</a> en d<>finissant un format de
sortie (toutes les propri<72>t<EFBFBD>s ne feront pas partie de la r<>ponse JSON) :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "4c45fdf9-8001-4509-bb4b-ce27a4a8708a",
"action": "fetch_all",
"entity": "blog",
"relations": [
"&lt;blog_alias&gt; { blog_text }",
"author_id &lt;author_alias&gt; { name, birthdate }",
"list_comment &lt;list_comment_alias&gt; { comment_text } -&gt; blog_id &lt;blog_alias_2&gt; -&gt; * &lt;..._my_alias_suffix&gt;"
],
"output_format": [
"{ blog_text }",
"author_id { name, birthdate }",
"list_comment { comment_text } -&gt; blog_id -&gt; *"
]
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}</pre>
</div>
<br /><br />
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9732">fetch_by_id</a></p>
<div class="manual_div_content">
L'action <b><i>fetch_by_id</i></b> permet de r<>cup<75>rer les donn<6E>es d'un <20>l<EFBFBD>ment d'une table
en fonction de son identifiant unique.
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>1 --</b></font> r<>cup<75>rer les donn<6E>es
du blog qui a pour identifiant unique 1 :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "4d6fbb9e-e088-482a-abfa-4e7ddee80569",
"action": "fetch_by_id",
"entity": "blog",
"data": {
"blog_id": 1
}
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>2 --</b></font> r<>cup<75>re uniquement
quelques donn<6E>es du blog qui a pour identifiant unique 1 (les autres donn<6E>es font partie du
JSON mais avec une valeur vide ou null) :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "72c9b362-d194-410e-98ed-23797a34318e",
"action": "fetch_by_id",
"entity": "blog",
"data": {
"blog_id": 1
},
"columns": [
"blog_text",
"date_creation"
]
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>3 --</b></font> r<>cup<75>re une liste de
blogs en fonction de leur identifiant :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "59c37f70-26ee-42e5-9177-b32c331adce1",
"action": "fetch_by_id",
"entity": "blog",
"data": [
{
"blog_id": 1
},
{
"blog_id": 2
},
{
"blog_id": 3
}
]
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>4 --</b></font> r<>cup<75>re une liste de
blogs (avec quelques relations associ<63>es) en fonction de leur identifiant, et d<>fini un
format de sortie (toutes les propri<72>t<EFBFBD>s ne feront pas partie de la r<>ponse JSON) :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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 &lt;author_alias&gt; { name, birthdate }",
"list_comment &lt;list_comment_alias&gt; { comment_text }"
],
"output_format": [
"{ blog_text }",
"author_id { name, birthdate }",
"list_comment { comment_text }"
]
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}</pre>
</div>
<br /><br />
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9733">fetch_by_query</a>
</p>
<div class="manual_div_content">
L'action <b><i>fetch_by_query</i></b> permet de r<>cup<75>rer les <20>l<EFBFBD>ments d'une table <a
href="#manual_360">filtr<EFBFBD>s par une requ<71>te</a>.
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>1 --</b></font> r<>cup<75>re uniquement les
<20>l<EFBFBD>ments de la table <i>author</i> dont le sexe est de type <i>female</i> (<i>female</i> ==
enum dont la valeur est 1) :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "c178194c-a76f-4a77-af12-2b97fc7078e4",
"action": "fetch_by_query",
"entity": "author",
"query": {
"sql": "WHERE author.sex = :sex",
"params": [
{
"key": ":sex",
"value": 1
}
]
}
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>2 --</b></font> r<>cup<75>re uniquement les
<20>l<EFBFBD>ments de la table <i>author</i> (et toutes ses <a href="#manual_3840">relations
associ<63>es</a>) dont le sexe est de type <i>female</i> :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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": [
"*"
]
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>3 --</b></font> r<>cup<75>re uniquement les
<20>l<EFBFBD>ments de la table <i>author</i> (et toutes ses <a href="#manual_3840">relations
associ<63>es</a>) dont le sexe est de type <i>female</i>, et d<>fini un format de sortie
(toutes les propri<72>t<EFBFBD>s ne feront pas partie de la r<>ponse JSON) :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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 }"
]
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}</pre>
</div>
<br /><br />
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9734">count</a></p>
<div class="manual_div_content">
L'action <b><i>count</i></b> permet de compter les <20>l<EFBFBD>ments d'une table avec ou sans requ<71>te
(et avec ou sans relation).
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>1 --</b></font> compter le nombre de
blogs dans la base de donn<6E>es :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "1ef62fd7-d847-4d67-9fd0-0207af463aa4",
"action": "count",
"entity": "blog"
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"count": 4
},
"request_id": "1ef62fd7-d847-4d67-9fd0-0207af463aa4"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>2 --</b></font> compter tous les
<i>author</i> dont le sexe est de type <i>female</i> :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "a80646d1-5a42-46fb-9306-3b91c7f594c8",
"action": "count",
"entity": "author",
"query": {
"sql": "WHERE author.sex = :sex",
"params": [
{
"key": ":sex",
"value": 1
}
]
}
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"count": 2
},
"request_id": "a80646d1-5a42-46fb-9306-3b91c7f594c8"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>3 --</b></font> compter tous les blogs
dont l'<i>author</i> est de type <i>female</i> :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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 &lt;author_alias&gt; { sex }"
]
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"count": 4
},
"request_id": "6ef252f7-385c-465e-8304-b9afa9fea490"
}</pre>
</div>
<br /><br />
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9735">exist</a></p>
<div class="manual_div_content">
L'action <b><i>exist</i></b> permet de tester l'existence d'un ou plusieurs <20>l<EFBFBD>ments d'une
table en fonction de l'identifiant.
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>1 --</b></font> tester l'existence d'un
blog dont l'identifiant unique a pour valeur 1 :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "e8db33db-b249-4349-93fe-ad12e208520e",
"action": "exist",
"entity": "blog",
"data": {
"blog_id": 1
}
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"exist": true
},
"request_id": "e8db33db-b249-4349-93fe-ad12e208520e"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>2 --</b></font> tester l'existence de
plusieurs blogs :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "f2d6ca3f-36de-4920-8f4c-c04842603467",
"action": "exist",
"entity": "blog",
"data": [
{
"blog_id": 1
},
{
"blog_id": 999
},
{
"blog_id": 3
}
]
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"exist": false
},
"request_id": "f2d6ca3f-36de-4920-8f4c-c04842603467"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>3 --</b></font> tester l'existence d'un
<i>author</i> :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "2c7df172-8010-4816-b8e1-3edbb0b0b90e",
"action": "exist",
"entity": "author",
"data": {
"author_id": "author_id_2"
}
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"exist": true
},
"request_id": "2c7df172-8010-4816-b8e1-3edbb0b0b90e"
}</pre>
</div>
<br /><br />
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_974">Ajout de donn<6E>es
(insert)</a></p>
<div class="manual_div_content">
L'action <b><i>insert</i></b> permet d'ins<6E>rer un ou plusieurs <20>l<EFBFBD>ments dans la base de donn<6E>es.
Les identifiants uniques g<>n<EFBFBD>r<EFBFBD>s par la base de donn<6E>es (par exemple identifiant
auto-incr<63>ment<6E>) sont fournis dans la r<>ponse JSON.
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>1 --</b></font> ins<6E>rer un blog dans la
base de donn<6E>es :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"blog_id": 5
},
"request_id": "573e4940-607a-4037-8a09-11ec52deb21c"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>2 --</b></font> ins<6E>rer une liste de blogs
dans la base de donn<6E>es :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}
]
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": [
{
"blog_id": 6
},
{
"blog_id": 7
}
],
"request_id": "6ade2d01-086c-45d6-971b-b65e8836475f"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>3 --</b></font> ins<6E>rer un <i>author</i>
dans la base de donn<6E>es :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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
}
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"author_id": "author_id_from_rest_api"
},
"request_id": "0cffa916-99f4-4395-bccd-02918a4b3c57"
}</pre>
</div>
<br />
<b>Remarque :</b> l'identifiant unique de la table <i>author</i> doit <20>tre fourni par l'appelant
(non auto-incr<63>ment<6E>).
Si on rejoue une 2<>me fois la m<>me requ<71>te, on obtient l'erreur suivante :
<br />
<div class="json_pretty">
<pre>{
"error": {
"code": 19,
"desc": "Unable to fetch row\ncolumn author_id is not unique"
},
"request_id": "0cffa916-99f4-4395-bccd-02918a4b3c57"
}</pre>
</div>
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_975">Mise <20> jour de donn<6E>es
(update)</a></p>
<div class="manual_div_content">
L'action <b><i>update</i></b> permet une mise <20> jour de un ou plusieurs <20>l<EFBFBD>ments dans la base de
donn<6E>es.
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>1 --</b></font> mise <20> jour du blog avec
pour identifiant unique la valeur 1 :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"blog_id": 1
},
"request_id": "4fa24a7f-a3d8-4bbf-85c1-c86df83dec0b"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>2 --</b></font> mise <20> jour uniquement de
certaines colonnes d'un blog :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
]
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"blog_id": 2
},
"request_id": "d0704db1-5c3a-48ad-b27e-14aa54ac0efb"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>3 --</b></font> mise <20> jour de plusieurs
<i>author</i> :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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
}
]
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": [
{
"author_id": "author_id_from_rest_api"
},
{
"author_id": "author_id_1"
}
],
"request_id": "26ec3a7b-cf2d-47f7-bab7-db303f15ee51"
}</pre>
</div>
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_976">Sauvegarde de donn<6E>es
(save)</a></p>
<div class="manual_div_content">
L'action <b><i>save</i></b> permet d'ins<6E>rer ou mettre <20> jour (<i>insert</i> ou <i>update</i>)
un ou plusieurs <20>l<EFBFBD>ments dans la base de donn<6E>es.
En cas d'insertion, les identifiants uniques g<>n<EFBFBD>r<EFBFBD>s par la base de donn<6E>es (par exemple
identifiant auto-incr<63>ment<6E>) sont fournis dans la r<>ponse JSON.
<br /><br />
La requ<71>te JSON dispose d'un param<61>tre optionnel nomm<6D> <i>save_mode</i> qui peut prendre les
valeurs suivantes :
<ul>
<li><i>check_insert_or_update :</i> sauvegarde l'instance et les relations de fa<66>on r<>cursive
(sur plusieurs niveaux) en v<>rifiant pour chaque relation s'il faut faire un <i>insert</i>
ou <i>update</i> (m<>thode pouvant <20>tre lente si beaucoup de relations <20> traiter) ;</li>
<li><i>insert_only :</i> ins<6E>re de fa<66>on r<>cursive (sur plusieurs niveaux) l'instance et
toutes les relations associ<63>es ;</li>
<li><i>update_only :</i> met <20> jour de fa<66>on r<>cursive (sur plusieurs niveaux) l'instance et
toutes les relations associ<63>es.</li>
</ul>
<br />
<font style="background-color:yellow"><b>-- Exemple n<>1 --</b></font> sauvegarde (ins<6E>re ou met
<20> jour suivant l'identifant unique) un blog dans la base de donn<6E>es :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"blog_id": 1
},
"request_id": "ec3c71eb-5014-4b36-85a0-aeb7ae48a5e9"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>2 --</b></font> sauvegarde (ins<6E>re ou met
<20> jour suivant l'identifant unique) une liste de blogs dans la base de donn<6E>es :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}
]
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": [
{
"blog_id": 1
},
{
"blog_id": 5
}
],
"request_id": "dc7c804e-f95a-4a9b-a4e3-547adcacf090"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>3 --</b></font> sauvegarde (ins<6E>re ou met
<20> jour suivant l'identifant unique) un blog et toutes ses relations sur plusieurs niveaux (de
fa<66>on r<>cursive) :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"blog_id": 1
},
"request_id": "5b78e468-2fa3-4aeb-82ce-4d85408f5fa7"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>4 --</b></font> ins<6E>re (<i>save_mode</i> =
<i>insert_only</i>) un blog et toutes ses relations sur plusieurs niveaux (de fa<66>on r<>cursive) :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"blog_id": 7
},
"request_id": "ef147c62-74e0-4be2-a294-ffeb020d5304"
}</pre>
</div>
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_977">Suppression de donn<6E>es
(delete)</a></p>
<div class="manual_div_content">
Ce chapitre d<>taille les diff<66>rentes m<>thodes pour supprimer des <20>l<EFBFBD>ments de la base de donn<6E>es
:
<ul>
<li>Supprimer tous les <20>l<EFBFBD>ments d'une table (<a href="#manual_9771">delete_all /
destroy_all</a>) ;</li>
<li>Supprimer les <20>l<EFBFBD>ments d'une table en fonction d'une requ<71>te (<a
href="#manual_9772">delete_by_query / destroy_by_query</a>) ;</li>
<li>Supprimer les <20>l<EFBFBD>ments d'une table en fonction de leur identifiant unique (<a
href="#manual_9773">delete_by_id / destroy_by_id</a>).</li>
</ul>
<br />
<b>Remarque :</b> la diff<66>rence entre <i>delete</i> et <i>destroy</i> est li<6C>e <20> la <a
href="#manual_3400">suppression logique (soft delete)</a> d'un <20>l<EFBFBD>ment.
<br /><br />
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9771">delete_all /
destroy_all</a></p>
<div class="manual_div_content">
Les actions <b><i>delete_all</i></b> et <b><i>destroy_all</i></b> permettent de supprimer
tous les <20>l<EFBFBD>ments d'une table.
La diff<66>rence entre <i>delete</i> et <i>destroy</i> est li<6C>e <20> la <a
href="#manual_3400">suppression logique (soft delete)</a> d'un <20>l<EFBFBD>ment.
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>1 --</b></font> supprime tous les
<20>l<EFBFBD>ments de la table <i>comment</i> :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "7b06b5c0-409f-4e0d-bfc4-acafbfe7e796",
"action": "delete_all",
"entity": "comment"
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"deleted": true
},
"request_id": "7b06b5c0-409f-4e0d-bfc4-acafbfe7e796"
}</pre>
</div>
<br /><br />
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9772">delete_by_query /
destroy_by_query</a></p>
<div class="manual_div_content">
Les actions <b><i>delete_by_query</i></b> et <b><i>destroy_by_query</i></b> permettent de
supprimer les <20>l<EFBFBD>ments d'une table en fonction d'une requ<71>te.
La diff<66>rence entre <i>delete</i> et <i>destroy</i> est li<6C>e <20> la <a
href="#manual_3400">suppression logique (soft delete)</a> d'un <20>l<EFBFBD>ment.
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>1 --</b></font> supprime les <20>l<EFBFBD>ments
de la table <i>author</i> qui ont un sexe de type <i>female</i> (<i>female</i> = enum de
valeur 1) :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "169ff0be-6e49-457b-a99c-22bd7141dc02",
"action": "delete_by_query",
"entity": "author",
"query": {
"sql": "WHERE author.sex = :sex",
"params": [
{
"key": ":sex",
"value": 1
}
]
}
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"deleted": true
},
"request_id": "169ff0be-6e49-457b-a99c-22bd7141dc02"
}</pre>
</div>
<br /><br />
</div>
<p class="manual_p_title_3"><a class="manual_a_title_3" name="manual_9773">delete_by_id /
destroy_by_id</a></p>
<div class="manual_div_content">
Les actions <b><i>delete_by_id</i></b> et <b><i>destroy_by_id</i></b> permettent de supprimer
les <20>l<EFBFBD>ments d'une table en fonction de leur identifiant unique.
La diff<66>rence entre <i>delete</i> et <i>destroy</i> est li<6C>e <20> la <a
href="#manual_3400">suppression logique (soft delete)</a> d'un <20>l<EFBFBD>ment.
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>1 --</b></font> supprime de la base de
donn<6E>es le blog qui a pour identifiant unique la valeur 4 :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "80bff383-8ebd-4bde-bb42-37b6f67bc39f",
"action": "delete_by_id",
"entity": "blog",
"data": {
"blog_id": 4
}
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"blog_id": 4
},
"request_id": "80bff383-8ebd-4bde-bb42-37b6f67bc39f"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>2 --</b></font> supprime de la base de
donn<6E>es les blogs qui ont pour identifiant unique les valeurs 2 et 3 :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "38020cb7-d725-4c0e-80a0-63db7569155e",
"action": "delete_by_id",
"entity": "blog",
"data": [
{
"blog_id": 3
},
{
"blog_id": 2
}
]
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": [
{
"blog_id": 3
},
{
"blog_id": 2
}
],
"request_id": "38020cb7-d725-4c0e-80a0-63db7569155e"
}</pre>
</div>
<br /><br />
</div>
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_978">Validation de donn<6E>es
(validate)</a></p>
<div class="manual_div_content">
L'action <b><i>validate</i></b> permet de valider les propri<72>t<EFBFBD>s d'une instance (sans d<>clencher
d'action sur la base de donn<6E>es).
L'action <b><i>validate</i></b> appelle <a href="#manual_420">le module QxValidator</a> de la
biblioth<74>que QxOrm.
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>1 --</b></font> un <i>blog</i> doit
contenir du texte (propri<72>t<EFBFBD> <i>blog_text</i>) pour pouvoir <20>tre sauvegard<72> dans la base de
donn<6E>es.
La requ<71>te JSON suivante permet d'indiquer que l'instance est non valide avec un message
explicite :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "92043c2b-4ba8-4583-8fad-c828251734ba",
"action": "validate",
"entity": "blog",
"data": {
"blog_id": 9999,
"blog_text": ""
}
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"invalid_values": [
"blog",
[
{
"message": "'blog_text' property cannot be empty",
"path": "blog"
}
]
]
},
"request_id": "92043c2b-4ba8-4583-8fad-c828251734ba"
}</pre>
</div>
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>2 --</b></font> en ajoutant une valeur <20>
la propri<72>t<EFBFBD> <i>blog_text</i>, alors le <i>blog</i> devient valide (la r<>ponse JSON dispose d'un
champ <i>invalid_values</i> qui vaut <i>null</i>) :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "92043c2b-4ba8-4583-8fad-c828251734ba",
"action": "validate",
"entity": "blog",
"data": {
"blog_id": 9999,
"blog_text": "my blog text !!!"
}
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"invalid_values": null
},
"request_id": "92043c2b-4ba8-4583-8fad-c828251734ba"
}</pre>
</div>
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_979">Appel RAW SQL ou
proc<6F>dure stock<63>e</a></p>
<div class="manual_div_content">
L'action <b><i>call_custom_query</i></b> permet d'appeler une requ<71>te SQL personnalis<69>e ou une
proc<6F>dure stock<63>e.
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>1 --</b></font> ins<6E>re dans la base de
donn<6E>es un nouveau <i>author</i> avec une requ<71>te SQL personnalis<69>e :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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
}
]
}
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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"
}</pre>
</div>
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_961">Appel fonctions natives
C++</a></p>
<div class="manual_div_content">
L'action <b><i>call_entity_function</i></b> permet d'appeler des fonctions natives C++
enregistr<74>es dans le contexte QxOrm.<br />
<b>Pr<EFBFBD>requis :</b> la fonction native C++ doit <20>tre une fonction <i>static</i> avec pour
signature : <b><i>
<font style="background-color:yellow">static QJsonValue myNativeCppFct(const QJsonValue &
request);</font>
</i></b>
<br /><br />
Voici un exemple d'enregistrement de fonction native C++ pouvant <20>tre appel<65>e par les API JSON
de la biblioth<74>que QxOrm :
<br /><br />
<table border="1" bgcolor="#FFFFFF">
<col>
<tbody>
<tr>
<td>
<pre><span class="keyword">namespace</span> qx {
<span class="keyword">template</span> &lt;&gt; <span class="keyword">void</span> register_class(QxClass&lt;blog&gt; & t)
{
<span class="comment">// Register 'helloWorld()' static function in QxOrm context (can be called by QxRestApi JSON API module)</span>
t.fctStatic_1&lt;<span class="type">QJsonValue</span>, const <span class="type">QJsonValue</span> & &gt;(& blog::helloWorld, <span class="string">"helloWorld"</span>);
}}
<span class="comment">// 'helloWorld()' static function implementation</span>
<span class="type">QJsonValue</span> blog::helloWorld(const <span class="type">QJsonValue</span> & request)
{
<span class="type">QJsonObject</span> response;
response.insert(<span class="string">"request"</span>, request);
response.insert(<span class="string">"response"</span>, QString(<span class="string">"Hello World !"</span>));
return response;
}</pre>
</td>
</tr>
</tbody>
</table>
<br /><br />
Voici comment appeler cette fonction <i>helloWorld</i> avec les API JSON en utilisant l'action
<b><i>call_entity_function</i></b> :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "ab1ba7d3-9f98-4b18-a310-a9c34498d043",
"action": "call_entity_function",
"entity": "blog",
"fct": "helloWorld",
"data": {
"param1": "test",
"param2": "static fct call"
}
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"data": {
"request": {
"param1": "test",
"param2": "static fct call"
},
"response": "Hello World !"
},
"request_id": "ab1ba7d3-9f98-4b18-a310-a9c34498d043"
}</pre>
</div>
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_962">Meta-data (structure des
classes C++ enregistr<74>es dans le contexte QxOrm)</a></p>
<div class="manual_div_content">
L'action <b><i>get_meta_data</i></b> permet de r<>cup<75>rer les m<>ta-donn<6E>es d'une ou de toutes les
entit<69>s enregistr<74>es dans le contexte QxOrm (structure des classes avec liste des propri<72>t<EFBFBD>s et
relations).
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>1 --</b></font> r<>cup<75>re toutes les
m<>ta-donn<6E>es du <a href="#manual_972">projet d'exemple <i>qxBlogRestApi</i></a> :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"request_id": "842ed7b5-9b94-455f-86dc-32992866b3d5",
"action": "get_meta_data",
"entity": "*"
}</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>{
"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&lt;std::shared_ptr&lt;blog&gt;&gt;",
"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&lt;author&gt;",
"type_relation": "relation many-to-one"
},
{
"description": "",
"key": "list_comment",
"target": "comment",
"type": "QList&lt;std::shared_ptr&lt;comment&gt;&gt;",
"type_relation": "relation one-to-many"
},
{
"description": "",
"key": "list_category",
"target": "category",
"type": "qx::QxCollection&lt;long, QSharedPointer&lt;category&gt;&gt;",
"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&lt;blog&gt;",
"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&lt;long, std::shared_ptr&lt;blog&gt;&gt;",
"type_relation": "relation many-to-many"
}
],
"version": 0
}
]
},
"request_id": "842ed7b5-9b94-455f-86dc-32992866b3d5"
}</pre>
</div>
<br /><br />
</div>
<p class="manual_p_title_2"><a class="manual_a_title_2" name="manual_963">Envoyer une liste de
requ<71>tes JSON</a></p>
<div class="manual_div_content">
Afin de limiter le nombre de transactions entre le client et le serveur, il est possible
d'envoyer une liste de requ<71>tes JSON au <b>module QxRestApi</b>.
Chaque requ<71>te JSON de la liste peut disposer de son propre identifiant <i>request_id</i> (afin
d'associer une r<>ponse JSON <20> la requ<71>te correspondante).
Lorsqu'une liste de requ<71>tes JSON est envoy<6F>e au <b>module QxRestApi</b>, alors <a
href="#manual_370">une transaction (commit/rollback)</a> est automatiquement cr<63><72>e (ainsi en
cas d'erreur, tous les traitements sur la base de donn<6E>es sont annul<75>s).
<br /><br />
<font style="background-color:yellow"><b>-- Exemple n<>1 --</b></font> envoi 4 requ<71>tes JSON au
<b>module QxRestApi</b> (1 requ<71>te pour <a href="#manual_962">r<EFBFBD>cup<EFBFBD>rer les m<>ta-donn<6E>es du
projet</a> + 3 requ<71>tes <a href="#manual_9731"><i>fetch_all</i> avec diff<66>rent niveau de
r<>cup<75>ration des relations</a>) :
<br /><br />
<font style="color:blue"><i>Requ<EFBFBD>te JSON :</i></font><br />
<div class="json_pretty">
<pre>[
{
"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": [
"*->*"
]
}
]</pre>
</div>
<font style="color:blue"><i>R<EFBFBD>ponse JSON :</i></font><br />
<div class="json_pretty">
<pre>[
{
"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&lt;std::shared_ptr&lt;blog&gt;&gt;",
"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&lt;author&gt;",
"type_relation": "relation many-to-one"
},
{
"description": "",
"key": "list_comment",
"target": "comment",
"type": "QList&lt;std::shared_ptr&lt;comment&gt;&gt;",
"type_relation": "relation one-to-many"
},
{
"description": "",
"key": "list_category",
"target": "category",
"type": "qx::QxCollection&lt;long, QSharedPointer&lt;category&gt;&gt;",
"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&lt;blog&gt;",
"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&lt;long, std::shared_ptr&lt;blog&gt;&gt;",
"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"
}
]</pre>
</div>
<br /><br />
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<br>
<hr style="width: 80%" align="center" size="1" color="#100D5A">
<table border="0" style="width: 80%" align="center">
<col>
<col>
<col>
<tbody>
<tr>
<td align="left" valign="middle">
<img alt="QxOrm" src="./resource/logo_qxorm_small.png" width="168" height="40">
</td>
<td align="center" valign="middle">
<font size="2"><EFBFBD> 2011-202XDL Teamty - <a href="mailto:ic-east.com">ic-east.com</a></font>
</td>
<td align="right" valign="middle">
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="2K4Z58ZYAYJ6S">
<input type="image" src="./resource/paypal_support_qxorm_library.gif" border="0" name="submit"
alt="Support QxOrm library - PayPal">
<img alt="" border="0" src="https://www.paypalobjects.com/fr_FR/i/scr/pixel.gif" width="1" height="1">
</form>
</td>
</tr>
</tbody>
</table>
</body>
</html>