Russian Qt Forum
Март 28, 2024, 12:57 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3   Вниз
  Печать  
Автор Тема: QST: QsT SQL Tools  (Прочитано 45016 раз)
GraninAS
Гость
« : Декабрь 09, 2009, 12:03 »

Добрый день, уважаемые коллеги!

Хочу поделиться своим инструментарием QsT Sql Tools, да не просто так, а с корыстными целями: мне нужна помощь по выявлению ошибок, особенно это касается SqlTreeModel - древовидной модели данных.

Вкратце о проекте.

QsT Sql Tools - это набор классов (который пока трудно назвать библиотекой), облегчающий в разы программирование приложений баз данных на Qt. Основная цель банальна: абстрагироваться от "лапши", которая появляется при внесении SQL-запросов в код. Делается это, разумеется, путем генерации SQL-запросов. Но куда менее были бы полезными мои классы, если бы они не позволяли работать с полями поименно, создавать запросы не просто в виде строки, а виде неких структур (даже можно сказать - классов), где потом можно поменять, к примеру, значение какого-нибудь фильтра... И, самое главное, облегчается настройка модели/представления.

Да не просто облегчается, а практически исчезает. В общем, я буду рад, если вам понравится.

Что хотелось бы взамен? Помощи. Плохо работает SqlTreeModel. Во-первых, модель только для чтения. Во-вторых, выдает странности в некоторых моментах. Моменты можно увидеть в прикрепленном здесь примере. С ним я уже обращался на один из форумов, но там молчат как рыбы. Надеюсь, что вы не оставите меня, так как хотелось бы довести библиотечку до нужной кондиции. Она того стоит.

Итак, во вложении вы найдете эту несчастную SqlTreeModel. Я понимаю, что вопросы, подобные моему, всплывают и здесь, и вообще везде. Сам рыл, причины искал, и понял, что чего-то не знаю. Вообще, хочется переписать SqlTreeModel, чтобы не использовать new или другое явное выделение памяти, - это дюже нехорошо. Но сначала надо понять, что я сделал не так...

Сам инструментарий находится здесь: https://sourceforge.net/projects/qstsqltools/
Там же есть пример базы данных, написанной с его помощью. Какое-то время он там полежит, потом я его уберу, - в нем классы еще даже без шапки и без лицензии, а это не есть гуд.

Итак, с волнением жду вашей реакции. Заранее спасибо, - хоть эти два слова и набили всем оскомину. Улыбающийся
« Последнее редактирование: Декабрь 10, 2009, 03:47 от GraninAS » Записан
panAlexey
Гипер активный житель
*****
Offline Offline

Сообщений: 864

Акцио ЗАРПЛАТА!!!!! :(


Просмотр профиля
« Ответ #1 : Декабрь 09, 2009, 12:40 »

Интересно. Поставлю закладку. Улыбающийся
Записан

Win Xp SP-2, Qt4.3.4/MinGW. http://trdm.1gb.ru/
viktor.kz
Гость
« Ответ #2 : Декабрь 09, 2009, 13:12 »

Error in 'Icons.qrc': Cannot find file 'Resources/Icons/arrow-circle-double-135.png'

Ресурсики бы ещё получить бы ...
Записан
GraninAS
Гость
« Ответ #3 : Декабрь 10, 2009, 02:46 »

P.S. от 7 апреля 2010
Ссылка на ресурсы более не актуальна. Пример использования QST убран с сайта. Скоро выложу новый пример.
P.P.S от 12 апреля 2010
Выложил новый пример.

Ресурсики бы ещё получить бы ...

Да, действительно, я о них забыл... Добавил отдельным файлом, вот: http://sourceforge.net/projects/qstsqltools/files/TradeDB%20Resources.rar/download.
Весят много, 4мб. Это Fugue Icons (http://code.google.com/p/fugue-icons/). И хотя используются далеко не все, залил полную папку.

Скопируйте ее в папку проекта. Должно скомпилироваться... Правда, давно не проверял исходники примера. Они могут быть "грязными".
« Последнее редактирование: Апрель 12, 2010, 03:00 от GraninAS » Записан
GraninAS
Гость
« Ответ #4 : Декабрь 10, 2009, 03:45 »

Интересно. Поставлю закладку. Улыбающийся

Могу рассказать подробнее... Хотите?

...Ммм, ну так вот. Самое интересное: генерация SQL. Вообще, для QST как-то сама собой разработалась концепция DFD - Declarative Field Descriptor. (Я понимаю, что звучит это масштабно, не в пример самой библиотечке, но у меня такая слабость - ставить высокие цели и называть их соответствующим образом.) По ней, простые запросы SQL мысленно разбиваются на "поля" и "значения" с разными свойствами, из которых потом можно собрать пакет. Пример скажет лучше.
Код:
SQL:
SELECT Name, Birthday, Birthplace, ID
FROM tPersons
WHERE
Age > 30
AND
Name LIKE 'Алекс%'

DFD:
SqlBatch batch;
batch.addSource("tPersons");

batch << SqlField("Name", fv_visible, fr_none, "Имя", 100)
<< SqlField("Birthday", fv_visible, fr_none, "Дата рождения", 80)
<< SqlField("Birthplace", fv_visible, fr_none, "Место рождения", 120)
<< SqlField("ID", fv_invisible, fr_id)

<< SqlField("Age", SqlValue(30, fo_greater), fp_where)
<< SqlField("Name", SqlValue("Алекс", fo_like, fb_right), fp_where);
Генератор принимает два параметра:
Код:
SqlGen gen;
QString query = gen.query(batch, sql_select);
Но вообще-то о его работе задумываться стоит только тогда, когда нужно понять/поменять алгоритм генерации. Сама генерация скрыта. Дело в том, что главной единицей для оперирования как DFD-описателями, так и моделями/представлениями становится класс, отнаследованный от AbstractModelHandler. Фактически, класс представляет собой функции, создающие DFD под конкретную задачу. Например, для работы с определенной таблицей БД. Конечно, никто не мешает в этом классе сделать дополнительные функции, например, что-то вроде addPerson(), updatePerson(), deletePerson() и т.д., которые бы сами создавали нужный DFD-описатель, а программист просто их вызывал с параметрами. Вот пример из другой БД, которую сейчас пишу:
Код:
#include "abstractmodelhandler.h"

using namespace Sql;

const QString GLOBAL_PARAMETERS_SOURCE = "ParamS";

const int GLOBAL_PARAMETERS = 3335;

const int GLOBAL_PARAMETER_CLOSE_PROC = 41;

class h_GlobalParametersHandler : public AbstractModelHandler
{
public:
    h_GlobalParametersHandler();

bool updateParameter(const SqlValue &oldId,
const SqlValue &name,
const SqlValue &stringValue,
const SqlValue &intValue,
const SqlValue &floatValue,
const SqlValue &description,
const SqlValue &dateBegin,
const SqlValue &dateEnd);

private:

virtual SqlQueryDescriptor _selector(const SqlQueryModelTypes &modelType = mt_plain,
const int &queryNumber = 0) const;
virtual SqlQueryDescriptor _inserter(const int &queryNumber = 0) const;
virtual SqlQueryDescriptor _updater(const int &queryNumber = 0) const;
virtual SqlQueryDescriptor _deleter(const int &queryNumber = 0) const;
virtual SqlQueryDescriptor _executor(const int &queryNumber = 0) const;
};
[code]
И определение класса:
[code]
bool h_GlobalParametersHandler::updateParameter(const SqlValue &oldId,
const SqlValue &name,
const SqlValue &stringValue,
const SqlValue &intValue,
const SqlValue &floatValue,
const SqlValue &description,
const SqlValue dateBegin,
const SqlValue dateEnd)
{
h_GlobalParametersHandler th;
th.setValue(ID_VALUE, oldId);
th.setValue("DateAfter", dateBegin);
bool res = th.Exec(GLOBAL_PARAMETER_CLOSE_PROC);
Q_ASSERT(res);

th.clearValues();
th.setValue("Name", name);
th.setValue("StringValue", stringValue);
th.setValue("IntValue", intValue);
th.setValue("FloatValue", floatValue);
th.setValue("Description", description);
th.setValue("DateBegin", dateBegin);
th.setValue("DateEnd", dateEnd);
res = th.Insert();

return res;
}

SqlQueryDescriptor h_GlobalParametersHandler::_selector(const SqlQueryModelTypes &modelType,
const int &queryNumber) const
{
SqlBatch batch;

batch.addSource("vGlobalParams");

if (queryNumber == GLOBAL_PARAMETERS)
{
batch << SqlField("ID", fv_invisible, fr_id)
<< SqlField("Name", fv_visible, fr_none, "Название", 100)
<< SqlField("StringValue", fv_visible, fr_none, "Строка", 120)
<< SqlField("IntValue", fv_visible, fr_none, "Целое", 50)
<< SqlField("FloatValue", fv_visible, fr_none, "Действит.", 70)
<< SqlField("Description", fv_visible, fr_none, "Описание", 120)
<< SqlField("vcDateBegin", fv_visible, fr_none, "Начало действия", 100)
<< SqlField("vcDateEnd", fv_visible, fr_none, "Конец действия", 100)
<< SqlField("vcIsUsed", fv_visible, fr_none, "Актив.?", 60)

<< SqlField("DateBegin", fv_invisible)
<< SqlField("DateEnd", fv_invisible)

<< SqlField("ID", value(ID_VALUE), fp_where)

<< SqlField("Name", value("Name"), fp_where)
<< SqlField("IsUsed", value("IsUsed"), fp_where)
<< SqlField("DateBegin", value("DateBegin"), fp_where)
<< SqlField("DateEnd", value("DateEnd"), fp_where);
}
else
if (queryNumber == LAST_ID)
{
batch << SqlField(MAX_ID, fv_visible, fr_none);
}
else
{
Q_ASSERT(false);
}

return SqlQueryDescriptor(batch, sql_select, queryNumber);
}

SqlQueryDescriptor h_GlobalParametersHandler::_inserter(const int &queryNumber) const
{
SqlBatch batch;

batch.addSource("tGlobalParams");

batch << SqlField("Name", value("Name"), fp_insert)
<< SqlField("StringValue", value("StringValue"), fp_insert)
<< SqlField("IntValue", value("IntValue"), fp_insert)
<< SqlField("FloatValue", value("FloatValue"), fp_insert)
<< SqlField("Description", value("Description"), fp_insert)
<< SqlField("DateBegin", value("DateBegin"), fp_insert)
<< SqlField("DateEnd", value("DateEnd"), fp_insert);

return SqlQueryDescriptor(batch, sql_insert, queryNumber);
}

SqlQueryDescriptor h_GlobalParametersHandler::_updater(const int &queryNumber) const
{
SqlBatch batch;

batch.addSource("tGlobalParams");

batch << SqlField("Name", value("Name"), fp_update)
<< SqlField("StringValue", value("StringValue"), fp_update)
<< SqlField("IntValue", value("IntValue"), fp_update)
<< SqlField("FloatValue", value("FloatValue"), fp_update)
<< SqlField("Description", value("Description"), fp_update)
<< SqlField("DateBegin", value("DateBegin"), fp_update)
<< SqlField("DateEnd", value("DateEnd"), fp_update)

<< SqlField("ID", value(ID_VALUE, SqlValue(nt_null)), fp_where);

return SqlQueryDescriptor(batch, sql_update, queryNumber);
}

SqlQueryDescriptor h_GlobalParametersHandler::_deleter(const int &queryNumber) const
{
SqlBatch batch;

batch.addSource("tGlobalParams");

batch << SqlField("ID", value(ID_VALUE, SqlValue(nt_null)), fp_where);

return SqlQueryDescriptor(batch, sql_delete, queryNumber);
}

SqlQueryDescriptor h_GlobalParametersHandler::_executor(const int &queryNumber) const
{
SqlBatch batch;

if (queryNumber == GLOBAL_PARAMETER_CLOSE_PROC)
{
batch.addSource("pCloseGlobalParameter");
batch << SqlValue(value(ID_VALUE, SqlValue(nt_null)))
<< SqlValue(value("DateAfter"));
}
else
{
Q_ASSERT(false);
}

return SqlQueryDescriptor(batch, sql_execute, queryNumber);
}
Закономерный вопрос: что эта вся мишура дает? Отвечу кодом. Сначала часть класса формы:
Код:
h_GlobalParametersHandler _handler
SqlQueryModel _model;
Затем некоторые функции класса формы:
Код:
void f_GlobalParametersForm::loadParameters()
{
// Загружаем источник данных. Берем select-запрос из h_GlobalParametersHandler::_selector() с номером GLOBAL_PARAMETERS.
_handler.reloadSource(GLOBAL_PARAMETERS_SOURCE, GLOBAL_PARAMETERS, &_model);
// Настраиваем tableView на источник данных. Названия колонок, их ширина, ориентация, - все берется из описателя DFD, сформированного функцией _selector с номером запроса GLOBAL_PARAMETERS и типом модели mt_plain (если бы использовался TreeView, тип модели был бы mt_tree).
_handler.setTableView(GLOBAL_PARAMETERS_SOURCE, ui->tv_Parameters);
}

SqlValue f_GlobalParametersForm::parameterID() const
{
// Ключевое значение текущей строки во view.
return _handler.keyValueOfView(GLOBAL_PARAMETERS_SOURCE);
}

// Удаление текущей строки во view из БД.
void f_GlobalParametersForm::deleteParameter()
{
d_ConfirmDialog dlg;
if (dlg.exec() == QDialog::Accepted)
{
_handler.DeleteCurrent(GLOBAL_PARAMETERS_SOURCE, ui->tv_Parameters);
loadParameters();
}
}

...И некоторые другие фишки.

Хотя, многого SQT не хватает. Например, создавать более сложные запросы или хотя бы те, где в секции WHERE есть OR. Я пока думаю над этим. Кое-какие мысли уже есть.[/code][/code]
« Последнее редактирование: Декабрь 10, 2009, 10:15 от GraninAS » Записан
daimon
Гость
« Ответ #5 : Декабрь 10, 2009, 09:43 »

Ваша Sql модель позволяет находу добавлять колонки и записывать их в базу и добавлять не в конец, а например в середину?
Записан
lit-uriy
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3880


Просмотр профиля WWW
« Ответ #6 : Декабрь 10, 2009, 10:10 »

daimon, в БД нельзя добавлять не в конец таблицы
Записан

Юра.
GraninAS
Гость
« Ответ #7 : Декабрь 10, 2009, 10:15 »

Ваша Sql модель позволяет находу добавлять колонки и записывать их в базу и добавлять не в конец, а например в середину?
А что подразумевается под колонками в базе, и о какой модели вы спрашиваете: SqlTreeModel или SqlQueryModel?

В любом случае, древовидная Sql-модель - нет. Она вообще ущербная, надо сказать. Ничего, кроме чтения данных, не умеет, да и там могут быть ошибки (см. пример в аттаче к первому сообщению).

Если вы имеете в виду поля какой-то таблицы, то - нет, этого инструментарий не умеет. Это уже не простой SQL-запрос получается, а целый ALTER TABLE. В подавляющем большинстве случаев такие запросы в приложении баз данных не нужны. К простым же запросам я отношу следующие: SELECT, INSERT, UPDATE, DELETE, EXEC(UTE).

Если вы имеете в виду записи какой-то таблицы, то - нет, это не умеет SqlTreeModel, но умеет SqlQueryModel (поскольку она незначительно отличается от родителя - QSqlQueryModel). И еще это умеет хэндлер - класс, инкапсулирующий работу с SQL-запросами к конкретной таблицей базы данных (или к нескольким, как уж напишете), правда, чтобы он это умел, нужно прописать дескриптор функции _inserter().

Я ответил на ваш вопрос?


UPD

И да: без танцев с бубном в середину таблицы записи не добавишь.
« Последнее редактирование: Декабрь 10, 2009, 10:17 от GraninAS » Записан
GraninAS
Гость
« Ответ #8 : Декабрь 16, 2009, 02:50 »

Написал статью на Хабр о QST. В ней - более развернуто о многом.

http://graninas.habrahabr.ru/blog/78394/
Записан
GraninAS
Гость
« Ответ #9 : Апрель 07, 2010, 07:08 »

Уважаемые господа!

Я закончил QST 0.3. Теперь из категории pre-alpha она переместилась в категорию beta. Много ли сделано?

* Исправлены ошибки. Тонны ошибок. Но не все. До сих пор меня мучает та самая древовидная модель данных, - ну и шут с ней. Пользоваться можно. В версии 0.4 напишу новую, толковую модель, благо, теперь разобрался, что там к чему в системе Interview.
* Ошибки исправлялись за счет создания модульных тестов. Пока их 4 для следующих классов: SqlBatch, SqlValue, SqlGen и SqlField.
* Функции этих четырех классов теперь документированы модульными тестами. Их поведение полностью предсказуемо.
* Кое-что изменилось в интерфейсах классов. Версии pre-alpha и beta во многом не совместимы.
* Полностью написана документация. Не то чтобы очень подробная, но, мне кажется, ее вполне хватает, - вкупе с материалами, которые выкладываю в Интернете.
* Пример, выложенный для QST 0.3 pre-alpha (простая БД о торговле) является примером, как лучше не делать. С тех пор я обкатал новые техники и способы программирования при помощи QST, многое упростилось и стало понятным, менее многословным. Старый пример с SourceForge убрал, новый пример скоро будет.

Более развиваться ветка 0.3 не будет, за исключением незначительных правок и оставшихся модульных тестов. На замену устаревшему коду я пишу 0.4, в большинстве случаев не совместимую с предшественницей.

Возможности QST 0.4 в сравнении с QST 0.3
* Убрано понятие "источники данных" класса AbstractModelHandler. Оказалось, что они не нужны и сильно мешаются. Легче создать два разных экземпляра своего класса-хэндлера, чем создавать в одном классе-хэндлере несколько источников данных и потом в них путаться.
* Полный рефакторинг имен: пространств, классов, перечислений, типов и функций. За основу именования взят стиль именования Qt 4.5 и выше. Главное правило: чтобы все было логично и очевидно.
* Пересматриваются возможности DFD-языка. Планирую ввести древовидные описатели с тем, чтобы можно было создавать SQL-запросы с подзапросами.
* Расширяется сам язык DFD. Вводится поддержка некоторых других элементов SQL.
* Планируется сделать заменяемый движок компоновки SQL с тем, чтобы легко можно было написать поддержку иного диалекта нежели T-SQL. Это, разумеется, потребует многих изменений в структуре проекта.
* Упрощение. Сейчас, например, уже упрощены функции DFD-генерации (_selector(), _inserter() и т.д.). Они теперь возвращают QstBatch. Все остальное (тип и номер запроса) уже известно перед вызовом функций, зачем заставлять программиста это еще раз указывать?
* И некоторые другие интересные штуки. Например, кэширование и динамически изменяемые DFD-описатели.

Как всегда, исходники можно взять здесь:
* 0.3 beta: http://sourceforge.net/projects/qstsqltools/files/qst%200.3%20beta/QST%200.3%20beta.rar/download
* 0.3 pre-alpha: https://sourceforge.net/projects/qstsqltools/files/qst%200.3%20pre-alpha/QST%200.3%20pre-alpha.rar/download

Или слить 0.3 beta с SVN: https://qstsqltools.svn.sourceforge.net/svnroot/qstsqltools

Скоро выложу какие-нибудь хорошие примеры и зачатки версии 0.4.
« Последнее редактирование: Апрель 07, 2010, 08:33 от GraninAS » Записан
GraninAS
Гость
« Ответ #10 : Апрель 12, 2010, 02:47 »

Слегка обновил QST 0.3 beta, теперь она стала QST 0.3c beta. Что изменилось? Обновил заголовки копирайтов, а то они на прошлый год записаны. Из функционала добавил тип в файл sqlvalue.h:

typedef QMap<QString, SqlValue>      SqlValuesMap

И две функции в класс AbstractModelHandler, аналогичные value и setValue, но работают с картой значений:

AbstractModelHandler::setValuesMap(const SqlValuesMap &valMap)
SqlValuesMap AbstractModelHandler::valuesMap() const

Версию 0.3 beta оставляю тоже, 0.3c beta кладу рядом.

Дополнительно, создал специальную программу для демонстрации некоторых возможностей QST 0.3, а точнее, ту же TradeDB, переписанную с нуля с применением некоторых важных установок. В частности, очень ратую за то, чтобы вместо _inserter(), _updater() и _deleter() вызывались хранимые процедуры. Во-первых, всегда можно поменять алгоритм работы. Во-вторых, проще отследить. В-третьих, можно раздать права. В-четвертых, логика разделена, и это очень хорошо снижает ошибкоопасность.

Программа использует базу данных SQL Server 2000 (которая, кажется, цепляется и к более новым версиям сервера). Источник данных называется Trade, пользователь – user с таким же паролем. Подключение можно изменить в файле main.cpp программы-примера. Ресурсы (Fugue Icons) так же выкладываю рядом с программой. Скопировать внутреннюю папку Resources (в которой лежит непосредственно 4 объекта: Icons, all-shadowless.png, history.txt и readme.txt) в папку проекта. Выкладываю раздельно потому, что размер ресурсов довольно большой, почти 5 мб. Возможно, кому-то иконки не нужны.

Зачатки QST 0.4 на подходе.

Кстати, принимаются предложения по усовершенствованию. Или запросы каких-либо функций. В общем, любой feedback категорически приветствуется!

С уважением.

QST 0.3c beta

TradeDB 0.3 special src

TradeDB 0.3 special Resources

TradeDB 0.3 special database

UPD

При попытке залить что-то из TradeDB мой файерфокс уходит в какой-то странный цикл. Что бы это могло быть? Подозреваю длинные имена файлов.

UPD2

Видимо, это SourceForge трясется в лихорадке. Перезалил пример. Скачивается все, кроме ресурсов. Подождем, пока зеркала договорятся о том, что есть "новая информация"...

UPD3

Кажется, все заработало.
« Последнее редактирование: Апрель 12, 2010, 03:29 от GraninAS » Записан
BaltikS
Гость
« Ответ #11 : Апрель 12, 2010, 22:12 »

GraninAS, без обид, кроме тебя этим кто нить пользуется? У меня создалось впечатление что нет. Потому всё это как то не удобно... Многие конструкции SQL-языка не поддерживаются, да и проще по-моему QSqlQuery и его производные модели юзать... С вашей моделью, я так понимаю, быстрее всё равно работать не будет.  Если не прав-поправьте....может я чего то недопонял...
Цитировать
QsT Sql Tools - это набор классов (который пока трудно назвать библиотекой), облегчающий в разы программирование приложений баз данных на Qt
Насчёт в разы категорически не согласен. Потому как есть визуальные построители запросов, которые на выходе генерируют SQL запрос, вот это действительно ускоряет работу. А разрабатывать клиента БД, не зная основ БД по-моему как-то не особо получится, даже используя вашу библиотеку!
Записан
GraninAS
Гость
« Ответ #12 : Апрель 13, 2010, 02:58 »

Да нет, какие обиды, мне самому интересен ответ на ваш первый вопрос. Кто-то еще пользуется? Не знаю, может быть. Код скачивают, примеры скачивают, даже вчера предложили услуги по тестированию.

SQL поддерживается на самом-самом базовом уровне, верно. И поэтому уйти от знания SQL с помощью библиотеки невозможно: придется писать view'шки, хранимые процедуры. Но задача ORM-системы и не в том, чтобы заменить SQL. Вряд ли это возможно, я себе такое представить не могу. Главная задача ORM - избавиться от лапша-style-кода. Чтобы изменить одно-единственное поле в какой-нибудь таблице БД и не бегать по всем файлам проекта в поисках, где же это поле встречается. Чтобы можно было настраивать запрос перед его выполнением, и при том единообразно настраивать, чтобы смысл оставался прозрачным. Ну а раз все равно при таких требованиях запрос придется генерировать, так почему не поднять над ним абстракцию, которая бы, помимо прочего, позволяла обращаться к полям не по их индексу, а по имени?

Это извечный холивар - использовать ORM в ущерб производительности или писать "чистые" запросы в коде. Я только утверждаю, что ORM делает работу удобнее и безопаснее, хотя и несколько увеличивает локальные затраты времени на код. Судите сами: написать запрос или сгенерировать его в каком-нибудь визуальном компоненте - дело двух минут, а вот создать тот же класс-хэндлер в моей QST, - на это надо минут 7 при том, что и запрос написать придется тоже, если вы не можете его в уме составить. Однако, в конечном итоге вы напишете класс-хэндлер один раз и будете везде его использовать; "чистые" запросы придется писать постоянно.

А отладка и тестирование! Они облегчаются в разы. Смотрите: регрессия ошибок от того, что изменилась структура БД, при использовании QST минимальна и сосредоточена всего лишь в одном файле; тогда как "чистые" запросы могут быть разбросаны повсеместно. И даже если не разбросаны, в них будет нечто вроде:

\\1.cpp
QString query = QString("SELECT ID, Name, Surname WHERE Age > " + QString::number(ageParameter));

Но это очень простой запрос. А что если вам понадобится не один параметр в секцию WHERE, а десять? Или в одном месте программы - один параметр, а в другом - другой? Количество запросов возрастает:

\\2.cpp
QString query = QString("SELECT ID, Name, Surname WHERE Age > " + QString::number(ageParameter)) + " AND Name LIKE %" + nameParameter + "%");

\\3.cpp
QString query = QString("SELECT ID, Name, Surname WHERE Age <= 10 AND SurName LIKE %" + nameParameter + "%" AND Address LIKE %'" + addressParameter.arg("ул. Лесная").arg(25).arg(6) + "%'");

Сколько здесь допущено ошибок, как вы думаете? Не меньше шести, есть и синтаксические. Отладка такого кода становится кошмаром. Могу вас уверить, QST от этого всего избавляет сразу и навсегда. И, например, чтобы посмотреть, какой там запрос выполнился, достаточно взглянуть на окно Applicaton Output в Qt Creator'е: там все запросы выводятся, уже чистые SQL-запросы, которые можно скопировать и протестировать отдельно. Я тестирую, например, с помощью Query Analyser'а. Он мне сразу же говорит, что у меня нет поля Surname, - я смотрю, какое есть, - и изменяю его в одном месте. А сколько телодвижений сделаете вы, прежде чем выясните причину ошибки? В этом и есть разница между "чистыми" SQL-запросами и ORM-обертками, в этом и есть удобство. Раз убедившись в этом, уже вряд ли будешь думать иначе.
Записан
BaltikS
Гость
« Ответ #13 : Апрель 13, 2010, 09:00 »

Отладка такого кода становится кошмаром. Могу вас уверить, QST от этого всего избавляет сразу и навсегда. И, например, чтобы посмотреть, какой там запрос выполнился, достаточно взглянуть на окно Applicaton Output в Qt Creator'е: там все запросы выводятся, уже чистые SQL-запросы, которые можно скопировать и протестировать отдельно. Я тестирую, например, с помощью Query Analyser'а. Он мне сразу же говорит, что у меня нет поля Surname, - я смотрю, какое есть, - и изменяю его в одном месте. А сколько телодвижений сделаете вы, прежде чем выясните причину ошибки? В этом и есть разница между "чистыми" SQL-запросами и ORM-обертками, в этом и есть удобство. Раз убедившись в этом, уже вряд ли будешь думать иначе.
всё таки позволю с вами не согласится...
1) если однотипный запрос используется много раз, то используется view. И если меняется таблица, меняется и view и мне не нужно править код.
2) По поводу поиска ошибок в коде. Это далеко не кошмар, есть опять таки визуальные построители запросов, которые избавляют пользователя от отладки запросов. А если необходимо всё таки отлаживаться имеется мощный отладчик.  И не приходится сравнивать того что выводит отладчик с тем, что должно быть.
3)
Цитировать
позволяла обращаться к полям не по их индексу, а по имени
есть такой метод QSqlRecord::field ( const QString & name ) const, который позволяет обращаться по имени.
4) Мой вывод таков: чтобы использовать библиотеку, нужно
Цитировать
поэтому уйти от знания SQL с помощью библиотеки невозможно: придется писать view'шки, хранимые процедуры
, что займёт время. К тому же
Цитировать
использовать ORM в ущерб производительности
как то тоже не здорово. В итоге, для себя я уяснил, что зачем это использовать, если время займёт на разработку больше? Это как писать приложения Qt на QtScript, вроде бы просто, но ....

GraninAS, спасибо за подробное разъяснение Улыбающийся
« Последнее редактирование: Апрель 13, 2010, 09:27 от BaltikS » Записан
Marat(Qt)
Гость
« Ответ #14 : Апрель 14, 2010, 12:46 »

Итак GraninAS привел свое решение, BaltikS свое. На мой взгляд, оба правы, но не во всем.
Лапша, которую привел GraninAS - не лучший метод написания запросов стандартными средствами Qt, есть bindValue, который упрощает код (хотя я, в основном, использую QString::arg). Но и bindValue не так хорош как идея QST. Как проверить запрос в самом Qt, не используя ничего со стороны? QSqlQuery::lastError - не лучший вариант (опять же IMHO), ибо результат виден только во время выполнения программы. Но как говорит BaltikS - вполне можно использовать сторонние утилиты. Только я вот терпеть не могу необходимость использования чего-то стороннего(если это не плагин для QtCreator). Т.е. QST - хорошая идея.

С другой стороны, расхвален он с большим авансом. Сейчас он написан настолько, насколько он нужен топикстартеру. Настолько универсально и настолько удобно. Он знает что это, знает как оно работает, как им пользоваться и наслаждается отсутствием лапши. Но я не буду использовать QST потому что:
1) синтаксис SQL я знаю лучше чем суть QST
2) QST не шибко универсален
3) самое главное: стиль работы с QST далек от стиля работы в Qt. Где вы видели в Qt имена подобные этим: fv_visible, fr_none, fo_like, fb_right, fp_where? Да это коротко и для автора очень удобно. Но не для меня, я не вижу логики: где эти штуки объявлены? почему где-то префикс fr, а где-то fo? зачем символ '_' в именах?

До современного стиля написания кода Qt рос довольно долго. enum'ы типа Qt::Ascending и им подобные в большинстве своем появились только в Qt4 (могу жутко ошибаться, статью читал давно, если что поправьте). Думаю, если QST не заглохнет, то может дорасти до библиотеки, бесшовно сливающейся с Qt. Время есть, а я пока с лапшой помучаюсь.
Записан
Страниц: [1] 2 3   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.085 секунд. Запросов: 23.