Overview of ORM for Qt

    Introduction



    Good afternoon, dear fellow programmers!

    For a year now, very interesting events have been taking place in the Qt world. Here you have the recent release of version 4.7, the concept of QML, and significant integration of the library into mobile platforms. Qt has the most correct trolls; I like what they do and where this library develops. I’m even betting she is the best in her class, but those who write in Qt already know that.

    There is something else that has changed over the annual period. For Qt, ORM libraries began to appear, one after another, like mushrooms. A holy place is never empty? There is demand, here is the offer. Read what is happening in the Qt ORM world in this article. I will try to give maximum information on the use and mechanisms used in the monitored libraries; but not one of them can be consecrated completely for the reason that any ORM is a very complex set of programming solutions.

    (I note right away that the article is somewhat advertising because it appeared because of my own ORM; however, in fairness, I not only PR myself, but also give a general assessment of what is now on the topic. Please be understanding: best intentions).


    QxOrm, ver. 1.1.1



    Author / Owner : QxOrm France
    Sites : official , on SourceForge
    License : GPLv3 + commercial
    Dependencies : Qt (4.5+), boost (1.38+)
    Development period : early 2010, last modified - April 2010
    Documentation : incomplete, in French
    Examples : yes, the most basic The

    main goal of development is to provide mechanisms of persistence (via QtSql), serialization (via boost :: serialization) and reflection.

    The library looks very strong and seems to be able to do a lot of things. Built on the principle of DAO ( Data Access Object), when there is a class displaying a row in the table, and using some methods, a list of such rows is retrieved from the database. To make this possible, QxOrm uses very tricky mechanisms, including templates (a lot of templates), macros, inheritance simple and multiple. The code is very interesting for acquaintance, if you are a lover of programmatic tricks or, for example, you like Alexandrescu.

    Judging by the examples, code and descriptions, the following features are implemented.
    • Display table data in any stl / boost container. I guess that Qt containers will work too.
    • Some complexity caching.
    • The generation of the simplest queries: insert, update, delete, create table, and, of course, select.
    • Wrap over QSqlDatabase, your own QxSqlQuery.
    • One-to-one, one-to-many, many-to-many relationships at the code level.
    • Own stl-like QxCollection collection of key / value type with hashing, sorting by key and value.
    • Template implementation of the foreach (!) Loop, the principles of which I can only guess.
    • Implemented template singleton.

    Pros:
    • The general focus is not on databases, but on abstract storage, including XML. True, this is achieved at the expense of Boost - "on bazooka sparrows."
    • Good ORM features, passing selected values ​​directly to containers.
    • A bunch of additional features: serialization, containers, caching, etc.
    • Simple syntax for working with mappers. It can be seen that the author was guided by something existing when he designed the interfaces. Everything is logical, neat, very similar to Boost and STL. Qt style is a minimum.
    • Technological library; however, this is exactly the minus, because it is very difficult to study its internal kitchen.

    Minuses:
    • The general focus is not on databases, but on abstract storage.
    • Weak SQL support; in particular, there is no generation of WHERE, JOIN, GROUP BY, ORDER BY and much more. For example, to extract specific data by a filter, you need to select all of them, and then apply the STL / Boost algorithms. (However, I'm not sure about everything; maybe I missed something. Nevertheless, there is no WHERE generation in the source code - this is a fact.)
    • Weak documentation. Perhaps the author believes that there are enough examples and tutorials, but I don’t think so. We Qt programmers are accustomed to good and complete documentation. There are no descriptions of classes, methods, or constants. Well, what is - in French.
    • The involvement of the library. Do not even hope that you can add something of your own there.
    • Boost addiction.
    • License. This is not to say that she is completely free; Want to sell a library based product? Pay the author.
    • The main thing: the library is not developing, the documentation is not being translated. The author first widely publicized QxOrm on foreign Internet, and then disappeared. Version 1.1.1 was the first and only.

    Paradoxically, with all the drawbacks, QxOrm is almost the only full-fledged ORM solution compatible with Qt. And this is the only solution where there is caching, which is important for complex projects. You will see that this tiny review is still more than others, since other ORMs are unlikely to compare with QxOrm. However, using the library in a large project, you might want some other possibility, especially if you are working not with an abstract repository, but with a full-fledged DBMS - but it will not be. You will want to fix some kind of bug - but it is not so simple. You will have to invent a lot of bicycles and crutches. The project will inevitably turn into a chimera. On the contrary, for a small project, where you need only a few good features, the library can be useful - to the extent that you are not afraid to tighten Boost.

    An example of a mapper class (all code is taken from the documentation):

    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)
     
     
    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 & 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'
    }}
     
     

    Usage example:

      // Create table 'drug' into database to store drugs
       QSqlError daoError = qx :: dao :: create_table();
     
       // 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();
     
       // 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 ::
       qx :: cache :: set ("drugs", lst_drug);
     



    QDjango, ver. ???


    Author : Jeremy Lainé, Bolloré telecom
    Sites : official , mailing list
    License : GPLv3, LGPLv3
    Dependencies : Qt (4.5+)
    In development since June 3, 2010
    Documentation : complete, in English, doxygen-generated
    Examples : yes, the most basic.

    The main goal : to create a free ORM for Qt, as similar to Django as possible.

    It is too early to talk about the advantages and disadvantages of this development, the library still does not know how. Apparently, this will be a DAO / Active Record-ORM. Now it is already possible to generate SELECT, retrieve data into a container, create tables through CREATE TABLE generation. The author immediately began to prescribe the WHERE generation; Moreover, the AND and OR operators are supported. That is, you can create a multi-level filter, and the syntax for setting filters is also successful. In development, the same methods are actively used as in QxOrm: templates, inheritance ... Based on them, presumably, the author is going to create huge farms of good OOP code.
    But that is all. We will believe that in a year and a half a powerful ORM system will grow from QDjango, but for now, there is no need to talk about its application in projects.

    An example of use taken from the author.

    // all users
    QDjangoQuerySet users;
     
    // find all users whose password is "foo" and whose username is not "bar"
    QDjangoQuerySet someUsers;
    someUsers = users.filter (QDjangoWhere ("password", QDjangoWhere :: Equals, "foo") &&
                             QDjangoWhere ("username", QDjangoWhere :: NotEquals, "bar"));
     
    // find all users whose username is "foo" or "bar"
    someUsers = users.filter (QDjangoWhere ("username", QDjangoWhere :: Equals, "foo") ||
                             QDjangoWhere ("username", QDjangoWhere :: Equals, " bar "));
     
    // find all users whose username starts with "f":
    someUsers = users.filter (QDjangoWhere ("username", QDjangoWhere :: StartsWith, "f"));
     
    // limit number of results
    someUsers = users.limit (0, 100);
     
    // get number of matching users
    int numberOfUsers = someUsers.size ();
     
    // retrieve the first matching user
    User * firstUser = someUsers.at (0);
     
    // free memory
    delete firstUser;
     
    // retrieve list of usernames and passwords for matching users
    QList <QList > propertyLists = someUsers.valuesList (QStringList () << "username" << "password");
     
    // delete all the users in the queryset
    someUsers.remove ();

    User Class:

    class User : public QDjangoModel
    {
        Q_OBJECT
        Q_PROPERTY(QString username READ username WRITE setUsername)
        Q_PROPERTY(QString password READ password WRITE setPassword)
     
        Q_CLASSINFO("username", "max_length=255")
        Q_CLASSINFO("password", "max_length=128")
     
    public:
        QString username() const;
        void setUsername(const QString &username);
     
        QString password() const;
        void setPassword(const QString &password);
     
    private:
        QString m_username;
        QString m_password;
    };


    QtPersistence, ver. 0.1.1


    Author : Matt Rogers
    Sites : on SourceForge
    License : LGPLv3
    Dependencies : Qt (4.5+)
    Development period : late 2009 - early 2010
    Documentation : none
    Examples : poor in unit tests

    Main goal : create an ORM for Qt based on the Active approach A Record similar to some (?) ORM for Ruby.

    Another library that knows almost nothing. What is worse: and does not develop; It seems that the author abandoned this project. Actually, all it can do is use the mapper class to write data to the database.

    The only examples of use are found in unit tests (based on the self-written testing module).

    class FakeBook: public QPersistantObject
    {
    Q_OBJECT
    Q_PROPERTY (QString author READ author WRITE setAuthor)
    Q_PROPERTY (int yearPublished READ yearPublished WRITE setYearPublished)
    public:
        Q_INVOKABLE FakeBook (QObject * parent = 0): QPersistant (}}
        QPersistant
     
        void setAuthor (const QString & a) {m_author = a; }
        QString author () const {return m_author; }
     
        void setYearPublished (int year) {m_yearPublished = year; }
        int yearPublished () const {return m_yearPublished; }
     
    private:
        QString m_author;
        int m_yearPublished;
    };
     
        FakeBook * b = new FakeBook;
        b-> setAuthor ("Matt Rogers");
        b-> setYearPublished (2009);
     
        bool objectSaved = b-> save ();
        delete b;
        ASSERT_TRUE (objectSaved);
     
        b = new FakeBook;
        b-> setAuthor ("Not Matt Rogers");
        b-> setYearPublished (1999);
     
        objectSaved = b-> save ();
        delete b;
        ASSERT_TRUE (objectSaved);


    QsT SQL Tools (QST), ver. 0.4.2a release


    Author : Alexander Granin (me :))
    Sites : on SourceForge , forum
    License : GPLv3, LGPLv3
    Dependencies : Qt (4.5+)
    In development since September 2009
    Documentation : full, doxygen-generated, only in Russian
    Examples : yes, in code in unit tests; I also created special TradeDB sample projects for versions 0.3 and 0.4 - full-fledged database applications.

    The main goal is to facilitate the programming of database applications under Qt.

    Talking about your ORM is not easy. It was thanks to her that I got to Habr, writing an article in the sandbox. Articleinterest did not arouse ... But it was version 0.3 - and not even release, but pre-alpha. Now I have gone far in the development of QST, and 0.5.1 pre-alpha is already available; but still there is so much to do.

    First of all: this is not an ordinary ORM. I began to write the library, not yet knowing the term; I needed a tool for generating queries, so as not to write them, and so that they were concentrated in one layer: it was easier to keep track of them. I did not know about approaches like Active Record. The result is what happened: a distinctive ORM, which is not exactly ORM. You cannot configure class fields in it that would map to table fields; it is impossible to write (read) data directly to (from) the database using only the assignment. But you can do a lot of other things.

    Opportunities, they are also advantages of the library.
    • Generation of simple (in the sense of non-hierarchical, without gadgets) SQL queries such as SELECT, INSERT, UPDATE, DELETE and EXECUTE (in the case of PostgreSQL, this is SELECT).
    • DFD Concept ¬– Declarative Field Descriptor. According to it, you describe how to generate a request; Additionally, for SELECT, you describe how to use the received data in Qt representations (QTableView, QListView, QComboBox).
    • Integration with Qt Interview. Some, no, but there is. Besides the non-Active-Record approach, this is the main difference between QST and everything else. For example, you can specify how wide the QTableView columns should be for a particular query, how they should be entitled. Or you can select the data values ​​associated with the current row in some view.
    • Multiple named queries.
    • For each request - the ability to connect many different views.
    • WHERE section generation. Filters can be set very different; main minus: conditions must be combined through the AND operator.
    • Automatically retrieving the field name if it is specified as "max (field_name)", "sum (price * count) as summa". In the first case, the field can be accessed both completely ("max (field_name)") and abbreviated ("field_name"). In the second case - only through "summa".
    • A multifunctional connection class is a wrapper over QSqlDatabase. It can perform connection testing, store settings, connect with different names, and delete connections.
    • In general, easy use; the main thing is to understand the meaning of how the library works.
    • Tree data model. I really want to finally rewrite it, because I do not want new and delete present in my library in this form. This is more than possible and will lead to safer memory operations.
    • Indirectly facilitates the conversion of data on the path "Program - DB."
    • Very extensive features for tuning SQL generation; for example, if you described a future query by including filters in it, then when the filter is invalid, it simply is not generated. However, if valid is reduced to the format of your SQL dialect and added to the WHERE section. The comparison functors are also automatically placed; for strings it is LIKE, for numbers - "=". However, they are easy to override.
    • Other.

    Minuses? Of course there are, and quite a lot!
    • Unusual concept, identity. You have to do a lot of things with your hands; more precisely, create handler classes and write DFDs for different types of queries.
    • SQL support, although more than in all the libraries reviewed, is still insufficient. Now I am struggling with several tasks; probably in the next versions the generation engine will be rewritten.
    • There is no caching, no serialization, no reflection - no real Object Relational Mapping. There is no way to create relationships (relations: 1: 1, 1: n, n: n). In this, I must admit, QxOrm is ahead of the rest.
    • No caching. Although I think how to better implement it.
    • You cannot just as easily retrieve data into structures as was conceived in all other ORMs. However, the QST approach itself is such that it suggests not thinking about individual datasets; instead, it is better to think at the level of models and representations, as well as the individual values ​​of a particular record.
    • The library is not as technological as the others. Yes, there are templates and inheritance inside - but this is nothing compared to the same QxOrm. In any case, the programmer does not need to bother with this.
    • Some non-obviousness, in any case, first. A lot of things are done automatically (conversion, for example), and you may not notice this right away, even despite the full documentation.
    • Other.


    In general, the library is still in development, but already knows a lot of things. I use it in my work; and as a freelancer I am writing another project on it. In general, the programmer has much less work to do than with the one with QxOrm or QDjango, as can be seen from the source code for the examples. Described handlers, loaded view into them - get features that almost all are located in the main class (QstAbstractModelHandler). All that is needed, I introduce slowly, but you can always turn to me, I will definitely help. Unlike. Therefore, I immodestly propose supporting me in this difficult undertaking. Though even a wish of good luck; or better, any feedback. I will be grateful.

    An example of a handler class and a DFD descriptor for a SELECT query.

    Please note that the QstField fields also transmit information for customizing views: field displayability, title and column width.
    // personshandler.h
    const int PERSONS = 1;
    const int PERSONS_FULL_NAME = 2;
     
    class PersonsHandler: public QstAbstractModelHandler
    {
    private:
     
    QstBatch _selector (const int & queryNumber) const;
    QstBatch _executor (const int & queryNumber) const;
    };
     
    // personshandler.cpp
    QstBatch PersonsHandler :: _ selector (const int & queryNumber) const
    {
    QstBatch batch;
     
    if (queryNumber == PERSONS)
    {
    batch.addSource ("vPersons");
    batch << QstField (RolePrimaryKey, "ID")
    << QstField ("Address_ID")
     
    << QstField ("LastName", FieldVisible, "Last Name", 100)
    << QstField ("
    << QstField ("ParentName", FieldVisible, "Middle Name", 100)
    << QstField ("vcBirthDate", FieldVisible, "Date of Birth", 90)
    << QstField ("Phone", FieldVisible, "Contact \ nphone", 120)
    << QstField ("[E-Mail]", FieldVisible, "e-mail", 120)
     
    << QstField ("ID", value (ID_VALUE), PurposeWhere)
    ;
    }
    else
    if (queryNumber == PERSONS_FULL_NAME)
    {
    batch.addSource ("vPersons");
                    batch
     
    << QstField ("FullName", FieldVisible, "Full Name", 300)
    << QstField ("LastName", FieldVisible, "Last Name", 100)
    <<

    << QstField ("vcBirthDate", FieldVisible, "Date of Birth \ n", 90)
    << QstField ("Phone", FieldVisible, "Contact \ nphone", 120)
    << QstField ("[E-Mail]", FieldVisible, "e-mail", 120)
     
    << QstField ("ID", value (ID_VALUE), PurposeWhere)
     
     
                                    << QstField (RolePrimaryKey, "ID")
                                    << QstField ("Address_ID")
                                    ;
    }
    else
    {
    Q_ASSERT (false);
    }
     
    return batch;
    }

    View customization:

    // PersonsHandler _personsHandler;
    // QstPlainQueryModel _personsModel; // - described in the PersonsForm class.
     
    void PersonsForm :: loadPersons ()
    {
    _personsHandler.reload (PERSONS, & _personsModel);
    _personsHandler.setTableView (ui-> tv_PersonsTableView);
    }
     
    QVariant PersonsForm :: personID () const
    {
    return _personsHandler.keyValueOfView ();
    }

    Using:

    void PersonsForm::loadPersonalDocumentInfo()
    {
    PersonalDocumentsHandler th;
    th.setValue("Person_ID", personID());
    QVariantMap valMap = th.SelectToMap(PERSONAL_DOCUMENTS,
    QStringList()
    << "DocTypeName"
    << "SerialNumber"
    << "Number"
    << "vcIssueDate"
    << "GivenBy");
    ui->le_DocumentTypeLineEdit->setText(valMap["DocTypeName"].toString());
    ui->le_SerialNumberLineEdit->setText(valMap["SerialNumber"].toString());
    ui->le_NumberLineEdit->setText(valMap["Number"].toString());
    ui->le_IssueDateDateEdit->setDate(valMap["vcIssueDate"].toDate());
    ui->le_GivenByLineEdit->setText(valMap["GivenBy"].toString());
    }

    Also popular now: