views:

42

answers:

1

My goal is to create a thick client to the database. Basically it is all about managing three lists of data.

I would like to slice my application into decoupled layers so using Qt's Model/View framework seems natural to me.

  • When should I create QSql*Model instances?

I need to be able to connect/disconnect to/from the database several times (I have menu items for that). I'm not happy with deleting a bunch of models and creating them once again upon every connect/disconnect.

Is there any alternative approach?

  • Where should I create QSql*Model instances?

I don't think MainWindow or any other GUI-related class is supposed to hold the code like this:

m_goodsModel->setRelation(1, QSqlRelation("Level", "LevelId", "Name"));

I want to decouple the GUI from the data structure. Any ideas how to do that?

  • When and where should I bind my views to models?

I need to represent my three lists in a dozen of ways. If I recreate the models every time I connect/disconnect I will need to inject newly created models into all the views again.

It would be great if I could do that only once, but I have no clue how.

  • What to do with the nasty QSqlTableModel::select() method?

This one drives me crazy. In contrast to other models (e.g. QStringListModel, QFileSystemModel, etc) where the data is ready to use out of the box, models derived from QSqlTableModel are useless until you manually invoke their select() method. Before that invocation the model is empty so as the views using that model; the header data is not populated either, so the view doesn't even know what columns does it have to render.

As I cannot avoid select() invocation I wonder where should I put it so that it fits nice? I don't think MainWindow or any other GUI-related classes should contain that code.

  • Performance and robustness

I'm not happy to reinitialize everything upon db reconnection. It takes too long to do that (I mean during execution). I also would like to avoid crashes during model recreation process as the views may still refer to them.

Isn't there any other way to set everything just once and handle reconnection gracefully?

A: 

It seems to me that the straight forward answer to all of these questions is to wrap all database related actions in a dedicated class, and hold a pointer to an instance of this class in your MainWindow/Dialog/whatever.

A possible design could be something like this:

class DatabaseAccess : public QObject
{
       Q_OBJECT
    public:
       void connectToDatabase(const QString & hostname, const QString & db, const QString & user, const QString & password);
       void disconnectFromDatabse();

       QAbstractItemModel * getModelForX();
       QAbstractItemModel * getModelForY();

    private:
       QSqlTableModel * modelForX;
       QSqlRelationalTableModel * modelForY;
}

Here, X and Y are just placeholders for the types of queries you have in your application.

You can create your models in connectToDatabase() and invoke select() when you need fresh data in the getModelForX/Y methods.

As far as I know, you can't keep using the same model instances with different database connections, as QSqlTableModel and its decedents are bound to a QSqlDatabase instance. You will need to refresh your views after a successful connection.

IgKh
That is basically what I was doing. But this approach doesn't address the model injection and recreation problem.Do I need to create new models after each connection or can I reuse the old ones? If I'm forced to recreate them, who (i.e. what class/layer) is responsible for model injection?
Ihor Kaharlichenko
You have to recreate the models - they become invalid after the connection is closed (see http://doc.qt.nokia.com/4.6/qsqldatabase.html#removeDatabase). The layer that injects the model, is the layer that knows about the view. Probably the main window in the connection slot.
IgKh
In fact I don't `remove()` the QSqlDatabase, I just `close()` it. Does that matter?
Ihor Kaharlichenko
Yes, open queries are invalidated anyway.
IgKh