2

I am using the Qt libraries in a C++ project but I have a design question: where should a database be declared? I would prefer not to declare global variables.

Currently I am dealing with this problem in this way. I have a mainwindow and I have declared the DB in there so I perform the queries in the main window and pass the results to the dialogs using different signals and slots.

I start the DB when the main window starts and close it when the window has been closed. I don't know if this is ok Now I need the DB connection in another class as well so I can pass a reference to the DB or make the DB global

I don't like these solutions.. is there a standard pattern to deal with this situation?

edit:

My class now looks like:

class Database
{
  public:
    bool open(void);
    bool close(void);
    static Database* getDatabase(void);
    // various methods like loadThisTable(), saveThisTable() etc

  private:
    Database();                                // disable constructor
    ~Database();                               // disable destructor
    Database(const Database&);                 // disable copy constructor
    Database& operator=(const Database&);      // disable assignment

    static Database* instance_;                // database instance
    QSqlDatabase qtDB;                         // qt db database
}

If I want I can add the add and remove methods but I have a single DB instance.

Nick
  • 11,475
  • 1
  • 36
  • 47
user2244311
  • 41
  • 1
  • 4
  • Please clarify what do you mean by database object? Aren't you using QtSql's model classes? – smitrp Apr 04 '13 at 10:21
  • ps I have create a class database with method like: loadThis(), loadThat() and inside this class there's the qt database so I m not using directly using the qt database. If I was using it I know I could have just added the database with QSqlDatabase::addDatabase, and got it from anywhere with QSqlDatabase::database – user2244311 Apr 04 '13 at 10:22
  • @SmitPatel I have created for each physical table a new class – user2244311 Apr 04 '13 at 10:23
  • I think for modular, easy-to-read code, your way of structuring this is fine. You can make another connection in other classes. But if you're really considering reusing existing connection, you should look at [shared pointer](http://stackoverflow.com/questions/10826541/passing-shared-pointers-as-arguments). That way you can use existing connection by your application elsewhere. But it would over-complicate this task. – smitrp Apr 04 '13 at 10:40
  • I know the shared_pointer but I hadn't thought about it. I will look into it thank you – user2244311 Apr 04 '13 at 10:43
  • Another way is to make generic class which can provide data from different tables. This way if you're using different tables in different db objects in different places, using same class to get data from different table will make sense. – smitrp Apr 04 '13 at 10:45

2 Answers2

2

If you're using QSqlDatabase, you don't really need to make it a global variable. Just set up the connection when you first start your application, then use the static QSqlDatabase methods to access the connection when you need it in different modules.

Example

QSqlDatabase db;  // set up the default connection
// alternative:  set up a named connection
// QSqlDatabase db("conn-name");

// set the connection params and open the connection

// ... later on
QSqlDatabase db = QSqlDatabase::database();  // retrieve the default connection
// alternative:  retrieve the named connection
// QSqlDatabase db = QSqlDatabase::database("conn-name");

From the docs:

QSqlDatabase is a value class. Changes made to a database connection via one instance of QSqlDatabase will affect other instances of QSqlDatabase that represent the same connection. Use cloneDatabase() to create an independent database connection based on an existing one.

Note: If you're application is multi-threaded, you have to be careful to only use a connection in the thread in which it was created.

Jacob Robbins
  • 1,860
  • 14
  • 16
0

You need a singleton pattern. It's a global class which have only one instance. Someone calls it antipattern (and sometimes it is), but it is the best way to handle resources like database connections.

And dont forget that you can use QSqlDatabase QSqlDatabase::database ( const QString & connectionName = QLatin1String( defaultConnection ), bool open = true ) [static] method to get QSqlDatabase instance by name (name can be set via QSqlDatabase QSqlDatabase::addDatabase ( QSqlDriver * driver, const QString & connectionName = QLatin1String( defaultConnection ) ) [static] method) to avoid creating singleton just for storing QSqlDatabase instances.

SpongeBobFan
  • 964
  • 5
  • 13
  • 2
    "You need a singleton pattern". No. Absolutely not. No one *needs* a singleton, it seems so only if you are too lazy to find another solution. "It is the best way to handle resources like database connections". Says who? What if you want multiple connections inside your application? – Shoe Apr 04 '13 at 12:49
  • Let's holywar begins! Ok, let's imagine that I have 2 database connections that I need to use in my application. Should I transfer them to every form or worker class I call, or just store them in a singleton that can be accessed from anywhere? Everyone know that global objects are bad, but sometimes you just have global resources like database connections/pool of worker threads/etc, they are global by their nature. – SpongeBobFan Apr 04 '13 at 13:00
  • "Should I transfer them to every form or worker class I call, or just store them in a singleton that can be accessed from anywhere?" Can you see the pattern? If you are lazy you use singletons (but you'll regret it in the future), otherwise you explicitly define dependencies for each classes, therefore yes: you should transfer them to every form or worker class you call. It's not about any holy war, it's about having reusability. What if you need any of those class to use another database object next time? – Shoe Apr 04 '13 at 13:13
  • Globals are not bad just because they are global, it's because other objects hardcode the use of that specific global instance instead of defining a proper reusable interface. – Shoe Apr 04 '13 at 13:14
  • Your arguments are good, singletons are very bad for code reusage, unit testing, etc. But sometimes code is much more readable when you're not transfer all outer resources that form-which-called-by-form-which-called-by-our-current-form uses in our form's constructor. You can watch Qt source code to see how database connections stored there. Singletons are the things to avoid, but sometimes you just need to choose a lesser of evils. – SpongeBobFan Apr 04 '13 at 13:29
  • hi guys.. I have created a singleton class that wraps the QSqlDatabase class and I have added some methods . The point is: does the QSqlDatabase use the singleton pattern? If it does using the singleton database is ok in this case, isn't ? – user2244311 Apr 04 '13 at 13:52
  • Qt keeps global static instances of `QSqlDatabase`'s mapping it with their `nonnectionName`'s. `QSqlDatabase` class itself is not a singleton, singletons don't have public constructors. Are you sure you need a singleton? Jueecy explains well why you should avoid using it. – SpongeBobFan Apr 04 '13 at 14:07
  • @SpongeBobFan so, if I have understood correctly, Qt uses a sort of more general singleton (sorry about the definition). It makes use of global values so from the point of view of the reusability and stuff it's not really good. Have I understoood well? – user2244311 Apr 04 '13 at 14:16
  • No, just sometimes you cannot avoid global objects, so Qt framework developers had to use global static variables sometimes for user's convenience. But you should avoid global objects when possible. In your case I'd create a class that open/close connections to database and refer to connections by `QSqlDatabase::database()`, or create singleton that contains a reference to `QSqlDatabase` object, open database connection when it is queried at first time and have a method to close connection when it is not needed anymore. – SpongeBobFan Apr 04 '13 at 14:39
  • Yes I have create a class that is not global but is a wrapper of the QSqlDatabase class: check below – user2244311 Apr 04 '13 at 15:15
  • It's looks right. You will have only one global instance of database connection, but I'm not sure you should have all the database-using logic in that class. If this class will grows up to 10+ methods, consider to add `QSqlDatabase& getSqlDatabase()` public method (instead of loadThisTable() and saveThisTable()) to it so another classes could use QSqlDatabase object by themselves. – SpongeBobFan Apr 04 '13 at 15:39
  • @SpongeBobFan yes I agree about that. I was going to do it but I needed a confirm. Thank you all guys, you were really useful! – user2244311 Apr 04 '13 at 15:54