Пост

Пару слов об ODB ORM

Некоторое время назад встретил на своем пути замечательную ORM-библиотеку ODB. Приглянулась она мне довольно подробными примерами и документацией. А так же мое почтение разработчикам за максимально наглядное изображение, демонстрирующее механизм работы с библиотекой.

Далее я решил применить ее в реальном проекте и столкнулся с парочкой проблем о которых напишу тут.

И так, при использовании библиотеки в чистом С++ проекте проблем возникнуть не должно. В моем случае развлечения начались в процессе прикручивания к проекту с использованием Qt библиотек, в частности при генерации кода для структуры с полем QDateTime для хранения таймстемпов (Это все я делал на Ubuntu). Структура, для которой нужно было сгенерировать код выглядела примерно так.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <string>
#include <odb/core.hxx>
#include <QtCore/QDateTime>

#pragma db object
class player
{
public:
    player (const QDateTime datetime,
    	    ...

    QDateTime datetime () const { return timestamp_; }
    ...

private:
    friend class odb::access;
    player () {}

#pragma db id auto
    unsigned long id_;
    QDateTime timestamp_;
    ...
};

Окей. В документации указано, что для генерации кода при использовании Qt достаточно использовать опцию –profile qt/date-time. Пытаемся сгенерировать:

1
$ odb --database pgsql --generate-query --generate-schema --profile qt/date-time ../player.hxx 

И ловим ошибку.

1
2
3
4
5
In file included from <odb-prologue-2>:1:
/usr/include/odb/qt/date-time/pgsql/default-mapping.hxx:8:10: fatal error: QtCore/QDate: Нет такого файла или каталога
    8 | #include <QtCore/QDate>
      |          ^~~~~~~~~~~~~~
compilation terminated.

Что не так? Ладно.. Ковыряемся в гугле и пишут, что надо явно указать путь к заголовочным файлам Qt. Указываем.

1
$ odb -I/usr/include/x86_64-linux-gnu/qt5/ --database pgsql --generate-query --generate-schema --profile qt/date-time ../player.hxx

И ловим огроооомный вывод с ошибками. Он реально огромный и среди большой кучи текста видим, что проскакивают следующие сообщения.

1
/usr/include/c++/9/bits/c++0x_warning.h:32:2: error: #error This file requires compiler and library support for the ISO C++ 2011 standard. This support must be enabled with the -std=c++11 or -std=gnu++11 compiler options.

В usage сообщении odb, помнится мне, я видел, что можно указать стандарт С++. И действительно:

1
2
$ odb --help | grep std
--std <version>               Specify the C++ standard that should be used

Теперь запускаем с такими опциями.

1
$ odb -I/usr/include/x86_64-linux-gnu/qt5/ --std c++14 --database pgsql --generate-query --generate-schema --profile qt/date-time ../player.hxx 

Ии… Опять ошибка!

1
2
3
/usr/include/x86_64-linux-gnu/qt5/QtCore/qglobal.h:1187:4: error: #error "You must build your code with position independent code if Qt was built with -reduce-relocations. " "Compile your code with -fPIC (-fPIE is not enough)."
 1187 | #  error "You must build your code with position independent code if Qt was built with -reduce-relocations. "\

Вот на этом моменте я прифигел и потратил несколько часов на гугление, курение документации и танцы с бубном. По итогу выяснилось, что нужно было с помощью опции -x передать параметр -fPIC. Итоговая команда выгоядела так:

1
$ odb -x -fPIC -I/usr/include/x86_64-linux-gnu/qt5/ --std c++14 --database pgsql --generate-query --generate-schema --profile qt/date-time ../player.hxx 

И после этого нужные файлы сгенерировались.

1
2
$ ls 
player-odb.cxx  player-odb.hxx  player-odb.ixx  player.sql

SQL-файл используем для создания таблицы в базе.

1
2
3
$ psql --username=ainr --host=localhost --dbname=huidb < player.sql
DROP TABLE
CREATE TABLE

Остальные файлы включаем в Qt/C++ проект. Далее пишем код для вставки данных в таблицу:

1
2
3
4
5
6
7
8
9
try {
    odb::transaction transaction(db->begin()); // Создаем транзакцию
    player p(ip, datetime, platform);          // Создаем объект нашего класса
    db->persist(p);                            // Подкидываем объект в базу
    transaction.commit();                      // Фиксируем изменения
}
catch (const odb::exception& e) {
    qDebug() << e.what();
}

Компилируем, запускаем и видим, что данные были адекватно добавлены в базу данных.

1
2
3
4
5
6
huidb=> select * from player;
 id |        timestamp        |        ip        | platform 
----+-------------------------+------------------+----------
  1 | 2020-06-25 22:04:40.589 | ::ffff:127.0.0.1 | linux
  2 | 2020-06-25 22:04:44.053 | ::ffff:127.0.0.1 | linux
(2 rows)

Код для получения данных из базы выглядит примерно так:

1
2
3
4
5
6
7
8
9
10
11
12
13
try {
    odb::transaction t (db->begin());
    odb::result<player> r(db->query<player>(
        odb::query<player>::id > 5 /* здесь может быть условие */
    ));
    for (auto i = r.begin(); i != r.end(); i++) {
        qDebug() << (*i).ip().c_str() << (*i).datetime() << (*i).platform().c_str();
    }
    t.commit();
}
catch (const odb::exception& e) {
    qDebug() << e.what();
}

Вместо вывода

В будущем здесь может появиться описание и решение другой проблемы.

Авторский пост защищен лицензией CC BY 4.0 .