Developpez.com

Plus de 14 000 cours et tutoriels en informatique professionnelle à consulter, à télécharger ou à visionner en vidéo.

FAQ Qt FAQ Qt Creator FAQ PyQt & PySide

FAQ QtConsultez toutes les FAQ

Nombre d'auteurs : 26, nombre de questions : 298, dernière mise à jour : 6 mai 2017 

 
OuvrirSommaireBibliothèques complémentairesQxOrm

QxOrm est une bibliothèque C++ open source de gestion de données (object relational mapping, ORM) sous licence LGPL.

À partir d'une simple fonction de paramétrage (que l'on peut comparer avec un fichier de mapping XML Hibernate), vous aurez accès aux fonctionnalités suivantes :

  • persistance : communication avec de nombreuses bases de données (avec support des relations 1-1, 1-n, n-1 et n-n) ;
  • sérialisation des données (flux binaire et XML) ;
  • moteur de réflexion (ou introspection) pour accéder aux classes, attributs et invoquer des méthodes.

QxOrm est dépendant des excellentes bibliothèques boost (compatible à partir de la version 1.38) et Qt (compatible à partir de la version 4.5.0).

Mis à jour le 8 avril 2017  par Lionel Marty

Lien : Tutoriel présentant les fonctionnalités de base sur la notion de persistance

QxOrm utilise le moteur QtSql de Qt, basé sur un système de plug-ins. Une liste détaillée des bases de données supportées est disponible dans la documentation de Qt. Le plug-in ODBC (QODBC) assure une compatibilité avec de nombreuses bases de données. Pour des performances optimales, il est conseillé d'utiliser le plug-in spécifique à une base de données :

  • QMYSQL : MySQL ;
  • QPSQL : PostgreSQL (version 7.3 et supérieures) ;
  • QOCI : Oracle Call Interface Driver ;
  • QSQLITE : SQLite version 3 ;
  • QDB2 : IBM DB2 (version 7.1 et supérieures) ;
  • QIBASE : Borland InterBase ;
  • QTDS : Sybase Adaptive Server.
Créé le 11 juin 2012  par Lionel Marty

QxOrm utilise qmake de la bibliothèque Qt pour générer les makefile et compiler le projet. qmake est multiplateforme et fonctionne parfaitement sous Windows, Linux (Unix) et Mac. Pour compiler QxOrm, il suffit d'exécuter les commandes suivantes :

 
Sélectionnez
qmake
 make debug
 make release

Sous Windows, des fichiers *.vcproj et *.sln sont disponibles pour les EDI Visual C++ 2008 et Visual C++ 2010.

Les fichiers *.pro sont lisibles par Qt Creator et des plug-ins existent, permettant de s'interfacer avec de nombreux éditeurs C++.

Les fichiers mingw_build_all_debug.bat et mingw_build_all_release.bat présents dans le dossier ./tools/ permettent de compiler rapidement QxOrm ainsi que tous les tests avec le compilateur MinGW sous Windows.

Enfin, les fichiers gcc_build_all_debug.sh et gcc_build_all_release.sh, présents dans le dossier ./tools/, permettent de compiler rapidement QxOrm ainsi que tous les tests avec GCC sous Unix.

Remarque : suivant l'environnement de développement, il peut être nécessaire de modifier le fichier QxOrm.pri pour paramétrer la configuration de la bibliothèque boost :

 
Sélectionnez
QX_BOOST_INCLUDE_PATH = $$quote(D:/Dvlp/_Libs/Boost/1_42/include)
 QX_BOOST_LIB_PATH = $$quote(D:/Dvlp/_Libs/Boost/1_42/lib_shared)
 QX_BOOST_LIB_SERIALIZATION_DEBUG = "boost_serialization-vc90-mt-gd-1_42"
 QX_BOOST_LIB_SERIALIZATION_RELEASE = "boost_serialization-vc90-mt-1_42"
Créé le 11 juin 2012  par Lionel Marty

Si vous trouvez un bogue ou si vous avez une question concernant le fonctionnement de la bibliothèque QxOrm, vous pouvez retrouver la communauté française de QxOrm sur le forum de Developpez.com.

Un forum (en anglais) dédié à QxOrm est également disponible.

Enfin, il est possible d'envoyer un mail à .

Créé le 11 juin 2012  par Lionel Marty

QxOrm utilise les techniques de métaprogrammation C++ pour fournir une grande partie de ses fonctionnalités. Il n'est pas nécessaire de savoir utiliser la métaprogrammation pour travailler avec la bibliothèque QxOrm. En effet, QxOrm se veut simple d'utilisation et un code C++ écrit avec Qt et QxOrm est facile à lire, donc facile à développer et à maintenir.

Cependant, la métaprogrammation est coûteuse en temps de compilation. En utilisant un fichier d'en-tête précompilé, un projet C++ se compilera beaucoup plus vite. Un seul fichier d'en-tête est nécessaire pour disposer de l'ensemble des fonctionnalités de QxOrm : le fichier QxOrm.h.

Créé le 11 juin 2012  par Lionel Marty

Oui, si la sérialisation des données au format XML n'est pas utilisée dans le projet, vous pouvez désactiver cette fonctionnalité. Les temps de compilation seront alors réduits, mais vous n'aurez plus accès au namespace qx::serialization:xml. Pour désactiver la sérialisation XML, il faut ouvrir le fichier QxConfig.h et modifier la constante _QX_SERIALIZE_XML. Une recompilation de la bibliothèque QxOrm est nécessaire pour prendre en compte cette modification.

Une autre possibilité est d'utiliser les classes polymorphiques de la bibliothèque boost::serialization (à la place des classes template). Ainsi, au lieu d'utiliser de manière intensive la métaprogrammation, l'implémentation des classes polymorphiques est basée sur la notion d'héritage. Cette fonctionnalité réduit donc les temps de compilation ainsi que la taille de l'exécutable généré. En contrepartie, la vitesse d'exécution du programme sera réduite, puisqu'une partie du travail effectué lors de la compilation devra être réalisé à l'exécution de l'application. Pour activer cette fonctionnalité dans QxOrm, vous devez modifier la constante _QX_SERIALIZE_POLYMORPHIC du fichier QxConfig.h.

Attention : les fonctions de sérialisation seront alors accessibles depuis les namespace suivants : qx::serialization::polymorphic_binary, qx::serialization::polymorphic_text et qx::serialization::polymorphic_xml. Une recompilation de la bibliothèque QxOrm est nécessaire pour prendre en compte cette modification.

Créé le 11 juin 2012  par Lionel Marty

Le module QxMemLeak permet une détection rapide des fuites mémoire, en mode Debug, une fois l'exécution du programme terminée (avec indication du fichier et de la ligne, dans le style de la MFC de Microsoft).

Ce module a été développé par Wu Yongwei et a subi quelques modifications pour intégration à QxOrm. Si un autre outil est déjà utilisé (Valgrind, par exemple), cette fonctionnalité ne doit pas être activée.

Pour activer/désactiver le module QxMemLeak, il suffit de modifier la constante _QX_USE_MEM_LEAK_DETECTION définie dans le fichier QxConfig.h. Une recompilation de la bibliothèque QxOrm est nécessaire pour prendre en compte cette modification.

Tous les fichiers source *.cpp doivent inclure le fichier suivant pour profiter de la fonctionnalité : #include <QxMemLeak.h>. Le mode de fonctionnement du module QxMemLeak est de surcharger les opérateurs new et delete et d'utiliser les macros __FILE__ et __LINE__ (compatibles avec la plupart des compilateurs) afin de lister toutes les créations/destructions d'instances. Un compte-rendu est disponible à la fin de l'exécution du programme. Par exemple, pour une fuite mémoire détectée dans le fichier main.cpp à la ligne 74, les traces suivantes sont générées :

 
Sélectionnez
[QxOrm] Leaked object at 0xf52ad8 (size 16, src\main.cpp:74)
[QxOrm] **** 1 memory leaks found ****
Créé le 11 juin 2012  par Lionel Marty

QxOrm utilise le framework de sérialisation proposé par la bibliothèque boost. Plusieurs types de sérialisation sont disponibles : binaire, XML, texte. Le fichier QxConfig.h permet d'activer et désactiver les différents types de sérialisation.

Chaque type de sérialisation possède ses propres caractéristiques :
  • binaire : le plus petit et rapide, non portable ;
  • texte : plus gros et lent, portable ;
  • XML : le plus gros et le plus lent, portable.

Remarque : le type binary n'est pas portable, ce qui signifie que des données ne peuvent pas s'échanger entre un système Windows et un système Unix. Si vous devez faire transiter des données sur un réseau à travers plusieurs systèmes d'exploitation, il faut utiliser les types de sérialisation texte ou XML.

QxOrm propose également une autre solution : le type de sérialisation portable_binary. Le type portable_binary possède les mêmes caractéristiques que le type binary, mais permet d'échanger des données de manière portable. Cependant, ce type de sérialisation n'est pas proposé officiellement par la bibliothèque boost, il est donc nécessaire de tester avant de l'utiliser dans un logiciel en production.

Autre remarque : le mécanisme de sérialisation XML est lent car il nécessite de convertir toutes les propriétés d'une classe au format texte et de procéder à un parsing XML pour déchiffrer les informations. Voici un exemple de sérialisation XML d'une liste d'objets de type std::vector<T>, T ayant trois propriétés (id, name et desc) :

 
Sélectionnez
<std.vector-boost.shared_ptr-drug-- class_id="0" tracking_level="0" version="0">
	<count>3</count>
	<item_version>1</item_version>
	<item class_id="1" tracking_level="0" version="1">
		<px class_id="2" tracking_level="1" version="1" object_id="_0">
			<id>1</id>
			<name class_id="3" tracking_level="0" version="0">name1</name>
			<desc>desc1</desc>
		</px>
	</item>
	<item>
		<px class_id_reference="2" object_id="_1">
			<id>2</id>
			<name>name2 modified</name>
			<desc>desc2 modified</desc>
		</px>
	</item>
	<item>
		<px class_id_reference="2" object_id="_2">
			<id>3</id>
			<name>name3</name>
			<desc>desc3</desc>
		</px>
	</item>
</std.vector-boost.shared_ptr-drug-->
Créé le 11 juin 2012  par Lionel Marty

Si une classe est définie dans un espace de nom (namespace), alors une erreur de compilation se produit avec l'utilisation des macros QX_REGISTER_HPP… et QX_REGISTER_CPP…

Pour éviter ces erreurs de compilation, il est nécessaire d'utiliser les macros QX_REGISTER_COMPLEX_CLASS_NAME_HPP… et QX_REGISTER_COMPLEX_CLASS_NAME_CPP…

Les macros QX_REGISTER_COMPLEX_CLASS_NAME… nécessitent un paramètre supplémentaire (dans l'exemple ci-dessus il s'agit du paramètre qx_test_CPerson) afin de créer une variable globale. Celle-ci est appelée dès le lancement de l'application. La construction de cette instance globale déclare la classe dans le module QxFactory (modèle de conception fabrique ou design pattern factory). Un objet C++ ne pouvant pas se nommer avec des caractères "::", le paramètre supplémentaire de la macro permet de remplacer tous les "::" par des "_".

Vous trouverez un exemple d'utilisation dans le dossier ./test/qxDllSample/dll1/ de la distribution de QxOrm avec la classe CPerson définie dans l'espace de nom qx::test :

 
Sélectionnez
QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QX_DLL1(qx::test::CPerson, QObject, 0, qx_test_CPerson)
Créé le 11 juin 2012  par Lionel Marty

Il existe de nombreux conteneurs dans les bibliothèques stl, boost et Qt. Il est donc légitime de se demander à quoi sert qx::QxCollection<Key, Value>.

qx::QxCollection<Key, Value> est un nouveau conteneur (basé sur l'excellente bibliothèque boost::multi_index_container) qui possède les fonctionnalités suivantes :

  • conserve l'ordre d'insertion des éléments dans la liste ;
  • accès rapide à un élément par son index : équivaut à std::vector<T> ou QList<T>, par exemple ;
  • accès rapide à un élément par une clé (hash-map) : équivaut à QHash<Key, Value> ou boost::unordered_map<Key, Value>, par exemple ;
  • fonctions de tri sur le type Key et sur le type Value.

Remarque : qx::QxCollection<Key, Value> est compatible avec la macro foreach fournie par la bibliothèque Qt ainsi que par la macro BOOST_FOREACH, fournie par la bibliothèque boost. Cependant, chaque élément renvoyé par ces deux macros correspond à un objet de type std::pair<Key, Value>. Pour obtenir un résultat plus naturel et plus lisible, il est conseillé d'utiliser la macro _foreach : cette macro utilise BOOST_FOREACH pour tous les conteneurs sauf pour qx::QxCollection<Key, Value>. Dans ce cas, l'élément renvoyé correspond au type Value (voir l'exemple d'utilisation). La macro _foreach est donc compatible avec tous les conteneurs (stl, Qt, boost, etc.), puisqu'elle utilise la macro BOOST_FOREACH.

Autre remarque : qx::QxCollection<Key, Value> est particulièrement adaptée pour recevoir des données issues d'une base de données. En effet, ces données peuvent être triées (en utilisant ORDER BY dans une requête SQL, par exemple), il est donc important de conserver l'ordre d'insertion des éléments dans la liste. De plus, chaque donnée issue d'une base de données possède un identifiant unique. Il est donc intéressant de pouvoir accéder à un élément en fonction de cet identifiant unique de manière extrêmement rapide (hash-map).

Exemple d'utilisation de la collection qx::QxCollection<Key, Value> :

 
Sélectionnez
/* définition d'une classe 'produit' avec 3 propriétés : 'code', 'name', 'description' */
class produit { public: QString code; QString name; QString desc; };
 
/* pointeur intelligent associé à la classe 'produit' */
typedef boost::shared_ptr<produit> produit_ptr;
 
/* collection de produits (accès rapide à un élément de la collection par la propriété 'code') */
qx::QxCollection<QString, produit_ptr> lst;
 
/* création de 3 nouveaux produits */
produit_ptr d1; d1.reset(new produit()); d1->code = "code1"; d1->name = "name1"; d1->desc = "desc1";
produit_ptr d2; d2.reset(new produit()); d2->code = "code2"; d2->name = "name2"; d2->desc = "desc2";
produit_ptr d3; d3.reset(new produit()); d3->code = "code3"; d3->name = "name3"; d3->desc = "desc3";
 
/* insertion des 3 produits dans la collection */
lst.insert(d1->code, d1);
lst.insert(d2->code, d2);
lst.insert(d3->code, d3);
 
/* parcours de la collection en utilisant le mot-clé '_foreach' */
_foreach(produit_ptr p, lst)
{ qDebug() << qPrintable(p->name) << " " << qPrintable(p->desc); }
 
/* parcours de la collection en utilisant une boucle 'for' */
for (long l = 0; l < lst.count(); ++l)
{
   produit_ptr p = lst.getByIndex(l);
   QString code = lst.getKeyByIndex(l);
   qDebug() << qPrintable(p->name) << " " << qPrintable(p->desc);
}
 
/* parcours de la collection en utilisant le style Java avec 'QxCollectionIterator' */
qx::QxCollectionIterator<QString, produit_ptr> itr(lst);
while (itr.next())
{
   QString code = itr.key();
   qDebug() << qPrintable(itr.value()->name) << " " << qPrintable(itr.value()->desc);
}
 
/* effectue un tri croissant par clé (propriété 'code') et décroissant par valeur */
lst.sortByKey(true);
lst.sortByValue(false);
 
/* accès rapide à un produit par son 'code' */
produit_ptr p = lst.getByKey("code2");
 
/* accès rapide à un produit par son index (position) dans la collection */
produit_ptr p = lst.getByIndex(2);
 
/* teste si un produit existe dans la collection et si la liste est vide */
bool bExist = lst.exist("code3");
bool bEmpty = lst.empty();
 
/* supprime de la collection le 2e élément */
lst.removeByIndex(2);
 
/* supprime de la collection l'élément avec le code 'code3' */
lst.removeByKey("code3");
 
/* efface tous les éléments de la collection */
lst.clear();
Créé le 11 juin 2012  par Lionel Marty

QxOrm utilise de nombreuses fonctionnalités disponibles dans les excellentes bibliothèques boost et Qt. De plus, ces deux bibliothèques sont utilisées dans de nombreux projets, à la fois professionnels et open source. Il existe un grand nombre de forums, de tutoriels et toute une communauté pour répondre à toutes les problématiques que vous pourriez rencontrer. L'objectif de QxOrm n'est pas de redévelopper des fonctionnalités qui existent déjà, mais de fournir un outil performant d'accès aux bases de données, comme il en existe dans d'autres langages (Java avec Hibernate, .Net avec NHibernate, Ruby et ActiveRecord, Python et SQLAlchemy, PHP avec Doctrine, etc.).

boost : de nombreux modules de la bibliothèque boost font partie de la nouvelle norme C++. C'est une bibliothèque reconnue pour sa qualité, son code orienté C++ moderne, sa documentation, sa portabilité, etc. QxOrm utilise les fonctionnalités suivantes de boost : smart_pointer, serialization, type_traits, multi_index_container, unordered_container, any, tuple, foreach, function. Il est conseillé d'installer et d'utiliser la dernière version de boost.

Qt : bibliothèque complète : IHM (QtGui), réseau (QtNetwork), XML (QtXml), base de données (QtSql), etc. La documentation est excellente et le code C++ écrit à partir de cette bibliothèque est à la fois performant et simple de compréhension. Depuis le rachat par Nokia et sa nouvelle licence LGPL, Qt est, sans arrière-pensée, la bibliothèque phare du moment. QxOrm est compatible avec les principaux objets définis par Qt : QObject, QString, QDate, QTime, QDateTime, QList, QHash, QSharedPointer, QScopedPointer, etc. Il est conseillé d'installer et d'utiliser la dernière version de Qt.

Créé le 11 juin 2012  par Lionel Marty

QxOrm est compatible avec les pointeurs intelligents des bibliothèques boost et Qt.

Le pointeur intelligent développé par QxOrm est basé sur QSharedPointer et apporte de nouvelles fonctionnalités : s'il est utilisé avec les fonctions qx::dao::qx::dao::ptr<T>, il conserve automatiquement les valeurs issues de la base de données.

Il est ainsi possible de vérifier à tout moment si une instance d'objet a subi des modifications grâce à la méthode isDirty() : cette méthode peut renvoyer la liste de toutes les propriétés ayant été modifiées. qx::dao::ptr<T> peut également être utilisé par la fonction qx::dao::update_optimized() pour mettre à jour en base de données uniquement les champs modifiés. qx::dao::ptr<T> peut être utilisé avec un objet simple ou bien avec la plupart des containers : stl, boost, Qt et qx::QxCollection<Key, Value>.

Exemple d'utilisation du pointeur intelligent qx::dao::ptr<T> :
Sélectionnez
// exemple d'utilisation de la méthode 'isDirty()'
qx::dao::ptr<blog> blog_isdirty = qx::dao::ptr<blog>(new blog());
blog_isdirty->m_id = blog_1->m_id;
daoError = qx::dao::fetch_by_id(blog_isdirty);
qAssert(! daoError.isValid() && ! blog_isdirty.isDirty());
 
blog_isdirty->m_text = "blog property 'text' modified => blog is dirty !!!";
QStringList lstDiff; bool bDirty = blog_isdirty.isDirty(lstDiff);
qAssert(bDirty && (lstDiff.count() == 1) && (lstDiff.at(0) == "blog_text"));
if (bDirty) { qDebug("[QxOrm] test dirty 1 : blog is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
 
// met à jour uniquement la propriété 'm_text' de l'instance 'blog_isdirty'
daoError = qx::dao::update_optimized(blog_isdirty);
qAssert(! daoError.isValid() && ! blog_isdirty.isDirty());
qx::dump(blog_isdirty);
 
// exemple d'utilisation de la méthode 'isDirty()' avec une liste d'objets
typedef qx::dao::ptr< QList<author_ptr> > type_lst_author_test_is_dirty;
 
type_lst_author_test_is_dirty container_isdirty = type_lst_author_test_is_dirty(new QList<author_ptr>());
daoError = qx::dao::fetch_all(container_isdirty);
qAssert(! daoError.isValid() && ! container_isdirty.isDirty() && (container_isdirty->count() == 3));
 
author_ptr author_ptr_dirty = container_isdirty->at(1);
author_ptr_dirty->m_name = "author name modified at index 1 => container is dirty !!!";
bDirty = container_isdirty.isDirty(lstDiff);
qAssert(bDirty && (lstDiff.count() == 1));
if (bDirty) { qDebug("[QxOrm] test dirty 2 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
 
author_ptr_dirty = container_isdirty->at(2);
author_ptr_dirty->m_birthdate = QDate(1998, 03, 06);
bDirty = container_isdirty.isDirty(lstDiff);
qAssert(bDirty && (lstDiff.count() == 2));
if (bDirty) { qDebug("[QxOrm] test dirty 3 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
 
// met à jour la propriété 'm_name' en position 1, la propriété 'm_birthdate' en position 2 et ne change rien en position 0
daoError = qx::dao::update_optimized(container_isdirty);
qAssert(! daoError.isValid() && ! container_isdirty.isDirty());
qx::dump(container_isdirty);
 
// récupère uniquement la propriété 'm_dt_creation' du blog
QStringList lstColumns = QStringList() << "date_creation";
list_blog lst_blog_with_only_date_creation;
daoError = qx::dao::fetch_all(lst_blog_with_only_date_creation, NULL, lstColumns);
qAssert(! daoError.isValid() && (lst_blog_with_only_date_creation.size() > 0));
 
if ((lst_blog_with_only_date_creation.size() > 0) && (lst_blog_with_only_date_creation[0] != NULL))
{ qAssert(lst_blog_with_only_date_creation[0]->m_text.isEmpty()); }
 
qx::dump(lst_blog_with_only_date_creation);
Créé le 11 juin 2012  par Lionel Marty

QxOrm conseille fortement d'utiliser les pointeurs intelligents de boost ou Qt, car le langage C++ ne possède pas de garbage collector comme Java ou C#. L'utilisation des pointeurs intelligents simplifie énormément la gestion de la mémoire en C++. L'idéal dans un programme C++ est de n'avoir aucun appel à delete ou delete[]. De plus, les pointeurs intelligents font partie de la nouvelle norme C++, C++11. Il est donc essentiel aujourd'hui de connaître les classes suivantes :

  • shared_ptr, scoped_ptr et weak_ptr pour les pointeurs intelligents de la bibliothèque boost ;
  • QSharedPointer, QScopedPointer et QWeakPointer pour les pointeurs intelligents de la bibliothèque Qt.

Pour plus d'informations sur les pointeurs intelligents du framework Qt, regardez l'excellent article de Thiago Macieira et Harald Fernengel, traduit par Thibaut Cuvelier.

Créé le 11 juin 2012  par Lionel Marty

Les déclencheurs (triggers) de QxOrm permettent d'effectuer divers traitements avant et/ou après une insertion, une mise à jour ou bien une suppression dans la base de données.

Un exemple d'utilisation se trouve dans le dossier ./test/qxDllSample/dll2/ avec la classe BaseClassTrigger.

Cette classe contient cinq propriétés : m_id, m_dateCreation, m_dateModification, m_userCreation et m_userModification. Ces propriétés se mettront à jour automatiquement pour chaque classe héritant de BaseClassTrigger (cf. les classes Foo et Bar du même projet). Il est nécessaire de spécialiser le template QxDao_Trigger pour profiter de cette fonctionnalité.

 
Sélectionnez
#ifndef _QX_BASE_CLASS_TRIGGER_H_
#define _QX_BASE_CLASS_TRIGGER_H_
 
class QX_DLL2_EXPORT BaseClassTrigger
{
 
   QX_REGISTER_FRIEND_CLASS(BaseClassTrigger)
 
protected:
 
   long        m_id;
   QDateTime   m_dateCreation;
   QDateTime   m_dateModification;
   QString     m_userCreation;
   QString     m_userModification;
 
public:
 
   BaseClassTrigger() : m_id(0)  { ; }
   virtual ~BaseClassTrigger()   { ; }
 
   long getId() const                     { return m_id; }
   QDateTime getDateCreation() const      { return m_dateCreation; }
   QDateTime getDateModification() const  { return m_dateModification; }
   QString getUserCreation() const        { return m_userCreation; }
   QString getUserModification() const    { return m_userModification; }
 
   void setId(long l)                              { m_id = l; }
   void setDateCreation(const QDateTime & dt)      { m_dateCreation = dt; }
   void setDateModification(const QDateTime & dt)  { m_dateModification = dt; }
   void setUserCreation(const QString & s)         { m_userCreation = s; }
   void setUserModification(const QString & s)     { m_userModification = s; }
 
   void onBeforeInsert(qx::dao::detail::IxDao_Helper * dao);
   void onBeforeUpdate(qx::dao::detail::IxDao_Helper * dao);
 
};
 
QX_REGISTER_HPP_QX_DLL2(BaseClassTrigger, qx::trait::no_base_class_defined, 0)
 
namespace qx {
namespace dao {
namespace detail {
 
template <>
struct QxDao_Trigger<BaseClassTrigger>
{
 
   static inline void onBeforeInsert(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
   { if (t) { t->onBeforeInsert(dao); } }
   static inline void onBeforeUpdate(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
   { if (t) { t->onBeforeUpdate(dao); } }
   static inline void onBeforeDelete(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
   { Q_UNUSED(t); Q_UNUSED(dao); }
   static inline void onAfterInsert(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
   { Q_UNUSED(t); Q_UNUSED(dao); }
   static inline void onAfterUpdate(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
   { Q_UNUSED(t); Q_UNUSED(dao); }
   static inline void onAfterDelete(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
   { Q_UNUSED(t); Q_UNUSED(dao); }
 
};
 
} // namespace detail
} // namespace dao
} // namespace qx
 
#endif // _QX_BASE_CLASS_TRIGGER_H_
 
Sélectionnez
#include "../include/precompiled.h"
#include "../include/BaseClassTrigger.h"
#include <QxMemLeak.h>
 
QX_REGISTER_CPP_QX_DLL2(BaseClassTrigger)
 
namespace qx {
template <> void register_class(QxClass<BaseClassTrigger> & t)
{
   IxDataMember * pData = NULL;
 
   pData = t.id(& BaseClassTrigger::m_id, "id");
 
   pData = t.data(& BaseClassTrigger::m_dateCreation, "date_creation");
   pData = t.data(& BaseClassTrigger::m_dateModification, "date_modification");
   pData = t.data(& BaseClassTrigger::m_userCreation, "user_creation");
   pData = t.data(& BaseClassTrigger::m_userModification, "user_modification");
}}
 
void BaseClassTrigger::onBeforeInsert(qx::dao::detail::IxDao_Helper * dao)
{
   Q_UNUSED(dao);
   m_dateCreation = QDateTime::currentDateTime();
   m_dateModification = QDateTime::currentDateTime();
   m_userCreation = "current_user_1";
   m_userModification = "current_user_1";
}
 
void BaseClassTrigger::onBeforeUpdate(qx::dao::detail::IxDao_Helper * dao)
{
   Q_UNUSED(dao);
   m_dateModification = QDateTime::currentDateTime();
   m_userModification = "current_user_2";
}
Créé le 11 juin 2012  par Lionel Marty

QxOrm supporte la notion de 'multi-columns primary key'. L'identifiant de la classe doit être du type suivant :

  • QPair ou std::pair pour définir deux colonnes ;
  • boost::tuple pour définir de deux à neuf colonnes.

Il est nécessaire d'utiliser la macro QX_REGISTER_PRIMARY_KEY() pour spécialiser le template et ainsi définir le type d'identifiant sur plusieurs colonnes. La liste des noms des colonnes doit être de la forme suivante : "column1|column2|column3|etc.".

Exemple d'utilisation avec la classe author du projet qxBlogCompositeKey ; cette classe possède un identifiant sur trois colonnes :

 
Sélectionnez
#ifndef _QX_BLOG_AUTHOR_H_
#define _QX_BLOG_AUTHOR_H_
 
class blog;
 
class QX_BLOG_DLL_EXPORT author
{
 
   QX_REGISTER_FRIEND_CLASS(author)
 
public:
 
// -- clé composée (clé primaire définie sur plusieurs colonnes dans la base de données)
   typedef boost::tuple<QString, long, QString> type_composite_key;
   static QString str_composite_key() { return "author_id_0|author_id_1|author_id_2"; }
 
// -- typedef
   typedef boost::shared_ptr<blog> blog_ptr;
   typedef std::vector<blog_ptr> list_blog;
 
// -- enum
   enum enum_sex { male, female, unknown };
 
// -- propriétés
   type_composite_key   m_id;
   QString              m_name;
   QDate                m_birthdate;
   enum_sex             m_sex;
   list_blog            m_blogX;
 
// -- constructeur, destructeur virtuel
   author() : m_id("", 0, ""), m_sex(unknown) { ; }
   virtual ~author() { ; }
 
// -- méthodes
   int age() const;
 
// -- méthodes d'accès à la clé composée
   type_composite_key getId() const    { return m_id; }
   QString getId_0() const             { return boost::tuples::get<0>(m_id); }
   long getId_1() const                { return boost::tuples::get<1>(m_id); }
   QString getId_2() const             { return boost::tuples::get<2>(m_id); }
 
// -- méthodes de modification de la clé composée
   void setId_0(const QString & s)     { boost::tuples::get<0>(m_id) = s; }
   void setId_1(long l)                { boost::tuples::get<1>(m_id) = l; }
   void setId_2(const QString & s)     { boost::tuples::get<2>(m_id) = s; }
 
};
 
QX_REGISTER_PRIMARY_KEY(author, author::type_composite_key)
QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
 
typedef boost::shared_ptr<author> author_ptr;
typedef qx::QxCollection<author::type_composite_key, author_ptr> list_author;
 
#endif // _QX_BLOG_AUTHOR_H_
 
Sélectionnez
#include "../include/precompiled.h"
#include "../include/author.h"
#include "../include/blog.h"
#include <QxMemLeak.h>
 
QX_REGISTER_CPP_QX_BLOG(author)
 
namespace qx {
template <> void register_class(QxClass<author> & t)
{
   t.id(& author::m_id, author::str_composite_key());
 
   t.data(& author::m_name, "name");
   t.data(& author::m_birthdate, "birthdate");
   t.data(& author::m_sex, "sex");
 
   t.relationOneToMany(& author::m_blogX, blog::str_composite_key(), author::str_composite_key());
 
   t.fct_0<int>(& author::age, "age");
}}
 
int author::age() const
{
   if (! m_birthdate.isValid()) { return -1; }
   return (QDate::currentDate().year() - m_birthdate.year());
}
Créé le 11 juin 2012  par Lionel Marty

Par défaut, lorsqu'un mapping d'une classe C++ est écrit avec la méthode void qx::register_class<T>, l'identifiant associé à la classe est de type long (clé primaire avec auto-incrémentation dans la base de données).

Il est possible de définir un identifiant d'un autre type en utilisant la macro QX_REGISTER_PRIMARY_KEY. Cette macro spécialise le template qx::trait::get_primary_key<T>::type pour associer un type d'identifiant à une classe C++.

Par exemple, pour définir un identifiant unique de type QString pour la classe C++ myClass (mappée vers une table de la BDD avec une colonne de type VARCHAR pour clé primaire), il suffit d'écrire :

 
Sélectionnez
QX_REGISTER_PRIMARY_KEY(myClass, QString)

Voici un exemple d'utilisation de la macro QX_REGISTER_PRIMARY_KEY avec une classe author possédant un identifiant de type QString :

 
Sélectionnez
#ifndef _QX_BLOG_AUTHOR_H_
#define _QX_BLOG_AUTHOR_H_
 
class author
{
public:
// -- propriétés
   QString  m_id;
   QString  m_name;
// -- constructeur, destructeur virtuel
   author() { ; }
   virtual ~author() { ; }
};
 
QX_REGISTER_PRIMARY_KEY(author, QString)
QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
 
#endif // _QX_BLOG_AUTHOR_H_
Créé le 11 juin 2012  par Lionel Marty

Une classe abstraite C++ (contenant au moins une méthode virtuelle pure) ne peut pas être mappée avec une table d'une base de données (puisqu'elle ne peut pas être instanciée). Cependant, il peut être intéressant de définir une classe abstraite contenant une liste de propriétés utilisées par plusieurs objets persistants.

Un exemple de classe abstraite se trouve dans le dossier ./test/qxDllSample/dll2/ de la distribution de QxOrm avec la classe BaseClassTrigger.

QxOrm propose le mécanisme suivant pour définir une classe abstraite dans le contexte QxOrm :
  • déclarer la classe avec la méthode void register_class, comme n'importe quelle autre classe ;
  • utiliser la macro QX_REGISTER_ABSTRACT_CLASS(className) juste après la définition de la classe.
Créé le 11 juin 2012  par Lionel Marty

Une transaction est une suite d'opérations effectuées comme une seule unité logique de travail. Une fois terminée, la transaction est :

  • soit validée (commit), alors toutes les modifications sont faites dans la base de données ;
  • soit annulée (rollback), alors aucune modification n'est enregistrée.

La classe qx::QxSession de la bibliothèque QxOrm permet de gérer automatiquement les transactions (validation, annulation) en utilisant le mécanisme C++ RAII :

 
Sélectionnez
{ // Ouverture d'un scope  une session sera instanciée
 
  // Création d'une session : une connexion valide à la BDD est assignée à la session et une transaction est démarrée
  qx::QxSession session;
 
  // Exécution d'une série d'opérations avec la BDD (en utilisant l'opérateur += de la classe qx::QxSession et la connexion de la session)
  session += qx::dao::insert(my_object, session.database());
  session += qx::dao::update(my_object, session.database());
  session += qx::dao::fetch_by_id(my_object, session.database());
  session += qx::dao::delete_by_id(my_object, session.database());
 
  // Si la session n'est pas valide (donc une erreur s'est produite) => affichage de la 1re erreur de la session
  if (! session.isValid()) { qDebug("[QxOrm] session error : '%s'", qPrintable(session.firstError().text())); }
 
} // Fermeture du scope : la session est détruite (transaction => commit ou rollback automatique)

Remarque : une session peut déclencher une exception de type qx::dao::sql_error lorsqu'une erreur se produit (par défaut, aucune exception n'est déclenchée). Il est possible de paramétrer ce comportement en utilisant :

  • soit le constructeur de la classe qx::QxSession (pour une session en particulier) ;
  • soit le paramètre du singleton qx::QxSqlDatabase::getSingleton()->setSessionThrowable(bool b) (pour toutes les sessions).

Autre remarque : il est important de ne pas oublier de passer la connexion à la base de données de la session à chaque fonction qx::dao::... (en utilisant la méthode session.database()). De plus, il est possible d'initialiser une session avec sa propre connexion (provenant d'un pool de connexions, par exemple) en utilisant le constructeur de la classe qx::QxSession.

Créé le 11 juin 2012  par Lionel Marty

Une suppression logique permet de ne pas effacer de ligne dans une table d'une base de données (contrairement à une suppression physique) : une colonne supplémentaire est ajoutée à la définition de la table pour indiquer si la ligne est supprimée ou non. Cette colonne peut contenir soit un booléen (1 pour une ligne supprimée, 0 ou vide pour une ligne non supprimée), soit la date et l'heure de suppression de la ligne (si vide, la ligne est considérée comme non supprimée). Il est donc à tout moment possible de réactiver une ligne supprimée en réinitialisant la valeur à vide dans la table de la base de données.

Pour activer le mécanisme de suppression logique avec la bibliothèque QxOrm, il faut utiliser la classe qx::QxSoftDelete dans la fonction de mapping qx::register_class<T>. Voici un exemple d'utilisation avec une classe Bar contenant deux propriétés m_id et m_desc :

 
Sélectionnez
namespace qx {
template <> void register_class(QxClass<Bar> & t)
{
   t.setSoftDelete(qx::QxSoftDelete("deleted_at"));
 
   t.id(& Bar::m_id, "id");
   t.data(& Bar::m_desc, "desc");
}}

Les requêtes SQL générées automatiquement par la bibliothèque QxOrm vont prendre en compte ce paramètre de suppression logique pour ajouter les conditions nécessaires (ne pas récupérer les éléments supprimés, ne pas supprimer physiquement une ligne, etc.). Par exemple, si vous exécutez les lignes suivantes avec la classe Bar :

 
Sélectionnez
Bar_ptr pBar; pBar.reset(new Bar());
pBar->setId(5);
QSqlError daoError = qx::dao::delete_by_id(pBar);     qAssert(! daoError.isValid());
qx_bool bDaoExist = qx::dao::exist(pBar);             qAssert(! bDaoExist);
daoError = qx::dao::delete_all<Bar>();                qAssert(! daoError.isValid());
long lBarCount = qx::dao::count<Bar>();               qAssert(lBarCount == 0);
daoError = qx::dao::destroy_all<Bar>();               qAssert(! daoError.isValid());

Vous obtiendrez les traces suivantes :

 
Sélectionnez
[QxOrm] sql query (93 ms) : UPDATE Bar SET deleted_at = '20110617115148615' WHERE id = :id
[QxOrm] sql query (0 ms) : SELECT Bar.id AS Bar_id_0, Bar.deleted_at FROM Bar WHERE Bar.id = :id AND (Bar.deleted_at IS NULL OR Bar.deleted_at = '')
[QxOrm] sql query (78 ms) : UPDATE Bar SET deleted_at = '20110617115148724'
[QxOrm] sql query (0 ms) : SELECT COUNT(*) FROM Bar WHERE (Bar.deleted_at IS NULL OR Bar.deleted_at = '')
[QxOrm] sql query (110 ms) : DELETE FROM Bar

Remarque : pour supprimer physiquement une ligne de la base de données, il faut utiliser les fonctions : qx::dao::destroy_by_id() et qx::dao::destroy_all().

Créé le 11 juin 2012  par Lionel Marty

On retrouve généralement dans les différents outils de type ORM trois différentes stratégies pour gérer la notion d'héritage avec la base de données :

  • Single Table Inheritance (une seule table regroupant toutes les propriétés d'une hiérarchie d'héritage de classes) ;
  • Class Table Inheritance (à chaque classe d'une hiérarchie d'héritage est associée une table dans la base de données) ;
  • Concrete Table Inheritance (une table par classe concrète dans la hiérarchie d'héritage).

QxOrm utilise par défaut la stratégie Concrete Table Inheritance (les autres stratégies ne sont pas fonctionnelles à l'heure actuelle).

Un exemple d'utilisation avec une classe de base se trouve dans le dossier ./test/qxDllSample/dll2/ de la distribution de QxOrm avec la classe BaseClassTrigger.

Créé le 11 juin 2012  par Lionel Marty

QxOrm conseille d'utiliser la classe QString pour la gestion des chaînes de caractères.

Même si le framework boost fournit de nombreuses fonctionnalités avec son module boost::string_algo, la classe QString est plus simple à utiliser et surtout prend en charge automatiquement différents encodages : ASCII, UTF-8, UTF-16, etc.

Cependant, QxOrm est compatible avec std::string et std::wstring si vous préférez utiliser ce type de chaînes de caractères. Le type std::wstring permet de manipuler les chaînes de caractères Unicode. Pour plus d'informations sur la gestion des chaînes de caractères de la bibliothèque standard, rendez-vous sur la FAQ C++ de developpez.com.

Mis à jour le 22 avril 2017  par Lionel Marty
  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2006 - 2017 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.