QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", "MyDB1");
Второй необязательный параметр позволяет задать имя соединения.
Затем указывается имя сервера, название базы данных, имя пользователя и
пароль:
Если сервер использует нестандартный порт, то придётся задать и
его: db.setHostName("localhost"); // или, например, "my1.server.ru"
db.setDatabaseName("mydb1");
db.setUserName("root");
db.setPassword("mypassword");
В случае использования db.setPort(НомерПорта);
QODBC
имя сервера не требуется,
а вместо названия базы данных указывается ODBC-псевдоним (алиас).
SQLite не поддерживает авторизацию пользователей, поэтому ему требуется
указать только имя файла данных. Предопределённое имя ":memory:"
позволяет размещать временную базу данных в оперативной памяти.
После того, как все параметры подключения заданы, можно открыть соединение:
Если подключение установить не удалось, то не плохо бы узнать
описание ошибки и сообщить его пользователю: bool connected = db.open();
Если подключение установлено, то можно выполнить любой SQL-запрос,
например: if (!connected) {
QMessageBox::critical( // Диалог с сообщением об ошибке.
parent, // Родительский виджет.
QObject::tr("Database Error"), // Заголовок.
db.lastError().text()); // Текст сообщения.
return false; // Вернуть признак неудачного подключения.
}
или QSqlQuery sql;
sql.exec("SELECT id, name, salary FROM empl WHERE salary>=1000");
Здесь запрашиваются номера QSqlQuery sql("SELECT id, name, salary FROM empl WHERE salary>=1000");
id
, имена name
и оклады salary
всех работников из таблицы empl
, у
которых оклад не ниже 1 000. Обратите внимание, что если при создании
объекта QSqlQuery
указан текст запроса на языке SQL, то этот запрос
сразу выполняется.
В обоих случаях в конструкторе запроса можно указать базу данных
QSqlDatabase
, с которой он будет работать. Но как правило,
приложение открывает только одно соединение с единственной базой данных, поэтому
этот параметр принимает значение по умолчанию.
Если при выполнении запроса возникла ошибка, то метод
lastError()
позволяет вывести на экран её описание:
Иначе можно получить данные, которые сервер вернул в качестве
результата: if ( ! query.isActive() )
QMessageBox::warning(
this, tr("Database Error"),
query.lastError().text() );
Метод while ( sql.next() ) {
qint64 id = sql.value(0).toLongLong();
QString name = sql.value(1).toString();
double salary = sql.value(2).toDouble();
// .......
}
QSqlQuery::next()
переводит курсор на очередную
запись результирующего набора данных или возвращает false
, если
достигнут его конец. Метод value(номер_столбца)
возвращает значение
типа QVariant
, которое надо преобразовать к нужному типу с помощью
методов QVariant::toInt
, QVariant::toLongLong
,
QVariant::toString
, QVariant::toDouble
,
QVariant::toDate
, QVariant::toDateTime
и т.д.
Кроме next()
, для навигации по набору данных можно использовать
методы first()
, last()
, previous()
,
seek(int index, bool relative=false)
. Для увеличения быстродействия
набор данных лучше сделать однонаправленным, вызвав метод
QSqlQuery::setForwardOnly(true)
до выполнения запроса, после этого
можно использовать только next()
.
Метод QSqlQuery::size()
возвращает число записей, полученных в
результате выполнения запроса SELECT
(-1, если была ошибка или если
драйвер данной СУБД не поддерживает эту функцию). При выполнении SQL-запросов
INSERT
, UPDATE
или DELETE
вместо
size()
надо использовать метод
QSqlQuery::numRowsAffected()
. Чтобы узнать, возникла ли ошибка при
последнем выполнении запроса, используется метод
QSqlQuery::lastError()
. Аналогичный метод имеет и класс
QSqlDatabase
. В обоих случаях возвращается экземпляр класса
QSqlError
. Тип ошибки можно выяснить, вызвав метод
QSqlError::type()
. Возможные типы ошибок:
QSqlError::NoError
(ошибок не было),
QSqlError::ConnectionError
(ошибка соединения),
QSqlError::StatementError
(синтаксическая ошибка в SQL-запросе),
QSqlError::TransactionError
(ошибка транзакции) и
QSqlError::UnknownError
(неизвестная ошибка).
Если требуется выполнить большое количество однотипных SQL-операторов, то
эффективнее использовать запрос с параметрами:
или, что же самое (только параметры безымянные): QSqlQuery query;
sql.prepare("INSERT INTO empl (id, name, salary) "
"VALUES (:id, :name, :salary)");
for (int i=0; i<N; i++) {
sql.bindValue(":id", arr_id[i]);
sql.bindValue(":name", arr_name[i]);
sql.bindValue(":salary", arr_salary[i]);
sql.exec();
}
Здесь выполняется вставка QSqlQuery query;
sql.prepare("INSERT INTO empl (id, name, salary) "
"VALUES (?, ?, ?)");
for (int i=0; i<N; i++) {
sql.addBindValue(arr_id[i]);
sql.addBindValue(arr_name[i]);
sql.addBindValue(arr_salary[i]);
sql.exec();
}
N
записей, данные берутся из
массивов arr_id
, arr_name
и arr_salary
.
Если СУБД поддерживает механизм транзакций, то для начала новой транзакции
используется метод
для её подтверждения надо вызвать bool QSqlDatabase::transaction()
а для отмены: bool QSqlDatabase::commit()
Если СУБД не поддерживает транзакций, то вызовы
bool QSqlDatabase::rollback()
transaction
, commit
и rollback
ничего не
делают. С помощью метода QSqlDriver::hasFeature()
можно узнать,
поддерживается ли данным драйвером и СУБД та или иная функция, в том числе и
транзакции:
Каждое соединение с базой данных может иметь только одну активную
транзакцию. Если этого недостаточно, всегда можно открыть ещё несколько
соединений с той же базой данных.
QSqlDriver *driver = QSqlDatabase::database().driver();
if (driver->hasFeature(QSqlDriver::Transactions))
.......
Ссылку на соединение с базой данных можно получить, вызвав функцию
QSqlDatabase::database(connectionName)
. Необязательный параметр
connectionName
-- это имя соединения, которое было задано при его
создании с помощью QSqlDatabase::addDatabase()
.
По окончании работы с базой данных соединение надо закрыть:
QSqlDatabase::close()
. Затем можно либо открыть его снова с помощью
метода open()
, либо удалить из списка соединений, вызвав
статический метод QSqlDatabase::removeDatabase(connectionName)
.
Выполнение
SQL-запросов (система Windows, драйвер QODBC)
Выполнение SQL-запросов
(система Linux, драйвер QMYSQL)
В листингах приведён пример программы, работающей с базой данных, а на рис. показан результат её работы в Windows и Linux.
Листинг. Выполнение SQL-запросов (файл
examples-qt/db00/db00.h
)
1 #include <QtGui> 2 3 class MyDialog : public QDialog { 4 Q_OBJECT 5 public: 6 MyDialog(QWidget *parent=0); 7 protected: 8 virtual void closeEvent(QCloseEvent *event); 9 private slots: 10 bool start(); 11 private: 12 QComboBox *mode; // Режим (драйвер). 13 QLineEdit *host; // Хост. 14 QLineEdit *dbname; // Имя БД. 15 QLineEdit *user; // Пользователь. 16 QLineEdit *password; // Пароль. 17 QTextEdit *scr; // Для вывода сообщений. 18 QPushButton *btnStart; // Кнопка 'Старт'. 19 ;
Листинг. Выполнение SQL-запросов (файл
examples-qt/db00/db00.cpp
)
1 #include <QtGui> 2 #include <QtSql> 3 #include <QtTest/QtTest> // Для qWait(). 4 5 #include "db00.h" 6 7 MyDialog::MyDialog(QWidget *parent) 8 : QDialog(parent) { 9 QTextCodec *codec = QTextCodec::codecForName("CP1251"); 10 QTextCodec::setCodecForTr(codec); 11 QTextCodec::setCodecForCStrings(codec); 12 QTextCodec::setCodecForLocale(codec); 13 14 setWindowFlags(Qt::Window); 15 16 mode = new QComboBox(this); 17 QStringList drivers = QSqlDatabase::drivers(); 18 drivers.removeAll("QMYSQL3"); 19 drivers.removeAll("QOCI8"); 20 drivers.removeAll("QODBC3"); 21 drivers.removeAll("QPSQL7"); 22 drivers.removeAll("QTDS7"); 23 mode->addItems(drivers); 24 25 host = new QLineEdit(tr("localhost"), this); 26 dbname = new QLineEdit(this); 27 user = new QLineEdit(this); 28 password = new QLineEdit(this); 29 password->setEchoMode(QLineEdit::Password); 30 31 btnStart = new QPushButton(tr("Старт"), this); 32 33 scr = new QTextEdit(this); 34 scr->setReadOnly(true); 35 36 QGridLayout *layout = new QGridLayout(this); 37 layout->addWidget(new QLabel(tr("Режим:"), this), 38 0, 0, Qt::AlignRight); 39 layout->addWidget(mode, 0, 1, 1, 3); 40 41 layout->addWidget(new QLabel(tr("Хост:"), this), 42 1, 0, Qt::AlignRight); 43 layout->addWidget(host, 1, 1); 44 45 layout->addWidget(new QLabel(tr("База данных:"), this), 46 1, 2, Qt::AlignRight); 47 layout->addWidget(dbname, 1, 3); 48 49 layout->addWidget(new QLabel(tr("Пользователь:"), this), 50 2, 0, Qt::AlignRight); 51 layout->addWidget(user, 2, 1); 52 53 layout->addWidget(new QLabel(tr("Пароль:"), this), 54 2, 2, Qt::AlignRight); 55 layout->addWidget(password, 2, 3); 56 57 layout->addWidget(btnStart, 3, 1, 1, 2); 58 layout->addWidget(scr, 4, 0, 1, 4); 59 60 layout->setMargin(6); 61 layout->setSpacing(5); 62 layout->setColumnStretch(1, 1); 63 layout->setColumnStretch(3, 1); 64 layout->setRowStretch(4, 1); 65 setLayout(layout); 66 67 connect(btnStart, SIGNAL(clicked()), this, SLOT(start())); 68 } 69 70 bool MyDialog::start() { 71 scr->append(tr("Соединяюсь с базой данных...")); 72 QSqlDatabase db = QSqlDatabase::addDatabase( 73 mode->currentText() ); 74 db.setHostName(host->text()); 75 db.setDatabaseName(dbname->text()); 76 db.setUserName(user->text()); 77 db.setPassword(password->text()); 78 if (db.open()) { 79 mode->setEnabled(false); 80 host->setEnabled(false); 81 dbname->setEnabled(false); 82 user->setEnabled(false); 83 password->setEnabled(false); 84 btnStart->setEnabled(false); 85 scr->append(tr("Соединение установлено!")); 86 }else{ 87 scr->append(tr("Не могу соединиться: ")); 88 scr->append(db.lastError().text()); 89 return false; 90 } 91 92 QSqlQuery sql = QSqlQuery(); 93 //sql.exec(tr("SET NAMES 'cp1251'")); 94 QStringList dbtables = db.tables(QSql::Tables); 95 if (dbtables.contains( tr("employee"), 96 Qt::CaseInsensitive)) { 97 scr->append( tr( 98 "Таблица \"employee\" уже существует.")); 99 sql.exec(tr("DROP TABLE employee")); 100 if ( sql.lastError().type() == QSqlError::NoError ) { 101 scr->append( tr( 102 "Удалили таблицу \"employee\" ")); 103 }else{ 104 scr->append( tr( 105 "Не могу удалить таблицу \"employee\":")); 106 scr->append(sql.lastError().text()); 107 return false; 108 } 109 } 110 111 sql.exec( tr( 112 "create table employee ( " 113 " id integer PRIMARY KEY, " 114 " name char(30) not null, " 115 " born date null, " 116 " salary numeric(12,2), " 117 " married boolean NULL ) " ) ); 118 if ( sql.lastError().type() == QSqlError::NoError ) { 119 scr->append( tr( 120 "Создали таблицу \"employee\".")); 121 }else{ 122 scr->append( tr( 123 "Не могу создать таблицу \"employee\":")); 124 scr->append(sql.lastError().text()); 125 return false; 126 } 127 128 if (sql.prepare( tr( 129 "INSERT INTO employee " 130 " VALUES (?, ?, ?, ?, ?)") ) ) { 131 int arr_id[] = {123, 345, 501}; 132 QString arr_name[] = {tr("Винни-Пух"), 133 tr("Ослик Иа"), 134 tr("Поросёнок")}; 135 QDate arr_born[] = {QDate(1971, 12, 31), 136 QDate(1965, 2, 23), 137 QDate(1982, 6, 14)}; 138 float arr_salary[] = {1234.56f, 2345.67f, 871}; 139 int arr_married[] = {1, 0, 0}; 140 141 for (unsigned int i=0; i < 3; i++) { 142 sql.bindValue(0, arr_id[i]); 143 sql.bindValue(1, arr_name[i]); 144 sql.bindValue(2, arr_born[i]); 145 sql.bindValue(3, arr_salary[i]); 146 sql.bindValue(4, arr_married[i]); 147 sql.exec(); 148 if ( sql.lastError().type() == QSqlError::NoError ) { 149 scr->append( tr( 150 "Вставили новую запись.")); 151 }else{ 152 scr->append( tr( 153 "Не могу вставить новую запись:")); 154 scr->append(sql.lastError().text()); 155 return false; 156 } 157 } 158 }else{ 159 scr->append( tr( 160 "Не могу подготовить запрос:")); 161 scr->append(sql.lastError().text()); 162 return false; 163 } 164 165 sql.exec( tr("SELECT * FROM employee ") ); 166 if ( sql.isActive() ) { 167 QSqlRecord rec = sql.record(); 168 scr->append( tr( 169 "В таблице \"employee\" %1 столбцов: ") 170 .arg(rec.count() ) ); 171 172 QString fields; 173 for(int j=0; j<rec.count(); j++) 174 fields += rec.fieldName(j) + ", "; 175 176 scr->append(fields); 177 178 scr->append( tr( 179 "В таблице \"employee\" %1 записей: ") 180 .arg(sql.size() ) ); 181 182 while ( sql.next() ) { 183 int id = sql.value(0).toInt(); 184 QString name = sql.value(1).toString(); 185 QDate born = sql.value(2).toDate(); 186 double salary = sql.value(3).toDouble(); 187 bool married = sql.value(4).toBool(); 188 scr->append( tr( 189 "%1\t %2\t %3\t %4\t %5") 190 .arg(id) 191 .arg(name) 192 .arg(born.toString(tr("dd/MM/yyyy"))) 193 .arg(salary) 194 .arg(married) ); 195 } 196 }else{ 197 scr->append( tr( 198 "Не могу получить данные:")); 199 scr->append(sql.lastError().text()); 200 return false; 201 } 202 203 scr->append( tr( 204 "При закрытии окна соединение с БД будет завершено.")); 205 return true; 206 } 207 208 void MyDialog::closeEvent(QCloseEvent *event) { 209 QSqlDatabase db = QSqlDatabase::database(); 210 if (db.isOpen()) { 211 db.close(); 212 scr->append("--------------------------"); 213 scr->append(tr("Соединение с базой данных закрыто!")); 214 QTest::qWait(1000); // Ждать 1 сек. 215 } 216 } 217 218 int main(int argc, char *argv[]) { 219 QApplication app(argc, argv); 220 221 MyDialog *mainWin = new MyDialog(); 222 mainWin->show(); 223 return app.exec(); 224 }
Qt::Windows
, чтобы в
заголовке окна появились кнопки сворачивания и восстановления.
Старт
.
Старт
с функцией start()
.
employee
(работники).
closeEvent
выполняется при закрытии
окна.
QtTest
, в файл проекта
*.pro
надо добавить строку
CONFIG += qtestlib
. Перед компиляцией проекта, в котором используется модуль QtSql
,
надо добавить в pro
-файл строку QT += sql
.
Перед выполнением этой программы требуется создать какую-нибудь базу данных:
create database db1 character set utf8;
(вместо utf8
можно использовать кодировки cp1251
или koi8r
); а перед использованием QODBC
-- настроить
ODBC-псевдоним.