Search code examples
sqldatabaseqtc++11binaryfiles

What is the best way to read/write objects in a file with the following conditions


I am willing to store objects in a database. The purpose is to be able to read / write these objects with the program. The requirements are the following:

  • Objects can be complex using Qt classes such as QList, QString ... or even can contain other objects that use QObjects
  • The database should not be readable or modified by human (no text file, and if I use sqlite database, it has to be encrypted in a way)
  • I should be able to remove, read an object by its name and count the number of objects in the database, without loading everything in the memory

I asked a question here, to do this with a QDataStream with a minimalist example. But it seems it is not the best way to proceed. Would you have some suggestions regarding the solutions that exist for this purpose?

I have tried the following but it does not fulfill the requirements:

  • Storing text in sqlite with QtSQL: but the data is accessible by using sqlitemanager for example, can be modified or removed by humans. Moreover, I have no idea regarding the way to store QList for example or other objects that I created and contain QObject (for example, 2 QList)
  • Storing binary data using QDataStream: in this case, I cannot count the number of objects in my file, neither read or remove a specific object without loading the entire file in memory.

I would be grateful if you could give me some suggestions or provide example, even if the example is minimalist.


Solution

  • I finally found a solution, especially thanks to Igor Tandetnik and thanks to the topic here

    I haven't quite finalized, there is a small imperfection because I have to define an object of my user class that I don't use in order to call the readFromDB function to generate my object from the db.

    On the other hand, I get this error message "QSqlDatabasePrivate::addDatabase: duplicate connection name 'qt_sql_default_connection', old connection removed" each time I call my database.

    Anyway, it's a bit late now, and I think it might help some people so I post this minimalist imperfect code below. I'll post an update in the next few days.

    Thanks again.

    #include "QString"
    #include "QFile"
    #include "QDataStream"
    #include "qdebug.h"
    #include "QtSql"
    #include "QSqlDatabase"
    #include "qmessagebox.h"
    
    class User
    {
    protected:
    QString name;
    QList<QString> childrens;
    
    public:
    QString getName(){ return name;}
    QList<QString> getChildrens(){ return childrens;}
    
    void setName(QString x) {name = x;}
    void setChildrens(QList<QString> x) {childrens = x;}
    
    
    
    friend QDataStream &operator<<(QDataStream &out, const User &t)
    {
        out << t.name << t.childrens;
        return out;
    }
    
    friend QDataStream &operator>>(QDataStream &in, User &t)
    {
        QString inname;
        QList<QString> inchildrens;
        in >> inname >> inchildrens;
        t.name = inname;
        t.childrens = inchildrens;
        return in;
    }
    QByteArray object2blob( const User& user )
    {
      QByteArray result;
      QDataStream bWrite( &result, QIODevice::WriteOnly );
      bWrite << user;
    
      return result;
    
    }
    User blob2object( const QByteArray& buffer )
    {
      User result;
      QDataStream bRead( buffer );
      bRead >> result;
    
      return result;
    }
    int saveToDB( const User& user )
    {
        QSqlDatabase myDB = QSqlDatabase::addDatabase("QSQLITE");
        myDB.setDatabaseName( "file.db");
        if (!myDB.open())
        {
            qDebug()<<"Failed to open SQL database of registered users";
        }
        else
        {
            qDebug()<<"Successfully opening SQL database of registered users";
            QSqlQuery query;
    
            query.prepare( "CREATE TABLE IF NOT EXISTS users (name TEXT, childrens BLOB)" );
              if( !query.exec() )
              {
                qDebug() << query.lastError();
              }
              else
              {
                qDebug() << "Table created!";
                query.prepare( "INSERT INTO users (name,childrens) VALUES (:name,:childrens)" );
                query.bindValue(":name", name);
                query.bindValue( ":childrens",  object2blob(user) );
                query.exec();
              }
            query.clear();
            myDB.close();
        }
        QSqlDatabase::removeDatabase("UserConnection");
    
        return 0;
    }
    User readFromDB( QString name )
    {
        User result;
        QSqlDatabase myDB = QSqlDatabase::addDatabase("QSQLITE");
        myDB.setDatabaseName( "file.db");
        if (!myDB.open())
        {
            qDebug()<<"Failed to open SQL database of registered users";
        }
        else
        {
            QSqlQuery query;
    
            query.prepare( "SELECT * FROM users WHERE name ='"+ name +"'" );
            //query.bindValue( 0, name );
    
            if ( query.exec() && query.next() ) {
              result = blob2object( query.value( 1 ).toByteArray() );
            }
            query.clear();
            myDB.close();
        }
        QSqlDatabase::removeDatabase("UserConnection");
        qDebug()<<result.getChildrens();
        return result;
    }
    };
    
    
    ////////////////////////////////////////////////////////////////
    int main()
    {
        User u;
        u.setName("Georges");
        u.setChildrens(QList<QString>()<<"Jeanne"<<"Jean");
        u.saveToDB(u);
    
        User v;
        v.setName("Alex");
        v.setChildrens(QList<QString>()<<"Matthew");
        v.saveToDB(v);
    
        User w;
        w.setName("Mario");
        w.saveToDB(w);
    
        User to_read; //to improve here
        User a = to_read.readFromDB("Georges");
    
        qDebug()<<a.getChildrens();
        return 0;
    }