13103 lines
916 KiB
HTML
13103 lines
916 KiB
HTML
<!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 >> 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 : </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<T></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<T>(qx::QxClass<T> & 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 => 'person' */
|
||
/* param 2 : the base class, if no base class, use the qx trait => '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 <QxOrm.h>' and '#include "export.h"'</span>
|
||
#include <span class="string">"person.h"</span> <span class="comment">// Class definition 'person'</span>
|
||
#include <span class="string"><QxOrm_Impl.h></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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>person<span class="operator">> &</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">(&</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' <=> primary key in your database
|
||
</span> t<span class="operator">.</span>data<span class="operator">(&</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">(&</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">(&</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<T>::id()</a> et <a
|
||
href="../doxygen/html/classqx_1_1_qx_class.html"
|
||
target="_blank">qx::QxClass<T>::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<T>::fct_0()</a>, <a
|
||
href="../doxygen/html/classqx_1_1_qx_class.html"
|
||
target="_blank">qx::QxClass<T>::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<T></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<T></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"><</span>QString<span class="operator">,</span><span class="type"> long</span><span class="operator">,</span> QString<span class="operator">></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"><</span>blog<span class="operator">></span> blog_ptr<span class="operator">;</span><span class="keyword">
|
||
typedef</span> std<span class="operator">::</span>vector<span class="operator"><</span>blog_ptr<span class="operator">></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"><</span><span class="int">0</span><span class="operator">>(</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"><</span><span class="int">1</span><span class="operator">>(</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"><</span><span class="int">2</span><span class="operator">>(</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"> &</span> s<span class="operator">) {</span> boost<span class="operator">::</span>tuples<span class="operator">::</span>get<span class="operator"><</span><span class="int">0</span><span class="operator">>(</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"><</span><span class="int">1</span><span class="operator">>(</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"> &</span> s<span class="operator">) {</span> boost<span class="operator">::</span>tuples<span class="operator">::</span>get<span class="operator"><</span><span class="int">2</span><span class="operator">>(</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"><</span>author<span class="operator">></span> author_ptr<span class="operator">;</span><span class="keyword">
|
||
typedef</span> qx<span class="operator">::</span>QxCollection<span class="operator"><</span>author<span class="operator">::</span>type_composite_key<span class="operator">,</span> author_ptr<span class="operator">></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 <QxOrm_Impl.h>
|
||
</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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>author<span class="operator">> &</span> t<span class="operator">)
|
||
{</span>
|
||
<font style="background-color:yellow">t<span class="operator">.</span>id<span class="operator">(&</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">(&</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">(&</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">(&</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">(&</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"><</span><span class="type">int</span><span class="operator">>(&</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<T></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"> <-></span><span class="string"> "SMALLINT"
|
||
"qx_bool"</span><span class="operator"> <-></span><span class="string"> "SMALLINT"
|
||
"short"</span><span class="operator"> <-></span><span class="string"> "SMALLINT"
|
||
"int"</span><span class="operator"> <-></span><span class="string"> "INTEGER"
|
||
"long"</span><span class="operator"> <-></span><span class="string"> "INTEGER"
|
||
"long long"</span><span class="operator"> <-></span><span class="string"> "INTEGER"
|
||
"float"</span><span class="operator"> <-></span><span class="string"> "FLOAT"
|
||
"double"</span><span class="operator"> <-></span><span class="string"> "FLOAT"
|
||
"long double"</span><span class="operator"> <-></span><span class="string"> "FLOAT"
|
||
"unsigned short"</span><span class="operator"> <-></span><span class="string"> "SMALLINT"
|
||
"unsigned int"</span><span class="operator"> <-></span><span class="string"> "INTEGER"
|
||
"unsigned long"</span><span class="operator"> <-></span><span class="string"> "INTEGER"
|
||
"unsigned long long"</span><span class="operator"> <-></span><span class="string"> "INTEGER"
|
||
"std::string"</span><span class="operator"> <-></span><span class="string"> "TEXT"
|
||
"std::wstring"</span><span class="operator"> <-></span><span class="string"> "TEXT"
|
||
"QString"</span><span class="operator"> <-></span><span class="string"> "TEXT"
|
||
"QVariant"</span><span class="operator"> <-></span><span class="string"> "TEXT"
|
||
"QUuid"</span><span class="operator"> <-></span><span class="string"> "TEXT"
|
||
"QDate"</span><span class="operator"> <-></span><span class="string"> "DATE"
|
||
"QTime"</span><span class="operator"> <-></span><span class="string"> "TIME"
|
||
"QDateTime"</span><span class="operator"> <-></span><span class="string"> "TIMESTAMP"
|
||
"QByteArray"</span><span class="operator"> <-></span><span class="string"> "BLOB"
|
||
"qx::QxDateNeutral"</span><span class="operator"> <-></span><span class="string"> "TEXT"
|
||
"qx::QxTimeNeutral"</span><span class="operator"> <-></span><span class="string"> "TEXT"
|
||
"qx::QxDateTimeNeutral"</span><span class="operator"> <-></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<T>::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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>person<span class="operator">> &</span> t<span class="operator">)
|
||
{</span>
|
||
t<span class="operator">.</span>id<span class="operator">(&</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">(&</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">(&</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">(&</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">(&</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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>person<span class="operator">> &</span> t<span class="operator">)
|
||
{</span>
|
||
t<span class="operator">.</span>id<span class="operator">(&</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">(&</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">(&</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">(&</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">(&</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">-></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">()-></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">()-></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">()-></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">()-></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">()-></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">()-></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"><</span>drug<span class="operator">></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">-></span>name<span class="operator"> =</span><span class="string"> "name1"</span><span class="operator">;</span> d1<span class="operator">-></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">-></span>name<span class="operator"> =</span><span class="string"> "name2"</span><span class="operator">;</span> d2<span class="operator">-></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">-></span>name<span class="operator"> =</span><span class="string"> "name3"</span><span class="operator">;</span> d3<span class="operator">-></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<Key, Value>'
|
||
</span><span class="keyword"> typedef</span> std<span class="operator">::</span>vector<span class="operator"><</span>drug_ptr<span class="operator">></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">-></span>name<span class="operator"> =</span><span class="string"> "name2 modified"</span><span class="operator">;</span>
|
||
d2<span class="operator">-></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; /* ... */
|
||
qx::dao::insert(t);</i></li>
|
||
<li><i>my_entity * t; /* ... */
|
||
qx::dao::insert(t);</i></li>
|
||
<li><i>std::shared_ptr<my_entity> t; /* ...
|
||
*/ qx::dao::insert(t);</i></li>
|
||
<li><i>QList<my_entity> lst; /* ... */
|
||
qx::dao::insert(lst);</i></li>
|
||
<li><i>QList<std::shared_ptr<my_entity> > lst; /* ...
|
||
*/ 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"><</span>drug<span class="operator">>();</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<T></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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>Bar<span class="operator">> &</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">(&</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">(&</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">-></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"><</span>Bar<span class="operator">>();</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"><</span>Bar<span class="operator">>();</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"><</span>Bar<span class="operator">>();</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">-></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"><</span>author<span class="operator">></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"> <</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"><</span>author<span class="operator">></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"> <</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"> > :</span>age_3_0
|
||
OR last_name<span class="operator"> <> :</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"> <= :</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"><</span>T<span class="operator">>()</span>
|
||
qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_query<span class="operator"><</span>T<span class="operator">>()</span>
|
||
qx<span class="operator">::</span>dao<span class="operator">::</span>update_by_query<span class="operator"><</span>T<span class="operator">>()</span>
|
||
qx<span class="operator">::</span>dao<span class="operator">::</span>delete_by_query<span class="operator"><</span>T<span class="operator">>()</span>
|
||
qx<span class="operator">::</span>dao<span class="operator">::</span>destroy_by_query<span class="operator"><</span>T<span class="operator">>()</span>
|
||
qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_query_with_relation<span class="operator"><</span>T<span class="operator">>()</span>
|
||
qx<span class="operator">::</span>dao<span class="operator">::</span>fetch_by_query_with_all_relation<span class="operator"><</span>T<span class="operator">>()</span>
|
||
qx<span class="operator">::</span>dao<span class="operator">::</span>update_by_query_with_relation<span class="operator"><</span>T<span class="operator">>()</span>
|
||
qx<span class="operator">::</span>dao<span class="operator">::</span>update_by_query_with_all_relation<span class="operator"><</span>T<span class="operator">>()</span>
|
||
qx<span class="operator">::</span>dao<span class="operator">::</span>update_optimized_by_query<span class="operator"><</span>T<span class="operator">>()</span><span class="comment">
|
||
|
||
// avec la classe qx::QxSession
|
||
</span>qx<span class="operator">::</span>QxSession<span class="operator">::</span>count<span class="operator"><</span>T<span class="operator">>()</span>
|
||
qx<span class="operator">::</span>QxSession<span class="operator">::</span>fetchByQuery<span class="operator"><</span>T<span class="operator">>()</span>
|
||
qx<span class="operator">::</span>QxSession<span class="operator">::</span>update<span class="operator"><</span>T<span class="operator">>()</span>
|
||
qx<span class="operator">::</span>QxSession<span class="operator">::</span>deleteByQuery<span class="operator"><</span>T<span class="operator">>()</span>
|
||
qx<span class="operator">::</span>QxSession<span class="operator">::</span>destroyByQuery<span class="operator"><</span>T<span class="operator">>()</span><span class="comment">
|
||
|
||
// avec la classe qx::QxRepository<T>
|
||
</span>qx<span class="operator">::</span>QxRepository<span class="operator"><</span>T<span class="operator">>::</span>count<span class="operator">()</span>
|
||
qx<span class="operator">::</span>QxRepository<span class="operator"><</span>T<span class="operator">>::</span>fetchByQuery<span class="operator">()</span>
|
||
qx<span class="operator">::</span>QxRepository<span class="operator"><</span>T<span class="operator">>::</span>update<span class="operator">()</span>
|
||
qx<span class="operator">::</span>QxRepository<span class="operator"><</span>T<span class="operator">>::</span>deleteByQuery<span class="operator">()</span>
|
||
qx<span class="operator">::</span>QxRepository<span class="operator"><</span>T<span class="operator">>::</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<T>()</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<T>()</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<T>()</a> est une fonction <i>template</i> :
|
||
le type T doit <20>tre enregistr<74> dans le contexte QxOrm (fonction
|
||
<i>qx::register_class<T></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">() ></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) => 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 => 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) => 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 => 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<T>::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"><</span>blog<span class="operator">></span> blog_ptr<span class="operator">;</span><span class="keyword">
|
||
typedef</span> std<span class="operator">::</span>vector<span class="operator"><</span>blog_ptr<span class="operator">></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"><</span>author<span class="operator">></span> author_ptr<span class="operator">;</span><span class="keyword">
|
||
typedef</span> qx<span class="operator">::</span>QxCollection<span class="operator"><</span>QString<span class="operator">,</span> author_ptr<span class="operator">></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"><QxOrm_Impl.h></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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>author<span class="operator">> &</span> t<span class="operator">)
|
||
{</span>
|
||
t<span class="operator">.</span>id<span class="operator">(&</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">(&</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">(&</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">(&</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">(&</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"><</span><span class="type">int</span><span class="operator">>(&</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<T>::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"><</span>blog<span class="operator">></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"><</span>comment<span class="operator">></span> comment_ptr<span class="operator">;</span><span class="keyword">
|
||
typedef</span> QList<span class="operator"><</span>comment_ptr<span class="operator">></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"><QxOrm_Impl.h></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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>comment<span class="operator">> &</span> t<span class="operator">)
|
||
{</span>
|
||
t<span class="operator">.</span>id<span class="operator">(&</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">(&</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">(&</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">(&</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<T>::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"><</span>blog<span class="operator">></span> blog_ptr<span class="operator">;</span><span class="keyword">
|
||
typedef</span> qx<span class="operator">::</span>QxCollection<span class="operator"><</span><span class="type">long</span><span class="operator">,</span> blog_ptr<span class="operator">></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"><</span>category<span class="operator">></span> category_ptr<span class="operator">;</span><span class="keyword">
|
||
typedef</span> qx<span class="operator">::</span>QxCollection<span class="operator"><</span><span class="type">long</span><span class="operator">,</span> category_ptr<span class="operator">></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"><QxOrm_Impl.h></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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>category<span class="operator">> &</span> t<span class="operator">)
|
||
{</span>
|
||
t<span class="operator">.</span>id<span class="operator">(&</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">(&</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">(&</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">(&</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<T>::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<T></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<T></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->list_blog->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"> <<</span><span class="string"> "author_id->list_blog->list_comment"</span><span class="operator">;</span>
|
||
relation<span class="operator"> <<</span><span class="string"> "author_id->list_blog->list_category"</span><span class="operator">;</span>
|
||
relation<span class="operator"> <<</span><span class="string"> "list_comment"</span><span class="operator">;</span>
|
||
relation<span class="operator"> <<</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">"*->*->*"</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">() <<</span><span class="string"> "{ blog_text }"</span><span class="operator"> <<</span><span class="string"> "author_id { name, birthdate }"</span><span class="operator"> <<</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">() ></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">]-></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">]-></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">]-></span>m_author<span class="operator">-></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">]-></span>m_author<span class="operator">-></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">]-></span>m_commentX<span class="operator">.</span>size<span class="operator">() ></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">]-></span>m_commentX<span class="operator">[</span><span class="int">0</span><span class="operator">]-></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">]-></span>m_commentX<span class="operator">[</span><span class="int">0</span><span class="operator">]-></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><</b> <b>></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 << <span class="string">"<font style="background-color:yellow"><blog_alias></font> { blog_text }"</span>;
|
||
relations << <span class="string">"author_id <font style="background-color:yellow"><author_alias></font> { name, birthdate }"</span>;
|
||
relations << <span class="string">"list_comment <font style="background-color:yellow"><list_comment_alias></font> { comment_text } -> blog_id <font style="background-color:yellow"><blog_alias_2></font> -> * <font style="background-color:yellow"><..._my_alias_suffix></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() << author::female));
|
||
daoError = qx::dao::fetch_by_query_with_relation(QStringList() << <span class="string">"<blog_alias> { blog_text }"</span> << <span class="string">"author_id <author_alias> { name, birthdate, sex }"</span>
|
||
<< <span class="string">"list_comment <list_comment_alias> { comment_text }"</span>, query, lstBlogWithJoinQueries);
|
||
qx::dump(lstBlogWithJoinQueries);
|
||
qAssert(lstBlogWithJoinQueries.size() > 0);
|
||
qAssert(lstBlogWithJoinQueries[0]->m_text == <span class="string">"update blog_text_1"</span>);
|
||
qAssert(lstBlogWithJoinQueries[0]->m_author->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<T>
|
||
QVector<T>
|
||
QSet<T>
|
||
QLinkedList<T>
|
||
QHash<Key, Value>
|
||
QMap<Key, Value>
|
||
QMultiHash<Key, Value>
|
||
QMultiMap<Key, Value>
|
||
</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<Key, Value>
|
||
boost::unordered_set<T>
|
||
boost::unordered_multimap<Key, Value>
|
||
boost::unordered_multiset<T>
|
||
</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<T>
|
||
std::vector<T>
|
||
std::set<T>
|
||
std::map<Key, Value>
|
||
|
||
std::unordered_map<Key, Value>
|
||
std::unordered_set<T>
|
||
std::unordered_multimap<Key, Value>
|
||
std::unordered_multiset<T>
|
||
</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<Key,
|
||
Value></i> ?<br>
|
||
<i><a href="../doxygen/html/classqx_1_1_qx_collection.html"
|
||
target="_blank">qx::QxCollection<Key, Value></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<T></i> ou
|
||
<i>QList<T></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<Key,
|
||
Value></i> ou <i>boost::unordered_map<Key, Value></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<Key, Value></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<Key, Value></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<Key, Value></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<Key, Value></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<Key, Value></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"><</span>drug<span class="operator">></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"><</span>QString<span class="operator">,</span> drug_ptr<span class="operator">></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">-></span>code<span class="operator"> =</span><span class="string"> "code1"</span><span class="operator">;</span> d1<span class="operator">-></span>name<span class="operator"> =</span><span class="string"> "name1"</span><span class="operator">;</span> d1<span class="operator">-></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">-></span>code<span class="operator"> =</span><span class="string"> "code2"</span><span class="operator">;</span> d2<span class="operator">-></span>name<span class="operator"> =</span><span class="string"> "name2"</span><span class="operator">;</span> d2<span class="operator">-></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">-></span>code<span class="operator"> =</span><span class="string"> "code3"</span><span class="operator">;</span> d3<span class="operator">-></span>name<span class="operator"> =</span><span class="string"> "name3"</span><span class="operator">;</span> d3<span class="operator">-></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">-></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">-></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">-></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">() <<</span> qPrintable<span class="operator">(</span>p<span class="operator">-></span>name<span class="operator">) <<</span><span class="string"> " "</span><span class="operator"> <<</span> qPrintable<span class="operator">(</span>p<span class="operator">-></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"> <</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">() <<</span> qPrintable<span class="operator">(</span>p<span class="operator">-></span>name<span class="operator">) <<</span><span class="string"> " "</span><span class="operator"> <<</span> qPrintable<span class="operator">(</span>p<span class="operator">-></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"><</span>QString<span class="operator">,</span> drug_ptr<span class="operator">></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">() <<</span> qPrintable<span class="operator">(</span>itr<span class="operator">.</span>value<span class="operator">()-></span>name<span class="operator">) <<</span><span class="string"> " "</span><span class="operator"> <<</span> qPrintable<span class="operator">(</span>itr<span class="operator">.</span>value<span class="operator">()-></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<T>
|
||
QScopedPointer<T>
|
||
QWeakPointer<T>
|
||
QSharedDataPointer<T>
|
||
</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<T>
|
||
boost::intrusive_ptr<T>
|
||
boost::scoped_ptr<T>
|
||
boost::weak_ptr<T>
|
||
</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<T>
|
||
std::unique_ptr<T>
|
||
std::weak_ptr<T>
|
||
</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<T></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<T></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<T></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<Key,
|
||
Value></i>.<br>
|
||
<br>
|
||
<b>Exemple d'utilisation du pointeur intelligent <i>qx::dao::ptr<T></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"><</span>blog<span class="operator">></span> blog_isdirty<span class="operator"> =</span> qx<span class="operator">::</span>dao<span class="operator">::</span>ptr<span class="operator"><</span>blog<span class="operator">>(</span><span class="keyword">new</span> blog<span class="operator">());</span>
|
||
blog_isdirty<span class="operator">-></span>m_id<span class="operator"> =</span> blog_1<span class="operator">-></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">() && !</span> blog_isdirty<span class="operator">.</span>isDirty<span class="operator">());</span>
|
||
|
||
blog_isdirty<span class="operator">-></span>m_text<span class="operator"> =</span><span class="string"> "blog property 'text' modified => 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"> && (</span>lstDiff<span class="operator">.</span>count<span class="operator">() ==</span><span class="int"> 1</span><span class="operator">) && (</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 => '%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">() && !</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"><</span> QList<span class="operator"><</span>author_ptr<span class="operator">> ></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"><</span>author_ptr<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>container_isdirty<span class="operator">);</span>
|
||
qAssert<span class="operator">(!</span> daoError<span class="operator">.</span>isValid<span class="operator">() && !</span> container_isdirty<span class="operator">.</span>isDirty<span class="operator">() && (</span>container_isdirty<span class="operator">-></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">-></span>at<span class="operator">(</span><span class="int">1</span><span class="operator">);</span>
|
||
author_ptr_dirty<span class="operator">-></span>m_name<span class="operator"> =</span><span class="string"> "author name modified at index 1 => 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"> && (</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 => '%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">-></span>at<span class="operator">(</span><span class="int">2</span><span class="operator">);</span>
|
||
author_ptr_dirty<span class="operator">-></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"> && (</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 => '%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">() && !</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">() <<</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">() && (</span>lst_blog_with_only_date_creation<span class="operator">.</span>size<span class="operator">() ></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">() ></span><span class="int"> 0</span><span class="operator">) && (</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">]-></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<T></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"> &</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"> &</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"> &</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"> &</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"> <></span>
|
||
<font style="background-color:yellow"><span class="keyword">struct</span> QxDao_Trigger<span class="operator"><</span>BaseClassTrigger<span class="operator">></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">-></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">-></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 <QxOrm_Impl.h>
|
||
</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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>BaseClassTrigger<span class="operator">> &</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">(&</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">(&</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">(&</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">(&</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">(&</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<T></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<T></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"> &</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 <QxOrm_Impl.h>
|
||
</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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>person<span class="operator">> &</span> t<span class="operator">)
|
||
{</span>
|
||
t<span class="operator">.</span>id<span class="operator">(&</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">(&</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">(&</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">(&</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">(&</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"><</span>person<span class="operator">> *</span> pAllValidator<span class="operator"> =</span> t<span class="operator">.</span>getAllValidator<span class="operator">();</span>
|
||
pAllValidator<span class="operator">-></span>add_NotEmpty<span class="operator">(</span><span class="string">"firstName"</span><span class="operator">);</span>
|
||
pAllValidator<span class="operator">-></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">-></span>add_CustomValidator<span class="operator">(&</span> person<span class="operator">::</span>isValid<span class="operator">);</span>
|
||
pAllValidator<span class="operator">-></span>add_CustomValidator_QVariant<span class="operator">(&</span> validateFirstName<span class="operator">,</span><span class="string"> "firstName"</span><span class="operator">);</span>
|
||
pAllValidator<span class="operator">-></span>add_CustomValidator_DataType<span class="operator"><</span>QDateTime<span class="operator">>(&</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"> &</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->add_CustomValidator(& 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">) && (</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"> &</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"> &</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"> &</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"> &</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<T></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<T></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><QxExtras/QxBoostOptionalOnly.h></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<QString> firstName<span class="operator">;</span></font>
|
||
<font style="background-color:yellow">boost::optional<QString> lastName<span class="operator">;</span></font>
|
||
<font style="background-color:yellow">boost::optional<QDateTime> 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<T></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<T></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<T></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"> &</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"> &</span> id<span class="operator"> =</span> QVariant<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &</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"> &</span> list<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &</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"> &</span> query<span class="operator">,</span> qx<span class="operator">::</span>IxCollection<span class="operator"> &</span> list<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &</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"> &</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"> &</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"> &</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &</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"> &</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"> &</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"> &</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"> &</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"> &</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"> &</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"> &</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"><</span>qx<span class="operator">::</span>IxPersistable<span class="operator"> *></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">-></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"><</span>blog<span class="operator">></span> blog_ptr<span class="operator">;</span><span class="keyword">
|
||
typedef</span> std<span class="operator">::</span>vector<span class="operator"><</span>blog_ptr<span class="operator">></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"><</span>author<span class="operator">></span> author_ptr<span class="operator">;</span><span class="keyword">
|
||
typedef</span> qx<span class="operator">::</span>QxCollection<span class="operator"><</span>QString<span class="operator">,</span> author_ptr<span class="operator">></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"><QxOrm_Impl.h></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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>author<span class="operator">> &</span> t<span class="operator">)
|
||
{</span>
|
||
t<span class="operator">.</span>id<span class="operator">(&</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">(&</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">(&</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">(&</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">(&</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"><</span><span class="type">int</span><span class="operator">>(&</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<blog_impl> m_pImpl; <span class="comment">//!< 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<author> & getAuthor();
|
||
QList< std::shared_ptr<comment> > & listOfComments();
|
||
qx::QxCollection<long, QSharedPointer<category> > & listOfCategories();
|
||
|
||
};
|
||
|
||
QX_REGISTER_HPP_QX_BLOG(blog, qx::trait::no_base_class_defined, 0)
|
||
|
||
<span class="keyword">typedef</span> std::shared_ptr<blog> blog_ptr;
|
||
<span class="keyword">typedef</span> std::vector<blog_ptr> 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"><QxOrm_Impl.h></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> <> <span class="keyword">void</span> register_class(QxClass<blog> & 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->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->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->m_dt_creation</font>; }
|
||
|
||
<span class="keyword">void</span> blog::setId(<span class="type">long</span> l) { <font style="background-color:yellow">m_pImpl->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->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->m_dt_creation</font> = d; }
|
||
|
||
std::shared_ptr<author> & blog::getAuthor() { <span class="keyword">return</span> <font style="background-color:yellow">m_pImpl->m_author</font>; }
|
||
|
||
QList< std::shared_ptr<comment> > & blog::listOfComments() { <span class="keyword">return</span> <font style="background-color:yellow">m_pImpl->m_commentX</font>; }
|
||
|
||
qx::QxCollection<long, QSharedPointer<category> > & blog::listOfCategories() { <span class="keyword">return</span> <font style="background-color:yellow">m_pImpl->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<T>()</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 <boost/serialization/serialization.hpp>
|
||
#include <boost/serialization/split_free.hpp>
|
||
#include <boost/serialization/nvp.hpp>
|
||
</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"> <</span><span class="keyword">class</span> Archive<span class="operator">></span><span class="type">
|
||
void</span> save<span class="operator">(</span>Archive<span class="operator"> &</span> ar<span class="operator">,</span><span class="keyword"> const</span> ExtObject3D<span class="operator"> &</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"> <<</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"> <<</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"> <<</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"> <<</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"> <</span><span class="keyword">class</span> Archive<span class="operator">></span><span class="type">
|
||
void</span> load<span class="operator">(</span>Archive<span class="operator"> &</span> ar<span class="operator">,</span> ExtObject3D<span class="operator"> &</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"> >></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"> >></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"> >></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"> >></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<mon_objet></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"> <></span><span class="keyword"> struct</span> QxConvert_ToVariant<span class="operator"><</span> ExtObject3D<span class="operator"> > {</span><span class="keyword">
|
||
static inline</span> QVariant toVariant<span class="operator">(</span><span class="keyword">const</span> ExtObject3D<span class="operator"> &</span> t<span class="operator">,</span><span class="keyword"> const</span> QString<span class="operator"> &</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"> <></span><span class="keyword"> struct</span> QxConvert_FromVariant<span class="operator"><</span> ExtObject3D<span class="operator"> > {</span><span class="keyword">
|
||
static inline</span> qx_bool fromVariant<span class="operator">(</span><span class="keyword">const</span> QVariant<span class="operator"> &</span> v<span class="operator">,</span> ExtObject3D<span class="operator"> &</span> t<span class="operator">,</span><span class="keyword"> const</span> QString<span class="operator"> &</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 <QxOrm.h></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<< (QDataStream & stream, const MyPersistableType & t)
|
||
{
|
||
<span class="comment">/* Your implementation here */</span>
|
||
}
|
||
|
||
QDataStream & operator>> (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> <> <span class="keyword">struct</span> QxConvert_ToVariant< MyPersistableType > {
|
||
<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< MyPersistableType > {
|
||
<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> <>
|
||
<span class="keyword">struct</span> QxConvert_ToJson< MyPersistableType >
|
||
{
|
||
<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> <>
|
||
<span class="keyword">struct</span> QxConvert_FromJson< MyPersistableType >
|
||
{
|
||
<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<T></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 <QxOrm_Impl.h>
|
||
</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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>DatabaseVersion<span class="operator">> &</span> t<span class="operator">)
|
||
{</span>
|
||
t<span class="operator">.</span>id<span class="operator">(&</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">(&</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"> <</span> qApp<span class="operator">-></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">-></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">()-></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"> >=</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"><</span>QString<span class="operator">,</span> qx<span class="operator">::</span>IxClass<span class="operator"> *> *</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"> <</span> pAllClasses<span class="operator">-></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">-></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">-></span>isKindOf<span class="operator">(</span><span class="string">"qx::service::IxParameter"</span><span class="operator">) ||</span> pClass<span class="operator">-></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 <= <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">-></span>getVersion<span class="operator">() <=</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">-></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">-></span>getName<span class="operator">() +</span><span class="string"> " ( ) WITH (OIDS = FALSE);"
|
||
"ALTER TABLE "</span><span class="operator"> +</span> pClass<span class="operator">-></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">-></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"> && (</span>l<span class="operator"> <</span> pDataMemberX<span class="operator">-></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">-></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">-></span>getVersion<span class="operator">() <=</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">-></span>getName<span class="operator">() +</span><span class="string"> " ADD COLUMN "</span><span class="operator"> +</span> p<span class="operator">-></span>getName<span class="operator">() +</span><span class="string"> " "</span><span class="operator"> +</span> p<span class="operator">-></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">-></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">-></span>getName<span class="operator">() +</span><span class="string"> " ADD PRIMARY KEY ("</span><span class="operator"> +</span> p<span class="operator">-></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">-></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">-></span>getName<span class="operator">() +</span><span class="string"> "_"</span><span class="operator"> +</span> p<span class="operator">-></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">-></span>getName<span class="operator">() +</span><span class="string"> " USING "</span><span class="operator"> +</span> p<span class="operator">-></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">-></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">-></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">-></span>getName<span class="operator">() +</span><span class="string"> " ALTER COLUMN "</span><span class="operator"> +</span> p<span class="operator">-></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">-></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">-></span>getName<span class="operator">() +</span><span class="string"> "_"</span><span class="operator"> +</span> p<span class="operator">-></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">-></span>getName<span class="operator">() +</span><span class="string"> "_"</span><span class="operator"> +</span> p<span class="operator">-></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">-></span>getName<span class="operator">() +</span><span class="string"> " ALTER COLUMN "</span><span class="operator"> +</span> p<span class="operator">-></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">-></span>getName<span class="operator">() +</span><span class="string"> "_"</span><span class="operator"> +</span> p<span class="operator">-></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">-></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">-></span>getName<span class="operator">() +</span><span class="string"> "."</span><span class="operator"> +</span> p<span class="operator">-></span>getName<span class="operator">() +</span><span class="string"> " IS $$"</span><span class="operator"> +</span> p<span class="operator">-></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 => 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"> &</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">() <<</span> sqlError<span class="operator">.</span>databaseText<span class="operator">();</span>
|
||
qDebug<span class="operator">() <<</span> sqlError<span class="operator">.</span>driverText<span class="operator">();</span>
|
||
qDebug<span class="operator">() <<</span> sqlError<span class="operator">.</span>number<span class="operator">();</span>
|
||
qDebug<span class="operator">() <<</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"> <-></span><span class="string"> "SMALLINT"
|
||
"qx_bool"</span><span class="operator"> <-></span><span class="string"> "SMALLINT"
|
||
"short"</span><span class="operator"> <-></span><span class="string"> "SMALLINT"
|
||
"int"</span><span class="operator"> <-></span><span class="string"> "INTEGER"
|
||
"long"</span><span class="operator"> <-></span><span class="string"> "INTEGER"
|
||
"long long"</span><span class="operator"> <-></span><span class="string"> "INTEGER"
|
||
"float"</span><span class="operator"> <-></span><span class="string"> "FLOAT"
|
||
"double"</span><span class="operator"> <-></span><span class="string"> "FLOAT"
|
||
"long double"</span><span class="operator"> <-></span><span class="string"> "FLOAT"
|
||
"unsigned short"</span><span class="operator"> <-></span><span class="string"> "SMALLINT"
|
||
"unsigned int"</span><span class="operator"> <-></span><span class="string"> "INTEGER"
|
||
"unsigned long"</span><span class="operator"> <-></span><span class="string"> "INTEGER"
|
||
"unsigned long long"</span><span class="operator"> <-></span><span class="string"> "INTEGER"
|
||
"std::string"</span><span class="operator"> <-></span><span class="string"> "TEXT"
|
||
"std::wstring"</span><span class="operator"> <-></span><span class="string"> "TEXT"
|
||
"QString"</span><span class="operator"> <-></span><span class="string"> "TEXT"
|
||
"QVariant"</span><span class="operator"> <-></span><span class="string"> "TEXT"
|
||
"QUuid"</span><span class="operator"> <-></span><span class="string"> "TEXT"
|
||
"QDate"</span><span class="operator"> <-></span><span class="string"> "DATE"
|
||
"QTime"</span><span class="operator"> <-></span><span class="string"> "TIME"
|
||
"QDateTime"</span><span class="operator"> <-></span><span class="string"> "TIMESTAMP"
|
||
"QByteArray"</span><span class="operator"> <-></span><span class="string"> "BLOB"
|
||
"qx::QxDateNeutral"</span><span class="operator"> <-></span><span class="string"> "TEXT"
|
||
"qx::QxTimeNeutral"</span><span class="operator"> <-></span><span class="string"> "TEXT"
|
||
"qx::QxDateTimeNeutral"</span><span class="operator"> <-></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"><</span>QString<span class="operator">,</span> QString<span class="operator">> *</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">-></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">-></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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>MyClass<span class="operator">> &</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">(&</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">-></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"> &</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">((&</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"> &,</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"> &,</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"> &</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">-></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->pInstance' method
|
||
</span> qx<span class="operator">::</span>IxPersistable_ptr ptr<span class="operator"> =</span> pDaoParams<span class="operator">-></span>pInstance<span class="operator">;</span><span class="comment">
|
||
// If the async query is associated to a list of objects, just use 'pDaoParams->pListOfInstances' method
|
||
</span> qx<span class="operator">::</span>IxPersistableCollection_ptr lst<span class="operator"> =</span> pDaoParams<span class="operator">-></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<T></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<T></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"><</span> QList<span class="operator"><</span>author<span class="operator">> ></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"><</span> std<span class="operator">::</span>vector<span class="operator"><</span>blog<span class="operator">> ></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">-></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"><</span> QSharedPointer<span class="operator"><</span> std<span class="operator">::</span>vector<span class="operator"><</span>blog<span class="operator">> > >(</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<T>()</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<T>()</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"> &</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"> &</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"> &</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 <QxOrm_Impl.h>
|
||
</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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>TestQtProperty<span class="operator">> &</span> t<span class="operator">)
|
||
{</span> <font style="background-color:yellow">qx<span class="operator">::</span>register_all_qt_properties<span class="operator"><</span>TestQtProperty<span class="operator">>(</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<T>()</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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>person<span class="operator">> &</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">(&</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">-></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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>person<span class="operator">> &</span> t<span class="operator">)
|
||
{</span>
|
||
t<span class="operator">.</span>id<span class="operator">(&</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">(&</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">(&</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">(&</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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>person<span class="operator">> &</span> t<span class="operator">)
|
||
{</span>
|
||
t<span class="operator">.</span>id<span class="operator">(&</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">(&</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">(&</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">(&</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">(&</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">(&</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 } -> blog_id -> *"</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->m_text != <span class="string">""</span>); <span class="comment">// Fetched</span>
|
||
qAssert(blogFromJsonFiltered->m_dt_creation.isNull()); <span class="comment">// Not fetched</span>
|
||
qAssert(blogFromJsonFiltered->m_author->m_sex == author::unknown); <span class="comment">// Not fetched</span>
|
||
qAssert(blogFromJsonFiltered->m_author->m_name != <span class="string">""</span>); <span class="comment">// Fetched</span>
|
||
qAssert(blogFromJsonFiltered->m_commentX.size() > <span class="int">0</span>);
|
||
qAssert(blogFromJsonFiltered->m_commentX[0]->m_dt_create.isNull()); <span class="comment">// Not fetched</span>
|
||
qAssert(blogFromJsonFiltered->m_commentX[0]->m_text != <span class="string">""</span>); <span class="comment">// Fetched</span>
|
||
qAssert(blogFromJsonFiltered->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<T>(const T &
|
||
t) :</a> cr<63>ation d'une nouvelle instance identique <20> <i>t</i> de type
|
||
<i>std::shared_ptr<T></i> (par d<>faut type <20>gal <20> <i>boost::shared_ptr<T></i>,
|
||
si C++11 activ<69> alors type <20>gal <20> <i>std::shared_ptr<T></i>) ;
|
||
</li>
|
||
<li><a href="../doxygen/html/_qx_clone_8h.html"
|
||
target="_blank">qx::clone_to_boost_shared_ptr<T>(const T & t) :</a> cr<63>ation
|
||
d'une nouvelle instance identique <20> <i>t</i> de type <i>boost::shared_ptr<T></i> ;
|
||
</li>
|
||
<li><a href="../doxygen/html/_qx_clone_8h.html"
|
||
target="_blank">qx::clone_to_qt_shared_ptr<T>(const T & t) :</a> cr<63>ation d'une
|
||
nouvelle instance identique <20> <i>t</i> de type <i>QSharedPointer<T></i> ;</li>
|
||
<li><a href="../doxygen/html/_qx_clone_8h.html"
|
||
target="_blank">qx::clone_to_std_shared_ptr<T>(const T & t) :</a> cr<63>ation d'une
|
||
nouvelle instance identique <20> <i>t</i> de type <i>std::shared_ptr<T></i> ;</li>
|
||
<li><a href="../doxygen/html/_qx_clone_8h.html"
|
||
target="_blank">qx::clone_to_nude_ptr<T>(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">-></span>name<span class="operator"> =</span><span class="string"> "name1"</span><span class="operator">;</span>
|
||
d1<span class="operator">-></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<myClass> (smart-pointer)
|
||
</span>boost<span class="operator">::</span>shared_ptr<span class="operator"><</span>myClass<span class="operator">> *</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"><</span>myClass<span class="operator">></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"><</span>myClass<span class="operator">>());</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">-></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<blog>'
|
||
<boost.shared_ptr-blog- class_id="0" tracking_level="0" version="1">
|
||
<px class_id="1" tracking_level="1" version="0" object_id="_0">
|
||
<blog_id>113</blog_id>
|
||
<blog_text class_id="2" tracking_level="0" version="0">update blog_text_1</blog_text>
|
||
<date_creation class_id="3" tracking_level="0" version="0">20100409162612000</date_creation>
|
||
<author_id class_id="4" tracking_level="0" version="1">
|
||
<px class_id="5" tracking_level="1" version="0" object_id="_1">
|
||
<author_id>author_id_2</author_id>
|
||
<name>author_2</name>
|
||
<birthdate class_id="6" tracking_level="0" version="0">20100409</birthdate>
|
||
<sex>1</sex>
|
||
<list_blog class_id="7" tracking_level="0" version="0">
|
||
<count>0</count>
|
||
<item_version>1</item_version>
|
||
</list_blog>
|
||
</px>
|
||
</author_id>
|
||
<list_comment class_id="8" tracking_level="0" version="0">
|
||
<count>2</count>
|
||
<item class_id="9" tracking_level="0" version="1">
|
||
<px class_id="10" tracking_level="1" version="0" object_id="_2">
|
||
<comment_id>209</comment_id>
|
||
<comment_text>comment_1 text</comment_text>
|
||
<date_creation>20100409162612000</date_creation>
|
||
<blog_id>
|
||
<px class_id_reference="1" object_id="_3">
|
||
<blog_id>113</blog_id>
|
||
<blog_text></blog_text>
|
||
<date_creation></date_creation>
|
||
<author_id>
|
||
<px class_id="-1"></px>
|
||
</author_id>
|
||
<list_comment>
|
||
<count>0</count>
|
||
</list_comment>
|
||
<list_category class_id="11" tracking_level="0" version="0">
|
||
<count>0</count>
|
||
</list_category>
|
||
</px>
|
||
</blog_id>
|
||
</px>
|
||
</item>
|
||
<item>
|
||
<px class_id_reference="10" object_id="_4">
|
||
<comment_id>210</comment_id>
|
||
<comment_text>comment_2 text</comment_text>
|
||
<date_creation>20100409162612000</date_creation>
|
||
<blog_id>
|
||
<px class_id_reference="1" object_id="_5">
|
||
<blog_id>113</blog_id>
|
||
<blog_text></blog_text>
|
||
<date_creation></date_creation>
|
||
<author_id>
|
||
<px class_id="-1"></px>
|
||
</author_id>
|
||
<list_comment>
|
||
<count>0</count>
|
||
</list_comment>
|
||
<list_category>
|
||
<count>0</count>
|
||
</list_category>
|
||
</px>
|
||
</blog_id>
|
||
</px>
|
||
</item>
|
||
</list_comment>
|
||
<list_category>
|
||
<count>2</count>
|
||
<item class_id="12" tracking_level="0" version="0">
|
||
<first>355</first>
|
||
<second class_id="13" tracking_level="0" version="0">
|
||
<qt_shared_ptr class_id="14" tracking_level="1" version="0" object_id="_6">
|
||
<category_id>355</category_id>
|
||
<name>category_1</name>
|
||
<description>desc_1</description>
|
||
<list_blog class_id="15" tracking_level="0" version="0">
|
||
<count>0</count>
|
||
</list_blog>
|
||
</qt_shared_ptr>
|
||
</second>
|
||
</item>
|
||
<item>
|
||
<first>357</first>
|
||
<second>
|
||
<qt_shared_ptr class_id_reference="14" object_id="_7">
|
||
<category_id>357</category_id>
|
||
<name>category_3</name>
|
||
<description>desc_3</description>
|
||
<list_blog>
|
||
<count>0</count>
|
||
</list_blog>
|
||
</qt_shared_ptr>
|
||
</second>
|
||
</item>
|
||
</list_category>
|
||
</px>
|
||
</boost.shared_ptr-blog->
|
||
[QxOrm] end dump 'boost::shared_ptr<blog>'
|
||
</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<T>()</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<T>()</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<T>()</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">-></span>getValue<span class="operator"><</span>QString<span class="operator">>(</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">-></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">-></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">()) && (</span>sFirstName<span class="operator"> == (*</span> boost<span class="operator">::</span>any_cast<span class="operator"><</span>QString<span class="operator"> *>(</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<T>()</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">-></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">-></span>setValue<span class="operator"><</span>QString<span class="operator">>(</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<T>::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<T>::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<T>::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<T>::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<T>::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<T>::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<T>::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<T>::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"> &</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"> <></span><span class="type"> void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>person<span class="operator">> &</span> t<span class="operator">)
|
||
{</span>
|
||
t<span class="operator">.</span>id<span class="operator">(&</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">(&</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">(&</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">(&</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"><</span><span class="type">long</span><span class="operator">>(&</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"><</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"> &>(&</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"><</span><span class="type">double</span><span class="operator">,</span><span class="type"> long</span><span class="operator">>(&</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">, (&</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"><</span><span class="type">long</span><span class="operator">>(</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<qx::any>
|
||
</span> std<span class="operator">::</span>vector<span class="operator"><</span>boost<span class="operator">::</span>any<span class="operator">></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">, (&</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"><</span><span class="type">double</span><span class="operator">>(</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<T>(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"><</span>qx<span class="operator">::</span>service<span class="operator">::</span>IxService<span class="operator">>(</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"><</span>QString<span class="operator">,</span> IxClass<span class="operator"> *> *</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">-></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">-></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">()-></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">-></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">-></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"> <</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">-></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">-></span>getSqlRelation<span class="operator">();</span>
|
||
QString sInfos<span class="operator"> =</span> p<span class="operator">-></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">-></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">-></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">-></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<T></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"><</span>blog<span class="operator">></span> blog_ptr<span class="operator">;</span><span class="keyword">
|
||
typedef</span> qx<span class="operator">::</span>QxCollection<span class="operator"><</span><span class="type">long</span><span class="operator">,</span> blog_ptr<span class="operator">></span> list_of_blog<span class="operator">;</span><span class="keyword">
|
||
typedef</span> boost<span class="operator">::</span>shared_ptr<span class="operator"><</span>list_of_blog<span class="operator">></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"> //!< Id to fetch or delete
|
||
</span> blog_ptr instance<span class="operator">;</span><span class="comment"> //!< Single instance to fetch, insert, update, delete or validate
|
||
</span> list_of_blog_ptr list<span class="operator">;</span><span class="comment"> //!< List of instances to fetch, insert, update, delete or validate
|
||
</span> qx_query query<span class="operator">;</span><span class="comment"> //!< Query to execute when fetching, updating or deleting
|
||
</span> QStringList columns<span class="operator">;</span><span class="comment"> //!< List of columns to fetch or update
|
||
</span> QStringList relations<span class="operator">;</span><span class="comment"> //!< 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"><</span>services<span class="operator">::</span>blog_input<span class="operator">></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"> //!< Single instance from server
|
||
</span> list_of_blog_ptr list<span class="operator">;</span><span class="comment"> //!< List of instances from server
|
||
</span> QSqlError error<span class="operator">;</span><span class="comment"> //!< 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"> //!< Check if a single instance (or a list of instances) is valid
|
||
</span> qx_query query<span class="operator">;</span><span class="comment"> //!< Query which contains all results
|
||
</span><span class="type"> long</span> count<span class="operator">;</span><span class="comment"> //!< Count how many items in database using a query or not
|
||
</span> qx_bool exist<span class="operator">;</span><span class="comment"> //!< 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"><</span>services<span class="operator">::</span>blog_output<span class="operator">></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"> <></span><font style="background-color:yellow"><span class="type">
|
||
void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>services<span class="operator">::</span>blog_input<span class="operator">> &</span> t<span class="operator">)</span></font>
|
||
<span class="operator">{</span>
|
||
t<span class="operator">.</span>data<span class="operator">(&</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">(&</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">(&</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">(&</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">(&</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">(&</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"> <></span><font style="background-color:yellow"><span class="type">
|
||
void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>services<span class="operator">::</span>blog_output<span class="operator">> &</span> t<span class="operator">)</span></font>
|
||
<span class="operator">{</span>
|
||
t<span class="operator">.</span>data<span class="operator">(&</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">(&</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">(&</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">(&</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">(&</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">(&</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">(&</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<INPUT, OUTPUT></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<T></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"><</span> blog_input<span class="operator">,</span> blog_output<span class="operator"> ></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"> &</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
|
||
QSqlError fetchById<span class="operator">(</span>blog_ptr<span class="operator"> &</span> p<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
|
||
QSqlError fetchById<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &</span> lst<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
|
||
QSqlError fetchAll<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &</span> lst<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &</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"> &</span> query<span class="operator">,</span> list_of_blog_ptr<span class="operator"> &</span> lst<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span>
|
||
<span class="keyword"> const</span> QStringList<span class="operator"> &</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
|
||
|
||
QSqlError insert<span class="operator">(</span>blog_ptr<span class="operator"> &</span> p<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
|
||
QSqlError insert<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &</span> lst<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
|
||
QSqlError update<span class="operator">(</span>blog_ptr<span class="operator"> &</span> p<span class="operator">,</span><span class="keyword"> const</span> qx_query<span class="operator"> &</span> query<span class="operator"> =</span> qx_query<span class="operator">(),</span>
|
||
<span class="keyword"> const</span> QStringList<span class="operator"> &</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
|
||
QSqlError update<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &</span> lst<span class="operator">,</span><span class="keyword"> const</span> qx_query<span class="operator"> &</span> query<span class="operator"> =</span> qx_query<span class="operator">(),</span>
|
||
<span class="keyword"> const</span> QStringList<span class="operator"> &</span> columns<span class="operator"> =</span> QStringList<span class="operator">(),</span><span class="keyword"> const</span> QStringList<span class="operator"> &</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
|
||
QSqlError save<span class="operator">(</span>blog_ptr<span class="operator"> &</span> p<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &</span> relations<span class="operator"> =</span> QStringList<span class="operator">());</span>
|
||
QSqlError save<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &</span> lst<span class="operator">,</span><span class="keyword"> const</span> QStringList<span class="operator"> &</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"> &</span> p<span class="operator">);</span>
|
||
QSqlError deleteById<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &</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"> &</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"> &</span> p<span class="operator">);</span>
|
||
QSqlError destroyById<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &</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"> &</span> query<span class="operator">);</span>
|
||
|
||
QSqlError executeQuery<span class="operator">(</span>qx_query<span class="operator"> &</span> query<span class="operator">,</span> blog_ptr<span class="operator"> &</span> p<span class="operator">);</span>
|
||
QSqlError executeQuery<span class="operator">(</span>qx_query<span class="operator"> &</span> query<span class="operator">,</span> list_of_blog_ptr<span class="operator"> &</span> lst<span class="operator">);</span>
|
||
QSqlError callQuery<span class="operator">(</span>qx_query<span class="operator"> &</span> query<span class="operator">);</span>
|
||
qx_bool exist<span class="operator">(</span>blog_ptr<span class="operator"> &</span> p<span class="operator">);</span>
|
||
qx_bool exist<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &</span> lst<span class="operator">);</span>
|
||
QSqlError count<span class="operator">(</span><span class="type">long</span><span class="operator"> &</span> lCount<span class="operator">,</span><span class="keyword"> const</span> qx_query<span class="operator"> &</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"> &</span> p<span class="operator">);</span>
|
||
qx<span class="operator">::</span>QxInvalidValueX isValid<span class="operator">(</span>list_of_blog_ptr<span class="operator"> &</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"><</span>services<span class="operator">::</span>blog_services<span class="operator">></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"> <></span><span class="type">
|
||
void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>services<span class="operator">::</span>blog_services<span class="operator">> &</span> t<span class="operator">)</span></font>
|
||
<span class="operator">{</span>
|
||
t<span class="operator">.</span>fct_0<span class="operator"><</span><span class="type">void</span><span class="operator">>(&</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"><</span><span class="type">void</span><span class="operator">>(&</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"><</span><span class="type">void</span><span class="operator">>(&</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"><</span><span class="type">void</span><span class="operator">>(&</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"><</span><span class="type">void</span><span class="operator">>(&</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"><</span><span class="type">void</span><span class="operator">>(&</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"><</span><span class="type">void</span><span class="operator">>(&</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"><</span><span class="type">void</span><span class="operator">>(&</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"><</span><span class="type">void</span><span class="operator">>(&</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"><</span><span class="type">void</span><span class="operator">>(&</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"><</span><span class="type">void</span><span class="operator">>(&</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"><</span><span class="type">void</span><span class="operator">>(&</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"><</span><span class="type">void</span><span class="operator">>(&</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"><</span><span class="type">void</span><span class="operator">>(&</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"><</span><span class="type">void</span><span class="operator">>(&</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"><</span><span class="type">void</span><span class="operator">>(&</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"><</span><span class="type">void</span><span class="operator">>(&</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"><</span>ParameterAuthentication<span class="operator">></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"> <></span><span class="type">
|
||
void</span> register_class<span class="operator">(</span>QxClass<span class="operator"><</span>ParameterAuthentication<span class="operator">> &</span> t<span class="operator">)
|
||
{</span>
|
||
t<span class="operator">.</span>data<span class="operator">(&</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">(&</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">(&</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<INPUT, OUTPUT></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"> <</span><span class="keyword">class</span> INPUT<span class="operator">,</span><span class="keyword"> class</span> OUTPUT<span class="operator">></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"><</span>INPUT<span class="operator">,</span> OUTPUT<span class="operator">>
|
||
{</span><span class="keyword">
|
||
|
||
public</span><span class="operator">:</span>
|
||
|
||
ServiceAuthentication<span class="operator">(</span><span class="keyword">const</span> QString<span class="operator"> &</span> sServiceName<span class="operator">) :</span> qx<span class="operator">::</span>service<span class="operator">::</span>QxService<span class="operator"><</span>INPUT<span class="operator">,</span> OUTPUT<span class="operator">>(</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">-></span>login<span class="operator">,</span> pParams<span class="operator">-></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<INPUT, OUTPUT></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">-></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">-></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">-></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">-></span>getService<span class="operator">()-></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"><</span>MyClass<span class="operator">>();</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<T></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"><</span>blog<span class="operator">></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"> &</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"> &</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"> &</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"> &</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"> &</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"> &</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">-></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"><</span>QObject<span class="operator"> *>(</span>pChild<span class="operator">); }</span><span class="flow">
|
||
|
||
if</span><span class="operator"> ((</span>row<span class="operator"> <</span><span class="int"> 0</span><span class="operator">) || (</span>row<span class="operator"> >=</span><span class="keyword"> this</span><span class="operator">-></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">-></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">-></span>getblog_id<span class="operator">();</span>
|
||
blog<span class="operator">::</span>type_author value<span class="operator"> =</span> ptr<span class="operator">-></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">() && !</span> sAppendRelations<span class="operator">.</span>startsWith<span class="operator">(</span><span class="string">"->"</span><span class="operator">) && !</span> sAppendRelations<span class="operator">.</span>startsWith<span class="operator">(</span><span class="string">">>"</span><span class="operator">)) {</span> sRelation<span class="operator"> +=</span><span class="string"> "->"</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">-></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">-></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">-></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">-></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"><</span>QObject<span class="operator"> *>(</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"> &</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">-></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"><</span>QObject<span class="operator"> *>(</span>pChild<span class="operator">); }</span><span class="flow">
|
||
|
||
if</span><span class="operator"> ((</span>row<span class="operator"> <</span><span class="int"> 0</span><span class="operator">) || (</span>row<span class="operator"> >=</span><span class="keyword"> this</span><span class="operator">-></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">-></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">-></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">-></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">() && !</span> sAppendRelations<span class="operator">.</span>startsWith<span class="operator">(</span><span class="string">"->"</span><span class="operator">) && !</span> sAppendRelations<span class="operator">.</span>startsWith<span class="operator">(</span><span class="string">">>"</span><span class="operator">)) {</span> sRelation<span class="operator"> +=</span><span class="string"> "->"</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">-></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">-></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">-></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">-></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"><</span>QObject<span class="operator"> *>(</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"> &</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">-></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"><</span>QObject<span class="operator"> *>(</span>pChild<span class="operator">); }</span><span class="flow">
|
||
|
||
if</span><span class="operator"> ((</span>row<span class="operator"> <</span><span class="int"> 0</span><span class="operator">) || (</span>row<span class="operator"> >=</span><span class="keyword"> this</span><span class="operator">-></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">-></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">-></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">-></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">() && !</span> sAppendRelations<span class="operator">.</span>startsWith<span class="operator">(</span><span class="string">"->"</span><span class="operator">) && !</span> sAppendRelations<span class="operator">.</span>startsWith<span class="operator">(</span><span class="string">">>"</span><span class="operator">)) {</span> sRelation<span class="operator"> +=</span><span class="string"> "->"</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">-></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">-></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">-></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">-></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"><</span>QObject<span class="operator"> *>(</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"> &</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"> <</span><span class="int"> 0</span><span class="operator">) || (</span>row<span class="operator"> >=</span><span class="keyword"> this</span><span class="operator">-></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">-></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">-></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">-></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">-></span>setauthor<span class="operator">(</span>value<span class="operator">);
|
||
}</span>
|
||
|
||
pNestedModel<span class="operator"> =</span><span class="keyword"> this</span><span class="operator">-></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">-></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">-></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">-></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">-></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">-></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"> &</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">-></span>m_lstChild<span class="operator">.</span>count<span class="operator">() <=</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"> <</span><span class="keyword"> this</span><span class="operator">-></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">-></span>syncNestedModel<span class="operator">(</span><span class="keyword">static_cast</span><span class="operator"><</span><span class="type">int</span><span class="operator">>(</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"><</span>author<span class="operator">>();</span>
|
||
pModel<span class="operator">-></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">()-></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"><</span>author<span class="operator">>();</span>
|
||
pModel<span class="operator">-></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<T, S></a> (qui h<>rite de : <a
|
||
href="../doxygen/html/classqx_1_1_qx_model.html" target="_blank">qx::QxModel<T></a>
|
||
>> <a href="../doxygen/html/classqx_1_1_ix_model.html" target="_blank">qx::IxModel</a>
|
||
>> <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<T, S></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<blog> blog_ptr;
|
||
<span class="keyword">typedef</span> std::vector<blog_ptr> 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"><QxOrm_Impl.h></span>
|
||
|
||
QX_REGISTER_CPP_QX_BLOG(blog)
|
||
|
||
<span class="keyword">namespace</span> qx {
|
||
<span class="keyword">template</span> <> void register_class(QxClass<blog> & 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<author>();
|
||
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<author>();
|
||
<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<author> 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<author> 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<author>();
|
||
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<author> 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<author>(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<author>();</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<author>();
|
||
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<author> 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() << <span class="string">"date_creation"</span> << <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<int>(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() << <span class="string">"{ \"sex\" : " + QString::number(static_cast<int>(author::female)) + " }"</span>
|
||
<< <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<QVariantMap></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<QVariantMap></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<QVariantMap> 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<blog>();
|
||
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>*->*->*->*</i>) :
|
||
<br /><br />
|
||
<table border="1" bgcolor="#FFFFFF">
|
||
<col>
|
||
<tbody>
|
||
<tr>
|
||
<td>
|
||
<pre><span class="comment">// Fetch blog with many relations using "*->*->*->*" (4 levels of relationships)</span>
|
||
blog_ptr blog = std::make_shared<blog>();
|
||
blog->m_id = <span class="string">"id_blog_1"</span>;
|
||
daoError = qx::dao::fetch_by_id_with_relation(<span class="string">"*->*->*->*"</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>{ <col_1>, <col_2>, 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() << <span class="string">"{ blog_text }"</span> << <span class="string">"author_id { name, birthdate }"</span> << <span class="string">"list_comment { comment_text } -> blog_id -> *"</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>-{ <col_1>, <col_2>, 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() << <span class="string">"-{ blog_text }"</span> << <span class="string">"author_id -{ name, birthdate }"</span> << <span class="string">"list_comment -{ comment_text } -> blog_id -> *"</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> <> void register_class(QxClass<blog> & 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> <<span class="string">QtCore/qcoreapplication.h</span>>
|
||
<span class="keyword">#include</span> <<span class="string">QxOrm.h</span>>
|
||
|
||
<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->setPort(<span class="int">9642</span>); <span class="comment">// HTTP server listening port</span>
|
||
serverSettings->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->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<QSslCertificate></span> certCA; certCA << 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->setSSLEnabled(true);
|
||
serverSettings->setSSLCACertificates(certCA);
|
||
serverSettings->setSSLLocalCertificate(certServerPublic);
|
||
serverSettings->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<QSslError> 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 < 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><var_name:var_type></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/<blog_id:int></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/<var1>/<var2:int>"</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><var_name:{my_reg_exp}></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<QString, QVariant> & 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->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<QByteArray,
|
||
QVariant></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": [
|
||
"*->*"
|
||
]
|
||
}</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": [
|
||
"<blog_alias> { blog_text }",
|
||
"author_id <author_alias> { name, birthdate }",
|
||
"list_comment <list_comment_alias> { comment_text } -> blog_id <blog_alias_2> -> * <..._my_alias_suffix>"
|
||
],
|
||
"output_format": [
|
||
"{ blog_text }",
|
||
"author_id { name, birthdate }",
|
||
"list_comment { comment_text } -> blog_id -> *"
|
||
]
|
||
}</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 <author_alias> { name, birthdate }",
|
||
"list_comment <list_comment_alias> { 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 <author_alias> { 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> <> <span class="keyword">void</span> register_class(QxClass<blog> & t)
|
||
{
|
||
<span class="comment">// Register 'helloWorld()' static function in QxOrm context (can be called by QxRestApi JSON API module)</span>
|
||
t.fctStatic_1<<span class="type">QJsonValue</span>, const <span class="type">QJsonValue</span> & >(& 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<std::shared_ptr<blog>>",
|
||
"type_relation": "relation one-to-many"
|
||
}
|
||
],
|
||
"version": 0
|
||
},
|
||
{
|
||
"base_entity": "",
|
||
"description": "",
|
||
"entity_id": {
|
||
"description": "",
|
||
"key": "blog_id",
|
||
"type": "long"
|
||
},
|
||
"key": "blog",
|
||
"name": "blog",
|
||
"properties": [
|
||
{
|
||
"description": "",
|
||
"key": "blog_text",
|
||
"type": "QString"
|
||
},
|
||
{
|
||
"description": "",
|
||
"key": "date_creation",
|
||
"type": "QDateTime"
|
||
}
|
||
],
|
||
"relations": [
|
||
{
|
||
"description": "",
|
||
"key": "author_id",
|
||
"target": "author",
|
||
"type": "std::shared_ptr<author>",
|
||
"type_relation": "relation many-to-one"
|
||
},
|
||
{
|
||
"description": "",
|
||
"key": "list_comment",
|
||
"target": "comment",
|
||
"type": "QList<std::shared_ptr<comment>>",
|
||
"type_relation": "relation one-to-many"
|
||
},
|
||
{
|
||
"description": "",
|
||
"key": "list_category",
|
||
"target": "category",
|
||
"type": "qx::QxCollection<long, QSharedPointer<category>>",
|
||
"type_relation": "relation many-to-many"
|
||
}
|
||
],
|
||
"version": 0
|
||
},
|
||
{
|
||
"base_entity": "",
|
||
"description": "",
|
||
"entity_id": {
|
||
"description": "",
|
||
"key": "comment_id",
|
||
"type": "long"
|
||
},
|
||
"key": "comment",
|
||
"name": "comment",
|
||
"properties": [
|
||
{
|
||
"description": "",
|
||
"key": "comment_text",
|
||
"type": "QString"
|
||
},
|
||
{
|
||
"description": "",
|
||
"key": "date_creation",
|
||
"type": "QDateTime"
|
||
}
|
||
],
|
||
"relations": [
|
||
{
|
||
"description": "",
|
||
"key": "blog_id",
|
||
"target": "blog",
|
||
"type": "std::shared_ptr<blog>",
|
||
"type_relation": "relation many-to-one"
|
||
}
|
||
],
|
||
"version": 0
|
||
},
|
||
{
|
||
"base_entity": "",
|
||
"description": "",
|
||
"entity_id": {
|
||
"description": "",
|
||
"key": "category_id",
|
||
"type": "long"
|
||
},
|
||
"key": "category",
|
||
"name": "category",
|
||
"properties": [
|
||
{
|
||
"description": "",
|
||
"key": "name",
|
||
"type": "QString"
|
||
},
|
||
{
|
||
"description": "",
|
||
"key": "description",
|
||
"type": "QString"
|
||
}
|
||
],
|
||
"relations": [
|
||
{
|
||
"description": "",
|
||
"key": "list_blog",
|
||
"target": "blog",
|
||
"type": "qx::QxCollection<long, std::shared_ptr<blog>>",
|
||
"type_relation": "relation many-to-many"
|
||
}
|
||
],
|
||
"version": 0
|
||
}
|
||
]
|
||
},
|
||
"request_id": "842ed7b5-9b94-455f-86dc-32992866b3d5"
|
||
}</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<std::shared_ptr<blog>>",
|
||
"type_relation": "relation one-to-many"
|
||
}
|
||
],
|
||
"version": 0
|
||
},
|
||
{
|
||
"base_entity": "",
|
||
"description": "",
|
||
"entity_id": {
|
||
"description": "",
|
||
"key": "blog_id",
|
||
"type": "long"
|
||
},
|
||
"key": "blog",
|
||
"name": "blog",
|
||
"properties": [
|
||
{
|
||
"description": "",
|
||
"key": "blog_text",
|
||
"type": "QString"
|
||
},
|
||
{
|
||
"description": "",
|
||
"key": "date_creation",
|
||
"type": "QDateTime"
|
||
}
|
||
],
|
||
"relations": [
|
||
{
|
||
"description": "",
|
||
"key": "author_id",
|
||
"target": "author",
|
||
"type": "std::shared_ptr<author>",
|
||
"type_relation": "relation many-to-one"
|
||
},
|
||
{
|
||
"description": "",
|
||
"key": "list_comment",
|
||
"target": "comment",
|
||
"type": "QList<std::shared_ptr<comment>>",
|
||
"type_relation": "relation one-to-many"
|
||
},
|
||
{
|
||
"description": "",
|
||
"key": "list_category",
|
||
"target": "category",
|
||
"type": "qx::QxCollection<long, QSharedPointer<category>>",
|
||
"type_relation": "relation many-to-many"
|
||
}
|
||
],
|
||
"version": 0
|
||
},
|
||
{
|
||
"base_entity": "",
|
||
"description": "",
|
||
"entity_id": {
|
||
"description": "",
|
||
"key": "comment_id",
|
||
"type": "long"
|
||
},
|
||
"key": "comment",
|
||
"name": "comment",
|
||
"properties": [
|
||
{
|
||
"description": "",
|
||
"key": "comment_text",
|
||
"type": "QString"
|
||
},
|
||
{
|
||
"description": "",
|
||
"key": "date_creation",
|
||
"type": "QDateTime"
|
||
}
|
||
],
|
||
"relations": [
|
||
{
|
||
"description": "",
|
||
"key": "blog_id",
|
||
"target": "blog",
|
||
"type": "std::shared_ptr<blog>",
|
||
"type_relation": "relation many-to-one"
|
||
}
|
||
],
|
||
"version": 0
|
||
},
|
||
{
|
||
"base_entity": "",
|
||
"description": "",
|
||
"entity_id": {
|
||
"description": "",
|
||
"key": "category_id",
|
||
"type": "long"
|
||
},
|
||
"key": "category",
|
||
"name": "category",
|
||
"properties": [
|
||
{
|
||
"description": "",
|
||
"key": "name",
|
||
"type": "QString"
|
||
},
|
||
{
|
||
"description": "",
|
||
"key": "description",
|
||
"type": "QString"
|
||
}
|
||
],
|
||
"relations": [
|
||
{
|
||
"description": "",
|
||
"key": "list_blog",
|
||
"target": "blog",
|
||
"type": "qx::QxCollection<long, std::shared_ptr<blog>>",
|
||
"type_relation": "relation many-to-many"
|
||
}
|
||
],
|
||
"version": 0
|
||
}
|
||
]
|
||
},
|
||
"request_id": "53c96a23-2566-4b3d-ae6c-bff634600e79"
|
||
},
|
||
{
|
||
"data": [
|
||
{
|
||
"author_id": {
|
||
"author_id": "author_id_1",
|
||
"birthdate": null,
|
||
"list_blog": [],
|
||
"name": "",
|
||
"sex": 2
|
||
},
|
||
"blog_id": 1,
|
||
"blog_text": "save recursive blog from QxOrm REST API",
|
||
"date_creation": "2013-11-25T09:56:33",
|
||
"list_category": [],
|
||
"list_comment": []
|
||
},
|
||
{
|
||
"author_id": {
|
||
"author_id": "author_id_1",
|
||
"birthdate": null,
|
||
"list_blog": [],
|
||
"name": "",
|
||
"sex": 2
|
||
},
|
||
"blog_id": 5,
|
||
"blog_text": "save another blog from QxOrm REST API !",
|
||
"date_creation": "2016-06-12T08:33:12",
|
||
"list_category": [],
|
||
"list_comment": []
|
||
},
|
||
{
|
||
"author_id": {
|
||
"author_id": "author_id_save_recursive",
|
||
"birthdate": null,
|
||
"list_blog": [],
|
||
"name": "",
|
||
"sex": 2
|
||
},
|
||
"blog_id": 6,
|
||
"blog_text": "save recursive - new blog from QxOrm REST API",
|
||
"date_creation": "2013-11-25T09:56:33",
|
||
"list_category": [],
|
||
"list_comment": []
|
||
},
|
||
{
|
||
"author_id": {
|
||
"author_id": "author_id_save_recursive",
|
||
"birthdate": null,
|
||
"list_blog": [],
|
||
"name": "",
|
||
"sex": 2
|
||
},
|
||
"blog_id": 7,
|
||
"blog_text": "save recursive - new blog from QxOrm REST API",
|
||
"date_creation": "2013-11-25T09:56:33",
|
||
"list_category": [],
|
||
"list_comment": []
|
||
}
|
||
],
|
||
"request_id": "56e3ca99-5c12-4aca-aa6c-7d0e43c1e636"
|
||
},
|
||
{
|
||
"data": [
|
||
{
|
||
"key": 1,
|
||
"value": {
|
||
"author_id": {
|
||
"author_id": "author_id_1",
|
||
"birthdate": null,
|
||
"list_blog": [],
|
||
"name": "",
|
||
"sex": 2
|
||
},
|
||
"blog_id": 1,
|
||
"blog_text": "save recursive blog from QxOrm REST API",
|
||
"date_creation": "2013-11-25T09:56:33",
|
||
"list_category": [],
|
||
"list_comment": []
|
||
}
|
||
},
|
||
{
|
||
"key": 5,
|
||
"value": {
|
||
"author_id": {
|
||
"author_id": "author_id_1",
|
||
"birthdate": null,
|
||
"list_blog": [],
|
||
"name": "",
|
||
"sex": 2
|
||
},
|
||
"blog_id": 5,
|
||
"blog_text": "save another blog from QxOrm REST API !",
|
||
"date_creation": "2016-06-12T08:33:12",
|
||
"list_category": [],
|
||
"list_comment": []
|
||
}
|
||
},
|
||
{
|
||
"key": 6,
|
||
"value": {
|
||
"author_id": {
|
||
"author_id": "author_id_save_recursive",
|
||
"birthdate": null,
|
||
"list_blog": [],
|
||
"name": "",
|
||
"sex": 2
|
||
},
|
||
"blog_id": 6,
|
||
"blog_text": "save recursive - new blog from QxOrm REST API",
|
||
"date_creation": "2013-11-25T09:56:33",
|
||
"list_category": [],
|
||
"list_comment": []
|
||
}
|
||
},
|
||
{
|
||
"key": 7,
|
||
"value": {
|
||
"author_id": {
|
||
"author_id": "author_id_save_recursive",
|
||
"birthdate": null,
|
||
"list_blog": [],
|
||
"name": "",
|
||
"sex": 2
|
||
},
|
||
"blog_id": 7,
|
||
"blog_text": "save recursive - new blog from QxOrm REST API",
|
||
"date_creation": "2013-11-25T09:56:33",
|
||
"list_category": [],
|
||
"list_comment": []
|
||
}
|
||
}
|
||
],
|
||
"request_id": "692968e4-8885-41ad-b918-6ce2791b3bb8"
|
||
},
|
||
{
|
||
"data": [
|
||
{
|
||
"author_id": {
|
||
"author_id": "author_id_1",
|
||
"birthdate": "2019-04-02",
|
||
"list_blog": [
|
||
{
|
||
"author_id": {
|
||
"author_id": "author_id_1",
|
||
"birthdate": null,
|
||
"list_blog": [],
|
||
"name": "",
|
||
"sex": 2
|
||
},
|
||
"blog_id": 5,
|
||
"blog_text": "save another blog from QxOrm REST API !",
|
||
"date_creation": "2016-06-12T08:33:12",
|
||
"list_category": [],
|
||
"list_comment": []
|
||
},
|
||
{
|
||
"author_id": {
|
||
"author_id": "author_id_1",
|
||
"birthdate": null,
|
||
"list_blog": [],
|
||
"name": "",
|
||
"sex": 2
|
||
},
|
||
"blog_id": 1,
|
||
"blog_text": "save recursive blog from QxOrm REST API",
|
||
"date_creation": "2013-11-25T09:56:33",
|
||
"list_category": [],
|
||
"list_comment": []
|
||
}
|
||
],
|
||
"name": "author_1",
|
||
"sex": 0
|
||
},
|
||
"blog_id": 1,
|
||
"blog_text": "save recursive blog from QxOrm REST API",
|
||
"date_creation": "2013-11-25T09:56:33",
|
||
"list_category": [],
|
||
"list_comment": []
|
||
},
|
||
{
|
||
"author_id": {
|
||
"author_id": "author_id_1",
|
||
"birthdate": "2019-04-02",
|
||
"list_blog": [
|
||
{
|
||
"author_id": {
|
||
"author_id": "author_id_1",
|
||
"birthdate": null,
|
||
"list_blog": [],
|
||
"name": "",
|
||
"sex": 2
|
||
},
|
||
"blog_id": 5,
|
||
"blog_text": "save another blog from QxOrm REST API !",
|
||
"date_creation": "2016-06-12T08:33:12",
|
||
"list_category": [],
|
||
"list_comment": []
|
||
},
|
||
{
|
||
"author_id": {
|
||
"author_id": "author_id_1",
|
||
"birthdate": null,
|
||
"list_blog": [],
|
||
"name": "",
|
||
"sex": 2
|
||
},
|
||
"blog_id": 1,
|
||
"blog_text": "save recursive blog from QxOrm REST API",
|
||
"date_creation": "2013-11-25T09:56:33",
|
||
"list_category": [],
|
||
"list_comment": []
|
||
}
|
||
],
|
||
"name": "author_1",
|
||
"sex": 0
|
||
},
|
||
"blog_id": 5,
|
||
"blog_text": "save another blog from QxOrm REST API !",
|
||
"date_creation": "2016-06-12T08:33:12",
|
||
"list_category": [],
|
||
"list_comment": []
|
||
},
|
||
{
|
||
"author_id": {
|
||
"author_id": "author_id_save_recursive",
|
||
"birthdate": null,
|
||
"list_blog": [],
|
||
"name": "",
|
||
"sex": 2
|
||
},
|
||
"blog_id": 6,
|
||
"blog_text": "save recursive - new blog from QxOrm REST API",
|
||
"date_creation": "2013-11-25T09:56:33",
|
||
"list_category": [],
|
||
"list_comment": []
|
||
},
|
||
{
|
||
"author_id": {
|
||
"author_id": "author_id_save_recursive",
|
||
"birthdate": null,
|
||
"list_blog": [],
|
||
"name": "",
|
||
"sex": 2
|
||
},
|
||
"blog_id": 7,
|
||
"blog_text": "save recursive - new blog from QxOrm REST API",
|
||
"date_creation": "2013-11-25T09:56:33",
|
||
"list_category": [],
|
||
"list_comment": []
|
||
}
|
||
],
|
||
"request_id": "4ffe38a6-d642-44b0-8be1-198e84256321"
|
||
}
|
||
]</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> |