Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Deserializing QSqlQuery using submethod in efficient way



  • My data access layer uses interface SQLAdapter, which provides two methods:

    • deserialize(),
    • deserializeAll()

    I'd like to use deserialize in deserializeAll method.
    Function, which uses both of those has access to QSqlQuery object.

    At this point, I am not really sure how to implement deserialize() method. According to what i've found in documentation, before reading values from db, we must use ex. next() or first().

    Currently, after executing QSqlQuery object, i check whether query isValid(). After that, i next() query, and pass it to deserialize() or deserializeAll() depending on number of records (deserializeAll calls deserialize for every record).

    std::unique_ptr<T> T::deserialize(QSqlQuery& row);
    std::vector<std::unique_ptr<T>> T::deserializeAll(QSqlQuery& result);
    

    First problem is that, after passing QSqlQuery to deserialize, isActive() method returns false, but im still able to read values from it.
    I cannot understand this behaviour.

    Secondly, im not sure if passing QSqlQuery is "elegant" way, so I've tried to modify deserialize so it uses QSqlRecord:

    std::unique_ptr<T> T::deserialize(const QSqlRecord& row)
    

    But if I do so, I cannot read any values from record. Each field isNull().
    Moreover, documentation says that we should use value() with QSqlQuery instead of QSqlRecord.

    Could somebody point out what am I missing about classes mentioned above, and provide me with elegant and correct way of implementing those methods?
    Thanks in advance.


  • Lifetime Qt Champion

    Hi,

    Can you show exactly how you are using that class including the querying part ?



  • @SGaist
    Hi,
    firstly, I prepare query in DAO class

    virtual std::unique_ptr<T> get(const int& arg) {
        std::string query = "SELECT * FROM ";
        query.append(tableName).append(" WHERE id = ");
        query.append(std::to_string(arg));
    
        std::unique_ptr<QSqlQuery> resultPtr  = SQLManager::execute(query.c_str());
        if (resultPtr != nullptr) {
            QSqlQuery result = *(resultPtr.get());
            if(result.isActive()){
                T person;
                result.first();
                std::unique_ptr<T> ptr = person.deserialize(result);
                SQLManager::finish(result);
                return ptr;
            }
        }
        return nullptr;
    };
    

    I execute it in SQLManager class:

    static std::unique_ptr<QSqlQuery> execute(const char* query) {
        establishConnection();
    
        if (db.isOpen()) {
            QSqlQuery sqlQuery;
    
            if ( sqlQuery.exec(query) != 1) {
                db.close();
                return nullptr;
            }
            else {
                db.close();
                return std::make_unique<QSqlQuery>(sqlQuery);
            }
        }
        return nullptr;
    }
    

    And as you can see in first bit of code, i deserialize it using, for example, Patient class:

    std::unique_ptr<Patient> Patient::deserialize(QSqlQuery& row) {
    
        if(row.isValid()){
    
            std::string pesel, surname, firstName, secondName;
            int doctorId;
    
            pesel = row.value(1).toString().toStdString();
            surname = row.value(2).toString().toStdString();
            firstName = row.value(3).toString().toStdString();
            secondName = row.value(4).toString().toStdString();
    
            if (row.value(5).isNull()) {
               doctorId = -1;
            }
            else {
               doctorId = row.value(5).toInt();
            }
    
            return std::make_unique<Patient>(pesel, surname, firstName, secondName, doctorId);
    
        }else{
            return nullptr;
        }
        
    }
    

    And finally, all of that is being used in deserialize all:

    std::vector<std::unique_ptr<Patient>> Patient::deserializeAll(QSqlQuery& result) {
    	
        std::vector<std::unique_ptr<Patient>> patients;
    
        if(result.isActive()){
    
            while(result.next()){
                 patients.push_back(deserialize(result));
            }
        }
    
        return patients;
    }
    
    SQLManager::finish(query);
    

    method is responsible for clearing query only (I have moved it into temporary function for now)


  • Lifetime Qt Champion

    Why are you closing the database connection before processing your query ?

    Out of curiosity, why use all these unique_ptr to manage QSqlQuery objects ?



  • About db connection:
    When I started to use QT sql library I thought that QSqlQuery is independent from db connection - its a mistake i haven't fixed yet.

    Is this the reason for QSqlQuery::isActive() to fail?
    About unique_ptrs:
    Previously i was using Mysql C Connector with VS in which i worked with pointers on query result sets, so when I changed IDE to QT i thought that it can stay like that for now, until i get done with connecting, and "translating" code to QT.

    Btw - Is QSqlQuery::clear() called when unique_ptr<QSqlQuery> goes out of scope?


  • Lifetime Qt Champion

    No it's not, you can see that with its constructors that take a QSqlDatabase parameter.

    No clear is not called, the destructor is.

    There's no need to create QSqlQuery objects on the heap.


Log in to reply