What is the correct way to handle connections for QSqlDatabase?
In my program I am doing it this way:
DatabaseConnector *databaseConnector = 0;
try
{
databaseConnector = new DatabaseConnector();
//Do stuff...
delete databaseConnector;
}
catch(QString e)
{
delete databaseConnector;
QMessageBox::information(this,"Error",e);
}
databaseConnector.h
#ifndef DATABASECONNECTOR_H
#define DATABASECONNECTOR_H
#include <QtSql>
class DatabaseConnector
{
public:
DatabaseConnector();
DatabaseConnector(QString hostname, QString database, QString user, QString password);
~DatabaseConnector();
private:
QSqlDatabase db;
};
#endif // DATABASECONNECTOR_H
databaseconnector.cpp
#include "databaseconnector.h"
#include <QString>
DatabaseConnector::DatabaseConnector()
{
QSettings settings;
db = QSqlDatabase::addDatabase("QIBASE");
db.setHostName(settings.value("db/host").toString());
db.setDatabaseName(settings.value("db/name").toString());
db.setUserName(settings.value("db/user").toString());
db.setPassword(settings.value("db/pass").toString());
if(!db.open())
{
QString databaseConnectionError = db.lastError().text();
throw databaseConnectionError;
}
}
DatabaseConnector::DatabaseConnector(QString hostname, QString database, QString user, QString password)
{
db = QSqlDatabase::addDatabase("QIBASE");
db.setHostName(hostname);
db.setDatabaseName(database);
db.setUserName(user);
db.setPassword(password);
if(!db.open())
{
QString databaseConnectionError = db.lastError().text();
throw databaseConnectionError;
}
}
DatabaseConnector::~DatabaseConnector()
{
db.close();
}
I'm getting error even if I use QSqlDatabase::removeDatabase(db.connectionName());
QSqlDatabasePrivate::addDatabase: duplicate connection name 'qt_sql_default_connection', old connection removed.
Normally you don’t need to open the database connection more than once within your application.
When adding a database, you can name the connection :
QSqlDatabase::addDatabase( "QIBASE", "MyDBConnectionName" );
You can use the name to query for the connection :
if( QSqlDatabase::contains( "MyDBConnectionName" ) )
{
QSqlDatabase db = QSqlDatabase::database( "MyDBConnectionName" );
//Do stuff...
}
else
{
// connection not found, do something
}
Also notice that before calling QSqlDatabase::removeDatabase you should disconnect your database :
db.close();
QSqlDatabase::removeDatabase("MyDBConnectionName");
In order to add a new database, you need to give it a name. If you do not give a unique name, the default database is re-used. This is documented in the class reference.
Try:
db = QSqlDatabase::addDatabase("QIBASE", databaseName);
In main app:
QSqlDatabase db =QSqlDatabase::addDatabase( "QSQLITE");
in second app:
QSqlDatabase db2 =QSqlDatabase::database();
Related
I've made separate functions for open and close connection.But it wont let me to add new record on new form.
this is login header file.
public:
QSqlDatabase mydb;
void connClose()
{
//QString connection;
//connection = mydb.connectionName();
mydb.close();
//mydb.removeDatabase(connection);
mydb.removeDatabase(mydb.connectionName());
}
bool connOpen()
{
mydb=QSqlDatabase::addDatabase("QSQLITE");
mydb.setDatabaseName("./Poem.db");
if(mydb.open())
{
return true;
}
else if(!mydb.open())
{
return false;
}
}
this is the other form add button :
QString Title,Group,Poem;
Title = ui->lineEdit->text();
Group = ui->label_2->text();
Poem = ui->textEdit->toPlainText();
MainWindow mainwindow;
mainwindow.connOpen();
QSqlQuery * qry1 = new QSqlQuery(mainwindow.mydb);
qry1->prepare("insert into Poems(Title,Poem,Group) values ('"+Title+"','"+Poem+"','"+Group+"')");
if(qry1->exec())
{
QMessageBox::critical(this,tr("درج شعر جدید"),tr("شعر اضافه شد"));
mainwindow.connClose();
}
I get this errors :
duplicate connection name 'qt_sql_default_connection', old connection removed.
connection 'qt_sql_default_connection' is still in use, all queries will cease to work.
You are committing exactly what Qt QSqlDatabase Documentation warns about:
Warning: There should be no open queries on the database connection
when this function is called
...
// WRONG
QSqlDatabase db = QSqlDatabase::database("sales");
QSqlQuery query("SELECT NAME, DOB FROM EMPLOYEES", db);
QSqlDatabase::removeDatabase("sales"); // will output a warning
// "db" is now a dangling invalid database connection, // "query"
contains an invalid result set
and the correct is:
{
QSqlDatabase db = QSqlDatabase::database("sales");
QSqlQuery query("SELECT NAME, DOB FROM EMPLOYEES", db);
}
// Both "db" and "query" are destroyed because they are out of scope
QSqlDatabase::removeDatabase("sales"); // correct
So in your case you execute query qry1 and remove the database within same scope (i.e before qry1 goes out of scope), you should modify your code to make sure qry1 is executed and gets destroyed / goes out of scope / before deleting the database. Try this:
{
QSqlQuery * qry1 = new QSqlQuery(mainwindow.mydb);
qry1->prepare("insert into Poems(Title,Poem,Group) values ('"+Title+"','"+Poem+"','"+Group+"')");
if(qry1->exec())
{
QMessageBox::critical(this,tr("درج شعر جدید"),tr("شعر اضافه شد"));
}
}
mainwindow.connClose();
I have a folder where i have a many databases. Some times may be deleted or added database to the folder.
So I use QTimer and read all databases.
It is a my code:
this->timer = new QTimer(this);
this->timer->setInterval(15000);
connect(this->timer, &QTimer::timeout, this, [=]() {
QString path = "C:\\Users\\User\\Desktop\\DAXI SMS SENDER\\SMSSenderAllBASE";
//QString path = qApp->applicationDirPath() + "\\SMSSenderAllBASE";
QDir recoredDir(path);
QStringList allFiles = recoredDir.entryList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst);
for (int i = 0; i < allFiles.size(); i++) {
QString fullPath = path + "\\" + allFiles[i];
QString connectionName = allFiles[i];
connectionName = connectionName.remove(connectionName.size() - 4, 4);
QSqlDatabase db = QSqlDatabase::addDatabase("QIBASE", connectionName);
db.setDatabaseName(fullPath);
db.setHostName("localhost");
db.setPort(3050);
db.setUserName("SYSDBA");
db.setPassword("masterkey");
thrdHelperSendSMS *help = new thrdHelperSendSMS(db, this);
connect(help, &thrdHelperSendSMS::helperFinished, this, [=](QString connectionName){
QSqlDatabase t_db = QSqlDatabase::database(connectionName);
t_db.close();
QSqlDatabase::removeDatabase(connectionName);
delete help;
});
help->run();
}
});
this->timer->start();
Yes I'm sure that the helperFinished signal will happen and this time I will not have any connection with this base.
EDIT:
If i remove
thrdHelperSendSMS *help = new thrdHelperSendSMS(db, this);
connect(help, &thrdHelperSendSMS::helperFinished, this, [=](QString connectionName){
QSqlDatabase t_db = QSqlDatabase::database(connectionName);
t_db.close();
QSqlDatabase::removeDatabase(connectionName);
delete help;
});
help->run();
example:
for (int i = 0; i < allFiles.size(); i++) {
QString fullPath = path + "\\" + allFiles[i];
QString connectionName = allFiles[i];
connectionName = connectionName.remove(connectionName.size() - 4, 4);
QSqlDatabase db = QSqlDatabase::addDatabase("QIBASE", connectionName);
db.setDatabaseName(fullPath);
db.setHostName("localhost");
db.setPort(3050);
db.setUserName("SYSDBA");
db.setPassword("masterkey");
QSqlDatabase::removeDatabase(connectionName);
}
I have the same error.
You don't use removeDatabase() correctly. The object of SqlDatabase needs to go out of scope first. See documentation.
Wrong use
QSqlDatabase db = QSqlDatabase::database("sales");
QSqlQuery query("SELECT NAME, DOB FROM EMPLOYEES", db);
QSqlDatabase::removeDatabase("sales"); // will output a warning
Correct use
{
QSqlDatabase db = QSqlDatabase::database("sales");
QSqlQuery query("SELECT NAME, DOB FROM EMPLOYEES", db);
}
// Both "db" and "query" are destroyed because they are out of scope
QSqlDatabase::removeDatabase("sales"); // correct
In the second example db will go out of scope after } and you will no longer see the error message QSqlDatabasePrivate::removeDatabase: connection 'myConnectionName' is still in use, all queries will cease to work
Please read the documentation carefully. The database stuff is sensible and every line needs to be checked carefully.
Also you have missing db.close(); - It makes sense to close the database before you remove it.
#user3606329's answer is right, but I add this possibility:
QSqlDatabase db = QSqlDatabase::database("sales");
{
QSqlQuery query("SELECT NAME, DOB FROM EMPLOYEES", db);
//use query
}
db = QSqlDatabase();
QSqlDatabase::removeDatabase("sales");
You can use std::swap to force it destruct on demand.
I basically use BOOST_SCOPE_EXIT for cases when want to call a function on scope exit including not expected exit like through an exception throw.
#include <boost/scope_exit.hpp>
{
QSqlDatabase db;
BOOST_SCOPE_EXIT(this_, &db) { // by reference, otherwise it will copy a stack object
// access object here through the this_ instead of this ...
if (db.isOpen()) {
db.close(); // closing if not yet closed before connection remove
}
std::swap(db, QSqlDatabase{}); // destruct via swap
// CAUTION:
// From this point you must not call to `QSqlDatabase::database("MYDB", ...)`, otherwise it will return another constructed object!
//
QSqlDatabase::removeDatabase("MYDB");
} BOOST_SCOPE_EXIT_END
// ui change from here ...
// accomplish last ui changes before long blocking operation
qApp->processEvents();
db = QSqlDatabase::addDatabase("...", "MYDB");
// database access from here ...
}
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "conn_name");
db.open();
if (db.open())
{
qDebug()<<"DataBase is Open";
}
else
{
qDebug()<<"DataBase is Not Open";
}
QSqlQueryModel * model = new QSqlQueryModel();
QSqlQuery query(QSqlDatabase::database("conn_name"));
query.exec("SMTHING")
if (query.exec())
{
while (query.next())
{
ui->QTableView->setModel(model);
model->setHeaderData(2, Qt::Horizontal, QObject::tr("????"));
}
}
db.close();
QSqlDatabase::removeDatabase("conn_name");
Here is my code
I am having an extremely annoying problem and it doesn't make sense to me whats wrong. I am creating a multiple form ATM app but for some reason my DB is saying its open in the app but in the output it is throwing the error: QSqlQuery::exec: database not open
Here is the mainmenu.cpp file where I create the first instance of the DB
mainMenu::mainMenu(QWidget *parent) :QMainWindow(parent),ui(newUi::mainMenu)
{
ui->setupUi(this);
atmDB = QSqlDatabase::addDatabase("QSQLITE","myConnection");
atmDB.setDatabaseName(Path_to_DB);
QFileInfo checkFile(Path_to_DB);
}
Here in the login.cpp file it displays on the screen "Database connected"
login::login(QWidget *parent) :QDialog(parent),ui(new Ui::login)
{
ui->setupUi(this);
QSqlDatabase logindb;
logindb = QSqlDatabase::database("myConnection",true);
bool x = logindb.open();
if(!x){
ui->loginStatusL->setText("No connection to log-in database!");
}
else
ui->loginStatusL->setText("Database connected!");
}
But when the user pushes the button to login it it throws the SqlQuery error,
void login::on_loginLoginB_clicked()
{
QSqlDatabase loginB;
loginB = QSqlDatabase::database("myConnection",true); //open database
QString email, password;
email = ui->loginEmailbox->text();
password = ui->loginPassbox->text();
pubEmail = email;
if(!loginB.isOpen()){
ui->loginStatusL->setText("Connection to database lost");
return;
}
QSqlQuery qry;
if(qry.exec("SELECT * FROM userRecords WHERE email = '"+email+"' and password='"+password+"'")){
int count = 0;
while(qry.next()){
count++;
}
if(count==1){
userMenu usermenu;
atmDB.close();
usermenu.setModal(true);
usermenu.exec();
}
else
ui->loginStatusL->setText("Login failed. Email or password incorrect.");
}
}
I also have the same problem in my register.cpp file and it follows the same logic.
If you're looking to just know why wouldn't the QSqlQuery::exec() work, that's because you haven't told it anything about the database that it should run the query on.
The initialization should look more like:
QSqlDatabase loginB;
//code omitted...
QSqlQuery qry(loginDB); //Tell QSqlQuery which database to work with
//call qry.exe() here
I'd bet that it's the same problem in your register.cpp.
I started with c++ QT recently.
I created class "ControllerOfDB" to hold pointer to my QSqlDatabase and few functions (to make inserts/selects).
Example select function:
QList<data1> GetData1()
{
QList<data1> output;
if(!dataBase->isOpen())
dataBase->open();
if(dataBase->isOpen())
{
QSqlQuery* query = new QSqlQuery(*dataBase);
query->prepare("SELECT * FROM table1");
if(query->exec())
while (query->next())
{
output.append( *(new data1(
query->value(0).toInt(),
query->value(1).toString(),
query->value(2).toInt(),
query->value(3).toInt(),
query->value(4).toInt(),
)) );
}
query->clear();
query->finish();
delete query;
}
return output;
}
It was all working fine, until i had to add possibility to change db or login as different user. I modified Connect and Disconnect function. After few iterrations, this is what i get:
void Connect()
{
dataBase = new QSqlDatabase(QSqlDatabase::addDatabase("QPSQL", "Main"));
dataBase->setHostName(hostName);
dataBase->setPort(port);
dataBase->setDatabaseName(dbName);
dataBase->setUserName(userName);
dataBase->setPassword(userPass);
if(!dataBase->isOpen())
dataBase->open();
}
void Disconnect()
{
if(dataBase != NULL)
if(dataBase->isOpen()){
dataBase->close();
dataBase->removeDatabase("Main");
dataBase = NULL;
}
}
hostName, port, dbName, userName and userPass are also attributes of that class.
public:
QSqlDatabase *dataBase;
QString hostName;
int port;
QString dbName;
QString userName;
QString userPass;
whenever i try to diconnect and create new connection i recieve warnings/errors about queries of my current connections - even if i only "connected" ( = used function Connect() ).
I already saw few similar topics and documentation for it, saying i have to remove queries from scope, but at this point i don't know how.
when you look at QSqlDatabase document, the method removeDatabase is static, so your method Disconnect logic should be like:
void Disconnect()
{
if(dataBase != NULL) {
if(dataBase->isOpen()){
dataBase->close();
}
delete dataBase;
dataBase = NULL;
}
QSqlDatabase::removeDatabase("Main");
}
I have widget which connects to database:
Widget::Widget(QWidget *parent)
{
QString databaseName = "name";
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(databaseName);
db.setHostName("localhost");
if(!db.open())
qDebug()<<"ret error";
}
Now I want to delete database connection after widget close (currently I get warnings like: QSqlDatabasePrivate::removeDatabase: connection 'qt_sql_default_connection is still in use...). I' ve read some topics and tried to evaluate some solution from them but none works for me. My code:
void Widget::closeEvent(QCloseEvent *e)
{
QSqlDatabase db = QSqlDatabase::database();
QString connection = db.connectionName();
db.close();
QSqlDatabase::removeDatabase(connection);
qDebug()<<"error: "<<db.lastError().text();
}
Error I get is: Driver not loaded Driver not loaded
What is the correct way to do this?
Edit:
another method:
void Widget::someMethod()
{
QSqlDatabase db = QSqlDatabase::database();
QSqlQuery query(db);
query.exec("some query");
}
Try giving a connection name parameter(2nd parameter) in addDatabase() , that should solve your problem.
Here is the modified code you could try:
Widget::Widget(QWidget *parent)
{
QString databaseName = "name";
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "test_db_connection" );
db.setDatabaseName(databaseName);
db.setHostName("localhost");
if(!db.open())
qDebug()<<"ret error";
}
Here is a complete working code from my machine for sqlite database which you can use as reference:
local_db = QSqlDatabase::addDatabase("QSQLITE","localdb");
local_db.setDatabaseName("localdb.sqlite");
local_db_query = QSqlQuery(local_db);
local_db_query.prepare( "SELECT * FROM sample_table" );
local_db_query.exec();