QxOrm

Introduction

QxOrm is a C++ library designed to provide Object Relational Mapping (ORM) feature to C++ users.
With a simple C++ setting function by class (like Hibernate xml mapping file), you will have access to the following features :
  • persistence  : communication with a lot of databases (with 1-1, 1-n, n-1 and n-n relationships)
  • serialization : binary and xml format
  • reflection : access to classes definitions, retrieve properties and call classes methods
QxOrm aims to simplify C++ development and provides a lot of functionalities.
Here is a list of advantages of QxOrm :
  • non intrusive : the C++ setting function doesn't modified class definition, QxOrm can be used in existing projects
  • no code generation
  • no xml mapping file
  • classes doesn't need to inherit from a 'super object'
  • template meta-programming : no macro hack
  • works with Visual C++ 2008 or 2010 on Windows, GCC 4.4.1 on Linux and MinGW on Windows (other platforms will be tested soon : Mac, phones...)
  • only one file <QxOrm.h> to include in precompiled-header (need precompiled-header file to reduce compilation times)

Before you install and compile QxOrm, you need the following libraries : boost (from version 1.38) and Qt (from version 4.5.0).

 

Using the code

QxOrm library uses the following syntax for C++ source code :
  • all classes, functions, properties, etc... are defined under namespace qx
  • all macros of QxOrm library start with QX_...
  • all abstracts classes (or interfaces) start with Ix (for example IxFactory is an interface to create an instance of object)
  • other classes start with Qx (for example QxDataMember)
  • containers of objects end with X (for example QxDataMemberX is a list of QxDataMember)
  • functions to interact with databases are under namespace qx::dao (for example qx::dao::fetch_by_id())
  • functions to serialize are under namespace qx::serialization (for example qx::serialization::xml::to_file())
  • the reflection engine can be used with qx::QxClassX (for example qx::QxClassX::invoke() to call a class method)
  • all traits classes are under namespace qx::trait (for example qx::trait::is_smart_ptr<T>)

 

Quick sample

Quick sample step by step :

* -----------------------------------------------------------------------------------------------------
* 1- drug.h file : drug class definition with 3 properties : id, name and description
* -----------------------------------------------------------------------------------------------------

#ifndef _CLASS_DRUG_H_
#define _CLASS_DRUG_H_

class drug
{
public:
   long id;
   QString name;
   QString description;

   drug() : id(0) { ; }
   virtual ~drug() { ; }
};

QX_REGISTER_HPP_MY_TEST_EXE(drug, qx::trait::no_base_class_defined, 1)

/* This macro is necessary to register 'drug' class in QxOrm context */
/* param 1 : the current class to register => 'drug' */
/* 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 to provide 'ascendant compatibility' */

#endif // _CLASS_DRUG_H_

* ----------------------------------------------------------------------------------------------------
* 2- drug.cpp file : 'setting function' implementation : void qx::register_class()
* ----------------------------------------------------------------------------------------------------

#include "precompiled.h"   // Precompiled-header with '#include <QxOrm.h>' and '#include "export.h"'
#include "drug.h"          // Class definition 'drug'
#include <QxMemLeak.h>     // Automatic memory leak detection

QX_REGISTER_CPP_MY_TEST_EXE(drug)   // This macro is necessary to register 'drug' class in QxOrm context

namespace qx {
template <> void register_class(QxClass<drug> & t)
{
  t.id(& drug::id, "id");               // Register 'drug::id' <=> primary key in your database
  t.data(& drug::name, "name", 1);      // Register 'drug::name' property with key 'name' and version '1'
  t.data(& drug::description, "desc");  // Register 'drug::description' property with key 'desc'
}}

* -----------------------------------------------------------------------------------------------
* 3- main.cpp file : basic functionalities of QxOrm library with drug class
* -----------------------------------------------------------------------------------------------

#include "precompiled.h"
#include "drug.h"
#include <QxMemLeak.h>

int main(int argc, char * argv[])
{
   QApplication app(argc, argv); // Qt application

   // Create 3 new drugs
   // It is possible to use 'boost' and 'Qt' smart pointer : 'boost::shared_ptr', 'QSharedPointer', etc...
   typedef boost::shared_ptr<drug> drug_ptr;
   drug_ptr d1; d1.reset(new drug()); d1->name = "name1"; d1->description = "desc1";
   drug_ptr d2; d2.reset(new drug()); d2->name = "name2"; d2->description = "desc2";
   drug_ptr d3; d3.reset(new drug()); d3->name = "name3"; d3->description = "desc3";

   // Insert drugs into container
   // It is possible to use a lot of containers from 'std', 'boost', 'Qt' and 'qx::QxCollection<Key, Value>'
   typedef std::vector<drug_ptr> type_lst_drug;
   type_lst_drug lst_drug;
   lst_drug.push_back(d1);
   lst_drug.push_back(d2);
   lst_drug.push_back(d3);

   // Init parameters to communicate with a database
   qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE");
   qx::QxSqlDatabase::getSingleton()->setDatabaseName("./test_qxorm.db");
   qx::QxSqlDatabase::getSingleton()->setHostName("localhost");
   qx::QxSqlDatabase::getSingleton()->setUserName("root");
   qx::QxSqlDatabase::getSingleton()->setPassword("");

   // Create table 'drug' into database to store drugs
   QSqlError daoError = qx::dao::create_table<drug>();

   // Insert drugs from container to database
   // 'id' property of 'd1', 'd2' and 'd3' are auto-updated
   daoError = qx::dao::insert(lst_drug);

   // Modify and update the second drug into database
   d2->name = "name2 modified";
   d2->description = "desc2 modified";
   daoError = qx::dao::update(d2);

   // Delete the first drug from database
   daoError = qx::dao::delete_by_id(d1);

   // Count drugs into database
   long lDrugCount = qx::dao::count<drug>();

   // Fetch drug with id '3' into a new variable
   drug_ptr d_tmp; d_tmp.reset(new drug());
   d_tmp->id = 3;
   daoError = qx::dao::fetch_by_id(d_tmp);

   // Export drugs from container to a file under xml format (serialization)
   qx::serialization::xml::to_file(lst_drug, "./export_drugs.xml");

   // Import drugs from xml file into a new container
   type_lst_drug lst_drug_tmp;
   qx::serialization::xml::from_file(lst_drug_tmp, "./export_drugs.xml");

   // Clone a drug
   drug_ptr d_clone = qx::clone(* d1);

   // Create a new drug by class name (factory)
   boost::any d_any = qx::create("drug");

   // Insert drugs container into 'qx::cache'
   qx::cache::set("drugs", lst_drug);

   // Remove all elements from 'qx::cache'
   qx::cache::clear();

   // Create a dummy memory leak
   drug * pDummy = new drug();

   return 0;
}

*-------------------------------------------------------------------------
* 4- execute program and trace output debug
* -------------------------------------------------------------------------

[QxOrm] qx::QxSqlDatabase : create new database connection in thread '3616' with key '{d315250c-b5c9-46e0-9402-f800368a6673}'
[QxOrm] sql query (78 ms) : CREATE TABLE drug (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT, desc TEXT)
[QxOrm] sql query (63 ms) : INSERT INTO drug (name, desc) VALUES (:name, :desc)
[QxOrm] sql query (62 ms) : UPDATE drug SET id = :id, name = :name, desc = :desc WHERE id = :id_bis
[QxOrm] sql query (63 ms) : DELETE FROM drug WHERE id = :id
[QxOrm] sql query (0 ms) : SELECT COUNT(*) FROM drug
[QxOrm] sql query (0 ms) : SELECT drug.id AS drug_id_0, drug.name AS drug_name_0, drug.desc AS drug_desc_0 FROM drug WHERE drug_id_0 = :id
[QxOrm] Leaked object at 0xf52ad8 (size 16, src\main.cpp:74)
[QxOrm] **** 1 memory leaks found **** 

* ------------------------------------------------------------------------------
* 5- ./export_drugs.xml file created by the program
* ------------------------------------------------------------------------------


quick_sample.export_drugs.xml

 

Tutorial : qxBlog project

In this tutorial, 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...).

qxBlog.pro


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_BLOG_DLL_EXPORT QX_DLL_EXPORT_HELPER
#else // _BUILDING_QX_BLOG
#define QX_BLOG_DLL_EXPORT QX_DLL_IMPORT_HELPER
#endif // _BUILDING_QX_BLOG

#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 // _BUILDING_QX_BLOG
#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_IMPORT_DLL
#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_IMPORT_DLL
#endif // _BUILDING_QX_BLOG

#endif // _QX_BLOG_EXPORT_H_


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 // _QX_BLOG_PRECOMPILED_HEADER_H_


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 :

qxBlog.table.author

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 (using boost library), and a list of author (using qx::QxCollection of QxOrm library).
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
   typedef boost::shared_ptr<blog> blog_ptr;
   typedef std::vector<blog_ptr> list_blog;
// -- enum
   enum enum_sex { male, female, unknown };
// -- properties
   QString     m_id;
   QString     m_name;
   QDate       m_birthdate;
   enum_sex    m_sex;
   list_blog   m_blogX;
// -- contructor, virtual destructor
   author() : m_id("0"), m_sex(unknown) { ; }
   virtual ~author() { ; }
// -- methods
   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 // _QX_BLOG_AUTHOR_H_
#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 :

qxBlog.table.comment

Like author class, we define 2 typedef : a smart-pointer to a comment object (using boost library) and a list of comment (using Qt library).
#ifndef _QX_BLOG_COMMENT_H_
#define _QX_BLOG_COMMENT_H_

class blog;

class QX_BLOG_DLL_EXPORT comment
{
public:
// -- typedef
   typedef boost::shared_ptr<blog> blog_ptr;
// -- properties
   long        m_id;
   QString     m_text;
   QDateTime   m_dt_create;
   blog_ptr    m_blog;
// -- contructor, virtual destructor
   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 // _QX_BLOG_COMMENT_H_
#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 :

qxBlog.table.category

Like author class and comment class, we define 2 typedef : a smart-pointer to a category object (using Qt library) and a list of category (using QxOrm library).
#ifndef _QX_BLOG_CATEGORY_H_
#define _QX_BLOG_CATEGORY_H_

class blog;

class QX_BLOG_DLL_EXPORT category
{
public:
// -- typedef
   typedef boost::shared_ptr<blog> blog_ptr;
   typedef qx::QxCollection<long, blog_ptr> list_blog;
// -- properties
   long        m_id;
   QString     m_name;
   QString     m_desc;
   list_blog   m_blogX;
// -- contructor, virtual destructor
   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 // _QX_BLOG_CATEGORY_H_
#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 (using boost library) and a list of blog (using stl library).
#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:
// -- properties
   long           m_id;
   QString        m_text;
   QDateTime      m_dt_creation;
   author_ptr     m_author;
   list_comment   m_commentX;
   list_category  m_categoryX;
// -- contructor, virtual destructor
   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 // _QX_BLOG_BLOG_H_
#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[])
{
   // Qt application
   QApplication app(argc, argv);

   // Parameters to connect to database
   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("");

   // Ensure there is no element in database
   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>();

   // Create a list of 3 author
   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);

   // Insert list of 3 author into database
   daoError = qx::dao::insert(authorX);
   qAssert(qx::dao::count<author>() == 3);

   // Clone author n°2 : 'author_id_2'
   author_ptr author_clone = qx::clone(* author_2);
   qAssert(author_clone->m_id == "author_id_2");
   qAssert(author_clone->m_sex == author::female);

   // Create a query to fetch only female author : 'author_id_2' and 'author_id_3'
   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);

   // Dump list of female author (xml serialization)
   qx::dump(list_of_female_author);

   // Create 3 categories
   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";

   { // Create a scope to destroy temporary connexion to database

   // Open a transaction to database
   QSqlDatabase db = qx::QxSqlDatabase::getDatabase();
   bool bCommit = db.transaction();

   // Insert 3 categories into database, use 'db' parameter for the 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);

   // Terminate transaction => commit or rollback if there is error
   if (bCommit) { db.commit(); }
   else { db.rollback(); }

   } // End of scope : 'db' is destroyed

   // Create a blog with the class name (factory)
   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;

   // Insert 'blog_1' into database with 'save()' method
   daoError = qx::dao::save(blog_1);

   // Modify 'blog_1' properties and save into database
   blog_1->m_text = "update blog_text_1";
   blog_1->m_author = author_2;
   daoError = qx::dao::save(blog_1);

   // Add 2 comments to '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);

   // Add 2 categories to 'blog_1' => must insert into extra-table 'category_blog'
   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);

   // Fetch blog into a new variable with all relation : 'author', 'comment' and 'category'
   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");

   // Dump 'blog_tmp' result from database (xml serialization)
   qx::dump(blog_tmp);

   // Call 'age()' method with class name and method name (reflection)
   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 :

qxBlog.table.person

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.  


History

Changes in version 1.1.0:
- First release

Changes in version 1.1.1:
- This version supports Visual Studio 2010

Changes in version 1.1.2:
- License LGPL
- Fix compilation problems on Linux and boost > 1.38
- Fix sql query with MySql database
- Disable assert when qx::dao functions return an error

Changes in version 1.1.3:
- This version works fine with MinGW on Windows

Changes in version 1.1.4:
- New parameter in functions 'qx::dao::fetch_by_id', 'qx::dao::fetch_all', 'qx::dao::fetch_by_query' and 'qx::dao::update' to define a list of properties to fetch/update (by default, all properties are fetched/updated)
- Support multi-columns primary key (composite key) : see sample './test/qxBlog_composite_key/'
- Improve strategy of inheritance : QxOrm supports 'Concrete Table Inheritance' strategy ('Concrete Table Inheritance' becomes default strategy)
- New smart-pointer 'qx::dao::ptr' based on Qt 'QSharedPointer' to provide 2 new features : 'is dirty' and 'update optimized'
- 'qx::dao::ptr' can be used with a simple object and with many containers (stl, boost, Qt and 'qx::QxCollection' containers)
- 'qx::dao::ptr' keeps original values from database and provides a 'isDirty()' method to retrieve all properties changed
- 'qx::dao::update_optimized' must be used with 'qx::dao::ptr' to save into database only properties changed

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"