Russian Qt Forum

Qt => Базы данных => Тема начата: DS_tm от Март 21, 2010, 12:51



Название: Запись производного класса в БД
Отправлено: DS_tm от Март 21, 2010, 12:51
Есть класс:

Код
C++ (Qt)
SomeClass {
 SomeClass(int p1, const QString &p2) : _param1(p1), _param2(p2) {}
 inline int param1() const { return _param1; }
 inline QString param2() const { return _param2; }
private:
 int _param1;
 QString _param2;
};
Q_DECLARE_METATYPE(SomeClass)
 

Хотелось бы обеспечить корректную работу следующего кода:
Код
C++ (Qt)
 QSqlQuery query;
 query.prepare("INSERT INTO table VALUES(?)");
 query.addBindValue(QVariant::fromValue(SomeClass(1, "2"));
 query.exec();
 

В таком виде, без добавлений, понятное дело ничего не работает (как я понимаю из-за отсутсвия авто конвертации в QVariant::String)

Какие есть решения данной проблемы?


Название: Re: Запись производного класса в БД
Отправлено: lit-uriy от Март 21, 2010, 14:04
Ты хочешь сделать т.н. "сериализацию"?
Запрос по идее должен выглядеть так:
"INSERT INTO table (field_name) VALUES (value)"
Т.е. имя поля ты пропустил. И какой тип данных у этого поля?



Название: Re: Запись производного класса в БД
Отправлено: DS_tm от Март 21, 2010, 14:13
Тип поля либо String, либо BLOB (для тестов использую sqlite, а там типизация не строгая).
В примере название поля не пропущено, просто таблица с одним полем. Да и даже если таблица с несколькими полями, суть дела это не меняет. Посмотрел как Qt генерит строку на запрос с помощью биндов. Все это упираеться в функцию QString QSqlDriver::formatValue(const QVariant &value). Для значений типа QDate и QTime данная функция формирует строку, для всех остальных вызывает банальный toString(). Но для пользовательского класса toString() вернет QString(), вот в чем проблема.


Название: Re: Запись производного класса в БД
Отправлено: lit-uriy от Март 21, 2010, 14:55
QVariant не имеет метода toColor, однако это не означает, что преобразование невозможно. Посмотри внимательно документацию по QVariant.
И в том числе:
T QVariant::value () const
И ещё QMetaType


Название: Re: Запись производного класса в БД
Отправлено: DS_tm от Март 21, 2010, 15:11
Проблема не в том, что я не могу превести переменную QVariant к своему собственному типу, а в том, что нет способа привести переменную QVariant моего типа к строке! То есть, вызов функции canConvert<QVariant::String>() для переменной созданой с помощью
QVariant::fromValue(SomeClass(...)) будет всегда возвращать false. Просмотрев пару раз всю документацию по QVariant и реализацию функции QVariant::toString() я пришел к выводу, что без правки QVariant это конвертация невозможна. Может кто-нибудь меня поправит? Мне искренне непонятно, почему нельзя было добавить возможность создания функций конвертации пользовательских типов в\из стандартных типов.

Что косаеться метатайп, то это фундамент для QVariant, он ничего не знает о возможности конвертации одного типа в другой.


Название: Re: Запись производного класса в БД
Отправлено: lit-uriy от Март 21, 2010, 15:42
мысль в слух:
А если для твоего класса реализовать функцию toString() и её использовать в твоём коде, т.е. сразу передавать в bind строку?


Название: Re: Запись производного класса в БД
Отправлено: DS_tm от Март 21, 2010, 15:59
Вы имеете ввиду operator QString() const?


Название: Re: Запись производного класса в БД
Отправлено: break от Март 21, 2010, 16:07
Да зачем - просто QString toString() - и вызвать его при бинде строкового поля, а QVariant сам преобразует QString в QVariant

query.addBindValue( SomeClass(1, "2").toString() );


Название: Re: Запись производного класса в БД
Отправлено: lit-uriy от Март 21, 2010, 16:10
нет, можно просто в твоём классе сделать функцию-член toString(), которая вернёт QString. Ну и fromString(), для обратного действия. При этом интерфейс класса выглядеть будет не красиво, но всё же выход.


Название: Re: Запись производного класса в БД
Отправлено: DS_tm от Март 21, 2010, 16:45
Да в данном классе можно написать функции fromString и toString, и бинд нормально будет работать с ними, но мне необходима однообразная работа с данными. К примеру:

Код
C++ (Qt)
SomeDbManager::addRecord(QVariantList list) {
 QSqlQuery query;
 query.prepare(QString("INSERT INTO table VALUES(%1)").arg(prepareValues(lilst.size());
 foreach (QVariant value, list) query.addBindValue(value);
 return query.exec();
}
 

Допустим можно проверять перед addBindValue тип:
Код
C++ (Qt)
 if (value.type() == someClassType) addBindValue(value.value<SomeClass>().toString());
 else addBindValue(value);
 

Но при чтении получиться:
Код
C++ (Qt)
SomeDbManager::record(int recId) {
 QSqlQuery query;
 QVariantList list;
 ...
 for (int i = 0; i < rowSize; ++i) {
   list << query.value(i);
 }
 return list;
}
 

И в итоге получаеться, записываемый QVariantList имеет некоторые поля типа SomeClass, а прочитаный только QString
И даже это бы меня устроило, но вот при проверке i-ого элемента (который содержал раньше SomeClass) получившившегося списка получиться:

Код
C++ (Qt)
list.at(i).canConvert<SomeClass>(); // false;
 


Название: Re: Запись производного класса в БД
Отправлено: break от Март 22, 2010, 00:32
Раз хочется такой автоматизации - используйте QDataStream - дщелаете для своего класса соответствующий не член метод для

QDataStream &   operator>> ( QDataStream & stream, CMyClass & class )
QDataStream &   operator<< ( QDataStream & stream, const CMyClass & class )

и для всех других классов которые будут писаться в БД должны они быть переопределены. Для большинства Qt классов это сделано.

Далее используете QBuffer для получения QByteArray ( метод QBuffer::QByteArray &   buffer () ) и уже это пишете в BLOB БД.

Но возможно не очень верно создавать QBuffer и QDataStream для каждого класса... По идее должно работать.


Название: Re: Запись производного класса в БД
Отправлено: DS_tm от Март 22, 2010, 22:00
Да, этот вариант будет работать на запись, а вот при чтении необходимо будет точно знать последовательность типов, чтобы вызвать оператор чтения из потока для соответсвующего объекта, и только потом переводить его в QVariant и помещать в список.
Вообщем я пока знаю одно решение, которое изначально использовал, хранить типы колонок таких таблиц в самой бд, и при чтении записи делать обработку для каждого значения, в зависимости от типа. Но оно мне не нравиться лишним запросом к бд, на каждое чтение записи (альтернативно стуктуру можно кешировать програмно, но тогда небольшие расходы памяти появлаються)
В итоге видно с Qt4 вариантов все равно нет. Будем надеяться, что в пятерке расширят возможности QVariant.


Название: Re: Запись производного класса в БД
Отправлено: break от Март 23, 2010, 17:03
Цитировать
В итоге видно с Qt4 вариантов все равно нет. Будем надеяться, что в пятерке расширят возможности QVariant.
варианты есть - надо копать если они не лежат на поверхности

я честно говоря не полностью понял задачу...

есть таблица - в ней поле BLOB где должны лежать классы?
можно сделать отдельный столбец с типом этого класса - и уже спокойно вытаскивать через QDataStream или другим способом - никаких лишних запросов - только 1 доп столбец

если я правильно понял задачу


Название: Re: Запись производного класса в БД
Отправлено: DS_tm от Март 24, 2010, 10:31
Ну со столбцом Вы конечно погорячились, наверно все таки имелась ввиду строка :). По сути получаеться обычная таблица, а эта страка есть заголовок. Спасибо, идея интересная, мне что-то в голову не приходило, хроню типы в отдельной таблице.

Вообще абстрактно задача была такой. Необходимо создать инструмент, который позволял бы пользователю (не программисту, а обычному юзеру) создавать наборы данных любых доступных типов. То есть пользователь сначала создает набор параметров, выбирает им тип (из доступных типов а-ля "целове чилос", "список", "бит" и т.д.). Потом, имея этот список, он создает некую сущность (в бд как раз такую таблицу BLOB столбцов) и выбирает из списка, какие параметры у этой сущности будут. Ну а дальше он может создовать, редактировать и удалять записи для этой сущности. Сообственно данные для конечной записи хроняться в QVariantList, а для чтения и редактирования используеться QtPropertyBrowser. Вообщем, такой небольшой редактор БД, для обынчх пользователей, не владеющих Sql.

В итоге еще в том году сделал каркас, потом прикрутил QtPropertyBrowser, все работает и давольно славно, но вот решил произвести небольшой рефакторинг, плюс появилась необходимость хранить сложные классы, так что начал поиск идей по улучшению отдельных компонентов.


Название: Re: Запись производного класса в БД
Отправлено: break от Март 24, 2010, 18:17
Цитировать
Ну со столбцом Вы конечно погорячились, наверно все таки имелась ввиду строка . По сути получаеться обычная таблица, а эта страка есть заголовок.
ничего не понял

я имел ввиду

Table CLASS_STORAGE

ROW_ID | CLASS_TYPE | CLASS_DATA



Название: Re: Запись производного класса в БД
Отправлено: DS_tm от Март 24, 2010, 19:31
Не, Вы не правильно поняли)

Таблица имеет вид:
Table CLASS_STORAGE
ROW_ID | CLASS1_DATA | CLASS2_DATA | CLASS3_DATA | .... | CLASSN_DATA

Так что можно создать строку след вида:
0 | CLASS1_TYPE | CLASS2_TYPE | CLASS3_TYPE | .... | CLASSN_TYPE

Ну или по имени столбца можно определять тип, но это уже совсем загон.


Название: Re: Запись производного класса в БД
Отправлено: break от Март 25, 2010, 13:52
Цитировать
Ты хранишь разные классы в разных столбцах???
То есть сколько потьенциально возможно классов - столько должно быть таких столбцов ?
Это не загон?


Название: Re: Запись производного класса в БД
Отправлено: DS_tm от Март 25, 2010, 15:55
То есть сколько потьенциально возможно классов - столько должно быть таких столбцов ?

Это несовсем верно, в разных столбцах могут быть данные принадлижащие одному и тому же классу (типу)
Например:
-----------------------------------------------------------------------
 id        | name | soname | patronymic | age | pasport_data                 |
-----------------------------------------------------------------------
 1         | Jhon  | Smith   |                 | 31   | 2389; 234111; 10/11/13  |
 2         | Tom  | Kent     |                 | 28   | 2222; 333211; 10/03/02  |
 3         | Иван | Иванов | Иванович   | 25   | 7777; 333211; 13/08/00  |
-----------------------------------------------------------------------

В этой таблице name, soname и patronymic это QString, age это int, а pasport_data пользовательский класс имеющий функции сериализации с QString.