In this chapter, we will see a lot of functionalities of QxOrm library with the creation of a C++ project : qxBlog - blog management in C++.
qxBlog tutorial step by step :
1- qxBlog project - blog management in C++ :
- blog : 1 blog is written by 1 author, can have many comment and can be put inside many category
- author : 1 author can write many blog
- comment : 1 comment belongs to 1 blog
- category : 1 category contains many blog
2- all files in qxBlog project :
./qxBlog/
./qxBlog/qxBlog.pro
./qxBlog/qxBlog.sqlite |
./qxBlog/include/precompiled.h
./qxBlog/include/export.h
./qxBlog/include/author.h
./qxBlog/include/blog.h
./qxBlog/include/category.h
./qxBlog/include/comment.h |
./qxBlog/src/author.cpp
./qxBlog/src/blog.cpp
./qxBlog/src/category.cpp
./qxBlog/src/comment.cpp
./qxBlog/src/main.cpp |
Note : tutorial source code is available in the folder
./test/qxBlog/ of your QxOrm directory.
qxBlog.sqlite file is the tutorial database in the format sqlite.
3- qxBlog.pro file :
This file is necessary to compile and build the project with the command qmake
provided by Qt library.
The tool qmake is multi-platform, so qxBlog project can be compiled under Windows, Linux, Mac, etc...
The file qxBlog.pro contains the list of all files in the project (header + source) and all dependencies
(QxOrm.pri file contains all dependencies with boost and Qt libraries).
qxBlog.pro contains an important constant (_BUILDING_QX_BLOG) to know if the project is compiling (cf. export.h and dll mechanism under Windows to import or export functions, classes...).

4- export.h file :
A Windows dll needs this file to manage 'export/import' of classes, functions...
QxOrm uses the same mechanism to provide some functionalities :
so the file export.h is necessary to all projects using QxOrm library.
Note : quick resume of dll mechanism under Windows :
- when a dll is compiling, each class is exported
- when another dll is compiling, each class of the first dll needs to be imported to be used
#ifndef _QX_BLOG_EXPORT_H_
#define _QX_BLOG_EXPORT_H_
#ifdef _BUILDING_QX_BLOG
#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_EXPORT_DLL
#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_EXPORT_DLL
#else
#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_IMPORT_DLL
#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_IMPORT_DLL
#endif
#ifdef Q_OS_WIN
#define QX_BLOG_DLL_EXPORT __declspec(dllexport)
#define QX_BLOG_DLL_IMPORT __declspec(dllimport)
#else
#define QX_BLOG_DLL_EXPORT
#define QX_BLOG_DLL_IMPORT
#endif
#ifdef Q_OS_WIN
#ifndef _BUILDING_QX_BLOG
#undef QX_BLOG_DLL_EXPORT
#define QX_BLOG_DLL_EXPORT QX_BLOG_DLL_IMPORT
#endif
#endif
#endif
|
5- precompiled.h file :
precompiled header file reduces compilation times of a C++ project.
QxOrm uses meta-programming concept to provide a lot of functionalities.
meta-programming is costly in compilation times, so your project will be compiled much more quickly with the file
precompiled.h.
Last but not least, another advantage is that the file QxOrm.h includes the basic functionalities of libraries boost and Qt.
It is thus not necessary anymore to write #include <QtCore/QString.h> to use the class QString of Qt for example.
In the same way, there is no need anymore to write #include <boost/shared_ptr.hpp> to use smart pointers of boost library.
#ifndef _QX_BLOG_PRECOMPILED_HEADER_H_
#define _QX_BLOG_PRECOMPILED_HEADER_H_
#include <QxOrm.h>
#include "export.h"
#endif
|
6- author.h and author.cpp (relationship one-to-many) :
1 author can write many blog
: we will see how to use relationship one-to-many.
In the database, there is 2 tables :

In the C++ source code, the properties of author class belong to columns of author table in the database.
So 1 instance of author class in the C++ source code belong to 1 row of author table in the database.
This mechanism provides C++ source code easy to develop and to maintain.
We add 1 method to our author class : int age() to retrieve the age with the data returned by the database.
We define also 2 typedef : a smart-pointer to an object author, and a list of author.
author class need an id of QString type (by default, QxOrm provides id of long type) : we use the macro QX_REGISTER_PRIMARY_KEY(author, QString) to specialize the template.
#ifndef _QX_BLOG_AUTHOR_H_
#define _QX_BLOG_AUTHOR_H_
class blog;
class QX_BLOG_DLL_EXPORT author
{
public: typedef boost::shared_ptr<blog> blog_ptr;
typedef std::vector<blog_ptr> list_blog; enum enum_sex { male, female, unknown }; QString m_id;
QString m_name;
QDate m_birthdate;
enum_sex m_sex;
list_blog m_blogX; author() : m_id(0), m_sex(unknown) { ; }
virtual ~author() { ; } int age() const;
};
QX_REGISTER_PRIMARY_KEY(author, QString)
QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
typedef boost::shared_ptr<author> author_ptr;
typedef qx::QxCollection<QString, author_ptr> list_author;
#endif
|
#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_id");
t.data(& author::m_name, "name");
t.data(& author::m_birthdate, "birthdate");
t.data(& author::m_sex, "sex");
t.relationOneToMany(& author::m_blogX, "list_blog", "author_id");
t.fct_0<int>(& author::age, "age");
}}
int author::age() const
{
if (! m_birthdate.isValid()) { return -1; }
return (QDate::currentDate().year() - m_birthdate.year());
}
|
7- comment.h and comment.cpp (relationship many-to-one) :
1 comment belongs to 1 blog and 1 blog can contain many comment : we will see how to use relationship many-to-one.
In the database, there is 2 tables :

Like author class, we define 2 typedef : a smart-pointer to a comment object and a list of comment.
#ifndef _QX_BLOG_COMMENT_H_
#define _QX_BLOG_COMMENT_H_
class blog;
class QX_BLOG_DLL_EXPORT comment
{
public: typedef boost::shared_ptr<blog> blog_ptr; long m_id;
QString m_text;
QDateTime m_dt_create;
blog_ptr m_blog; comment() : m_id(0) { ; }
virtual ~comment() { ; }
};
QX_REGISTER_HPP_QX_BLOG(comment, qx::trait::no_base_class_defined, 0)
typedef boost::shared_ptr<comment> comment_ptr;
typedef QList<comment_ptr> list_comment;
#endif
|
#include "../include/precompiled.h"
#include "../include/comment.h"
#include "../include/blog.h"
#include <QxMemLeak.h>
QX_REGISTER_CPP_QX_BLOG(comment)
namespace qx {
template <> void register_class(QxClass<comment> & t)
{
t.id(& comment::m_id, "comment_id");
t.data(& comment::m_text, "comment_text");
t.data(& comment::m_dt_create, "date_creation");
t.relationManyToOne(& comment::m_blog, "blog_id");
}}
|
8- category.h and category.cpp (relationship many-to-many) :
1 category contains many blog and 1 blog can be put inside many category :
we will see how to use relationship many-to-many.
This kind of relationship need a new table in the database to save the list of id for each relationship.
So in the database, there is 3 tables :

Like author class and comment class, we define 2 typedef :
a smart-pointer to a category object and a list of category.
#ifndef _QX_BLOG_CATEGORY_H_
#define _QX_BLOG_CATEGORY_H_
class blog;
class QX_BLOG_DLL_EXPORT category
{
public: typedef boost::shared_ptr<blog> blog_ptr;
typedef qx::QxCollection<long, blog_ptr> list_blog; long m_id;
QString m_name;
QString m_desc;
list_blog m_blogX; category() : m_id(0) { ; }
virtual ~category() { ; }
};
QX_REGISTER_HPP_QX_BLOG(category, qx::trait::no_base_class_defined, 0)
typedef QSharedPointer<category> category_ptr;
typedef qx::QxCollection<long, category_ptr> list_category;
#endif
|
#include "../include/precompiled.h"
#include "../include/category.h"
#include "../include/blog.h"
#include <QxMemLeak.h>
QX_REGISTER_CPP_QX_BLOG(category)
namespace qx {
template <> void register_class(QxClass<category> & t)
{
t.id(& category::m_id, "category_id");
t.data(& category::m_name, "name");
t.data(& category::m_desc, "description");
t.relationManyToMany(& category::m_blogX, "list_blog", "category_blog", "category_id", "blog_id");
}}
|
9- blog.h and blog.cpp (relationship one-to-many, many-to-one and many-to-many) :
1 blog is written by 1 author, can have many
comment and can be put inside many category.
So this class has 3 relationships : one-to-many,
many-to-one et many-to-many.
Like other classes, we define 2 typedef :
a smart-pointer to a blog object and a list of blog.
#ifndef _QX_BLOG_BLOG_H_
#define _QX_BLOG_BLOG_H_
#include "author.h"
#include "comment.h"
#include "category.h"
class QX_BLOG_DLL_EXPORT blog
{
public: long m_id;
QString m_text;
QDateTime m_dt_creation;
author_ptr m_author;
list_comment m_commentX;
list_category m_categoryX; blog() : m_id(0) { ; }
virtual ~blog() { ; }
};
QX_REGISTER_HPP_QX_BLOG(blog, qx::trait::no_base_class_defined, 0)
typedef boost::shared_ptr<blog> blog_ptr;
typedef std::vector<blog_ptr> list_blog;
#endif
|
#include "../include/precompiled.h"
#include "../include/blog.h"
#include <QxMemLeak.h>
QX_REGISTER_CPP_QX_BLOG(blog)
namespace qx {
template <> void register_class(QxClass<blog> & t)
{
t.id(& blog::m_id, "blog_id");
t.data(& blog::m_text, "blog_text");
t.data(& blog::m_dt_creation, "date_creation");
t.relationManyToOne(& blog::m_author, "author_id");
t.relationOneToMany(& blog::m_commentX, "list_comment", "blog_id");
t.relationManyToMany(& blog::m_categoryX, "list_category", "category_blog", "blog_id", "category_id");
}}
|
10- main.cpp file :
QxOrm can communicate with many databases (see the list of databases in Qt web site)
=> persistence.
QxOrm provides also 2 other important functionalities :
- serialization : binary and xml format
- reflection : access to classes definitions, retrieve properties and call classes methods
#include "../include/precompiled.h"
#include <QtGui/qapplication.h>
#include "../include/blog.h"
#include "../include/author.h"
#include "../include/comment.h"
#include "../include/category.h"
#include <QxMemLeak.h>
int main(int argc, char * argv[])
{ QApplication app(argc, argv); qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE");
qx::QxSqlDatabase::getSingleton()->setDatabaseName("./qxBlog.sqlite");
qx::QxSqlDatabase::getSingleton()->setHostName("localhost");
qx::QxSqlDatabase::getSingleton()->setUserName("root");
qx::QxSqlDatabase::getSingleton()->setPassword(""); QSqlError daoError = qx::dao::delete_all<author>();
daoError = qx::dao::delete_all<comment>();
daoError = qx::dao::delete_all<category>();
daoError = qx::dao::delete_all<blog>(); author_ptr author_1; author_1.reset(new author());
author_ptr author_2; author_2.reset(new author());
author_ptr author_3; author_3.reset(new author());
author_1->m_id = "author_id_1"; author_1->m_name = "author_1";
author_1->m_sex = author::male; author_1->m_birthdate = QDate::currentDate();
author_2->m_id = "author_id_2"; author_2->m_name = "author_2";
author_2->m_sex = author::female; author_2->m_birthdate = QDate::currentDate();
author_3->m_id = "author_id_3"; author_3->m_name = "author_3";
author_3->m_sex = author::female; author_3->m_birthdate = QDate::currentDate();
list_author authorX;
authorX.insert(author_1->m_id, author_1);
authorX.insert(author_2->m_id, author_2);
authorX.insert(author_3->m_id, author_3); daoError = qx::dao::insert(authorX);
qAssert(qx::dao::count<author>() == 3); author_ptr author_clone = qx::clone(* author_2);
qAssert(author_clone->m_id == "author_id_2");
qAssert(author_clone->m_sex == author::female); qx::QxSqlQuery query("WHERE author.sex = :sex");
query.bind(":sex", author::female);
list_author list_of_female_author;
daoError = qx::dao::fetch_by_query(query, list_of_female_author);
qAssert(list_of_female_author.count() == 2); qx::dump(list_of_female_author); category_ptr category_1 = category_ptr(new category());
category_ptr category_2 = category_ptr(new category());
category_ptr category_3 = category_ptr(new category());
category_1->m_name = "category_1"; category_1->m_desc = "desc_1";
category_2->m_name = "category_2"; category_2->m_desc = "desc_2";
category_3->m_name = "category_3"; category_3->m_desc = "desc_3";
{ QSqlDatabase db = qx::QxSqlDatabase::getDatabase();
bool bCommit = db.transaction(); daoError = qx::dao::insert(category_1, (& db)); bCommit = (bCommit && ! daoError.isValid());
daoError = qx::dao::insert(category_2, (& db)); bCommit = (bCommit && ! daoError.isValid());
daoError = qx::dao::insert(category_3, (& db)); bCommit = (bCommit && ! daoError.isValid());
qAssert(bCommit);
qAssert(category_1->m_id != 0);
qAssert(category_2->m_id != 0);
qAssert(category_3->m_id != 0); if (bCommit) { db.commit(); }
else { db.rollback(); }
} boost::any blog_any = qx::create("blog");
blog_ptr blog_1 = boost::any_cast<blog_ptr>(blog_any);
blog_1->m_text = "blog_text_1";
blog_1->m_dt_creation = QDateTime::currentDateTime();
blog_1->m_author = author_1; daoError = qx::dao::save(blog_1); blog_1->m_text = "update blog_text_1";
blog_1->m_author = author_2;
daoError = qx::dao::save(blog_1); comment_ptr comment_1; comment_1.reset(new comment());
comment_ptr comment_2; comment_2.reset(new comment());
comment_1->m_text = "comment_1 text";
comment_1->m_dt_create = QDateTime::currentDateTime();
comment_1->m_blog = blog_1;
comment_2->m_text = "comment_2 text";
comment_2->m_dt_create = QDateTime::currentDateTime();
comment_2->m_blog = blog_1;
daoError = qx::dao::insert(comment_1);
daoError = qx::dao::insert(comment_2);
qAssert(qx::dao::count<comment>() == 2); blog_1->m_categoryX.insert(category_1->m_id, category_1);
blog_1->m_categoryX.insert(category_3->m_id, category_3);
daoError = qx::dao::save_with_relation("list_category", blog_1); blog_ptr blog_tmp; blog_tmp.reset(new blog());
blog_tmp->m_id = blog_1->m_id;
daoError = qx::dao::fetch_by_id_with_all_relation(blog_tmp);
qAssert(blog_tmp->m_commentX.count() == 2);
qAssert(blog_tmp->m_categoryX.count() == 2);
qAssert(blog_tmp->m_text == "update blog_text_1");
qAssert(blog_tmp->m_author && blog_tmp->m_author->m_id == "author_id_2"); qx::dump(blog_tmp); qx_bool bInvokeOk = qx::QxClassX::invoke("author", "age", author_1);
qAssert(bInvokeOk);
return 0;
}
|
Click here to see output debug trace after execution of qxBlog program...
Important note : QxOrm doesn't want to hide sql query (by default, all sql queries are displayed).
QxOrm cannot resolve all problems with sql and databases,
so it is sometimes necessary to use QtSql engine of Qt library
to write your own sql query or stored procedure.
11- relationship one-to-one with person class :
QxOrm can also be used with relationship one-to-one.
We add to our project the class person (person.h and
person.cpp files) : 1 person is 1 author.
So person and author share the same id in the database : this is a relationship one-to-one.
There is 2 tables in the database :

Note : we add to person table the column mother_id. So we
can retrieve the mother (of type person) belongs to 1 person :
this is a relationship many-to-one on the same table person.
Moreover, if 1 person is a mother, we can retrieve the list of childs (of type person) :
this is a relationship one-to-many on the same table person.
|