согласен полностью с тем, что обучение гита лучше начинать с консоли. Фишка консоли именно в том, что там ты должен использовать оригинальные команды гита как они есть, а не кнопки, которые не всегда делают очевидные вещи. Идея колеса с командами в данной статье как раз пришла именно оттуда — суть колеса в том, что оно отображает команды один в один без искажений.
Полностью согласен с автором. Я когда начал писать свою ORM-либу на плюсах мне то же самое говорили https://github.com/fnc12/sqlite_orm. А сейчас она на втором месте по кол-ву звезд среди ORM для SQLite3 на плюсах на гитхабе
я так понял ваш вопрос состоит в том, чтобы как-то вывести тип хранилища. Это очень популярный вопрос у пользователей sqlite_orm, и ответ на него я подробно описал в FAQ на github вот тут https://github.com/fnc12/sqlite_orm/wiki/FAQ
Посмотрев внимательнее ODB я нашел еще одну важную вещь, которая есть в sqlite_orm и отсутствует в ODB — это возможность именовать столбцы таблиц независимо от имен полей класса. Как бы это смешно не звучало, но если в схеме есть колонка "new", например, то в C++ такой код не скомпилируется. Самое забавное, что если сменить язык на тот, в котором слово "new" не зарезервировано, то все заработает. Я сталкивался с таким лично в разработке (правда, это был enum-маппер, либа https://github.com/aantron/better-enums). Уговорить бэкенд разраба чтобы он поменял имя колонки в таблице только потому что это слово зарезервировано языком, на котором написан один из клиентов, нереально, да и идиоматически это неверно, на моя взгляд.
@igorjan94 я понял, теперь я нахожусь в процессе осознания, что вложенные сущности нужны. Но вот такой вопрос от меня: в случае если есть такая схема (буду писать коротко): books ( id:int, name:string, category_id:int ) categories ( id:int, name:string )
то подразумевается, что получая книгу, в ней есть вложенная категория с указанным id. То есть, ORM за юзера выполняет каскадный SELECT и достает нужную категорию. Это я понял. Но вот вопрос: а что если ссылки взаимные, то есть у категории есть id книги, которую добавили самой последней в эту категорию, например. Так: categories ( id:int, name:string, last_added_book_id:int? )
Тут может быть такая ситуация, когда книга ссылается на категорию, которая ссылается на эту же книгу, которая ссылается на эту же категорию. В данном случае что делает ORM (например, та сама на js, которую, кажется, вы мне рекомендовали как пример)? Попадает в бесконечную рекурсию?
окей, тогда мне следует добавить поддержку этого в sqlite_orm. И если один ко многих ясно как сделать, то вектор устроен хитрее — тут надо умно и очевидно указывать какая таблица выполняет роль словаря (в нашем случае это books_categories)
Тут другой случай — тут не просто абы-какие поля, о которых хранилище не знает, тут поля, типы которых связаны с хранилищем, но не в том виде, в котором они оформлены внутри структур. Это порождает несоответствие схемы в бд схеме доменной модели в программе и делает код менее читабельным, и, как следствие, менее поддерживаемым. А если у меня просто будет лишнее поле, которое не нужно сериализоваться (хэш имени пользователя, например), то это не породит неясностей, так как ясно, что это приблуда
Добавить поля, которые не сериализуются, в структуру не составляет проблем. Это даже подразумевается либой. Просто не создавайте колонку при создании хранилища со ссылкой на это поле и все
Пожалуйста. Впервые с IOD я столкнулся вот тут http://siliconframework.org. Может этот фреймворк будет вам тоже интересен.
Насчет ODB — классная штука за одним исключением — предкомпилятором. С++ и без того получает много критики за наличие сишного препроцессора. А если я подключу либу с предпредкомпилятором, что тогда получится? А если у меня две? Три? Кто первичен? Кто вторичен? Почему именно так? Например, у меня проект на Qt (у него есть MOC), я подключил ODB (у него своя приблуда), и еще IOD добавил, у него свой скрипт, который генерит заголовок с символами. Это все порождает путаницу и выходит за пределы возможностей] языка. Именно поэтому я такую черную магию не одобряю. Это мое личное мнение. Не сомневаюсь, что те два ваших проекта, которые содержали ODB, отлично функционировали в области взаимодействия с БД.
Что? sqlite_orm как и любая другая sqlite ORM библиотека отправляет запросы в первую очередь в SQLite. И разработчик работая с ORM или еще каким-угодно фронтэндом БД должен понимать в какие запросы его клиентский код вырождается в результате. Так вот: когда классы соответствуют схеме — это делает код очень читабельным в плане "где и какой sql запрос отправляется". Иначе повышается шанс быдлокодинга, так как новичок может не понимать какие запросы под собой подразумевает вот эта простая и удобная в использовании функция. Именно поэтому последний пример
auto categories = storage.get_all<Category>(where(in(&Category::id,
storage.select(&BookCategory::categoryId, where(is_equal(&BookCategory::bookId, book.id)))
)));
идеально иллюстрирует вложенный запрос:
SELECT * FROM categories WHERE id IN (
SELECT category_id FROM books_categories WHERE book_id = ?
)
Второе: поддержки std::experimental::optional<T> в sqlite_orm нет пока он не выйдет из experimental namespace (ожидается в С++17). Опциональные значения используют std::shared_ptr<T> либо std::unique_ptr<T>. Это неудобно, согласен. Я думал добавить сырые С-указатели, но этого не будет — вместо этого я добавлю трэйты чтобы можно было подключать свои типы полей — собственные строки, собственные опционалы. Там уже разработчик если хочет — добавит С-указатели или Glib::ustring, QString etc.
А вообще у меня мысли такие: сделать возможность хранить вложенную сущность (пока только в одном экземпляре) в виде поля типа std::shared_ptr<T> где T это другой класс также связанный с хранилищем. Насчет векторов тут надо подумать. Ваш совет про make_many_to_many_association очень интересен
По правде сказать из всех ORM-библиотек, которые я видел, ODB меня впечатлила больше всего. У меня вопрос: кто именно занимается парсингом прагм? Тут, похоже, есть кастомный предкомпилятор как у Qt (MOC) или у IOD (https://github.com/matt-42/iod) парсер символов
Идея отличная, только структуры схеме не соответствуют. Это создает неочевидности. Я не говорю, что вы что-то говорите неверно, я пытаюсь понять как лучше добавить возможность вложенных сущностей.
Вообще при схеме (добавил NOT NULL так как зануляемые значения мапятся в указатели только)
CREATE TABLE books(id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL);
CREATE TABLE categories(id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL)
CREATE TABLE books_categories(category_id INTEGER NOT NULL, book_id INTEGER NOT NULL)
Достать все категории конкретной книги можно так:
struct Book {
int id;
std::string name;
};
struct Category {
int id;
std::string name;
};
struct BookCategory {
int categoryId;
int bookId;
};
using namespace sqlite_orm;
auto storage = make_storage("db.sqlite",
make_table("books",
make_column("id", &Book::id, primary_key()),
make_column("name", &Book::name)),
make_table("categories",
make_column("id", &Category::id, primary_key()),
make_column("name", &Category::name)),
make_table("books_categories",
make_column("category_id", &BookCategory::categoryId),
make_column("book_id", &BookCategory::bookId)));
auto book = storage.get<Book>(2);
auto categories = storage.get_all<Category>(where(in(&Category::id,
storage.select(&BookCategory::categoryId, where(is_equal(&BookCategory::bookId, book.id)))
)));
ахаха, лучший коммент)
согласен полностью с тем, что обучение гита лучше начинать с консоли. Фишка консоли именно в том, что там ты должен использовать оригинальные команды гита как они есть, а не кнопки, которые не всегда делают очевидные вещи. Идея колеса с командами в данной статье как раз пришла именно оттуда — суть колеса в том, что оно отображает команды один в один без искажений.
Полностью согласен с автором. Я когда начал писать свою ORM-либу на плюсах мне то же самое говорили https://github.com/fnc12/sqlite_orm. А сейчас она на втором месте по кол-ву звезд среди ORM для SQLite3 на плюсах на гитхабе
уже есть все джоины
я так понял ваш вопрос состоит в том, чтобы как-то вывести тип хранилища. Это очень популярный вопрос у пользователей
sqlite_orm
, и ответ на него я подробно описал в FAQ на github вот тут https://github.com/fnc12/sqlite_orm/wiki/FAQПрекраснейшая новость. Вот только лишь бы не реализовали новую версию винампа на электроне
добавлять методы к классу можно если методы шаблонные путем специализаций. Это не идеал, но очень похоже
#include <codecvt>
— там все естьпроверьте в нынешней версии — теперь есть и GROUP BY, и JOIN (все виды кроме NATURAL и SELF)
Посмотрев внимательнее
ODB
я нашел еще одну важную вещь, которая есть вsqlite_orm
и отсутствует вODB
— это возможность именовать столбцы таблиц независимо от имен полей класса. Как бы это смешно не звучало, но если в схеме есть колонка "new", например, то в C++ такой код не скомпилируется. Самое забавное, что если сменить язык на тот, в котором слово "new" не зарезервировано, то все заработает. Я сталкивался с таким лично в разработке (правда, это был enum-маппер, либа https://github.com/aantron/better-enums). Уговорить бэкенд разраба чтобы он поменял имя колонки в таблице только потому что это слово зарезервировано языком, на котором написан один из клиентов, нереально, да и идиоматически это неверно, на моя взгляд.@igorjan94 я понял, теперь я нахожусь в процессе осознания, что вложенные сущности нужны. Но вот такой вопрос от меня: в случае если есть такая схема (буду писать коротко):
books ( id:int, name:string, category_id:int )
categories ( id:int, name:string )
то подразумевается, что получая книгу, в ней есть вложенная категория с указанным id. То есть, ORM за юзера выполняет каскадный
SELECT
и достает нужную категорию. Это я понял. Но вот вопрос: а что если ссылки взаимные, то есть у категории есть id книги, которую добавили самой последней в эту категорию, например. Так:categories ( id:int, name:string, last_added_book_id:int? )
Тут может быть такая ситуация, когда книга ссылается на категорию, которая ссылается на эту же книгу, которая ссылается на эту же категорию. В данном случае что делает ORM (например, та сама на js, которую, кажется, вы мне рекомендовали как пример)? Попадает в бесконечную рекурсию?
окей, тогда мне следует добавить поддержку этого в
sqlite_orm
. И если один ко многих ясно как сделать, то вектор устроен хитрее — тут надо умно и очевидно указывать какая таблица выполняет роль словаря (в нашем случае этоbooks_categories
)да
Тут другой случай — тут не просто абы-какие поля, о которых хранилище не знает, тут поля, типы которых связаны с хранилищем, но не в том виде, в котором они оформлены внутри структур. Это порождает несоответствие схемы в бд схеме доменной модели в программе и делает код менее читабельным, и, как следствие, менее поддерживаемым. А если у меня просто будет лишнее поле, которое не нужно сериализоваться (хэш имени пользователя, например), то это не породит неясностей, так как ясно, что это приблуда
Добавить поля, которые не сериализуются, в структуру не составляет проблем. Это даже подразумевается либой. Просто не создавайте колонку при создании хранилища со ссылкой на это поле и все
Пожалуйста. Впервые с IOD я столкнулся вот тут http://siliconframework.org. Может этот фреймворк будет вам тоже интересен.
Насчет ODB — классная штука за одним исключением — предкомпилятором. С++ и без того получает много критики за наличие сишного препроцессора. А если я подключу либу с предпредкомпилятором, что тогда получится? А если у меня две? Три? Кто первичен? Кто вторичен? Почему именно так? Например, у меня проект на Qt (у него есть MOC), я подключил ODB (у него своя приблуда), и еще IOD добавил, у него свой скрипт, который генерит заголовок с символами. Это все порождает путаницу и выходит за пределы возможностей] языка. Именно поэтому я такую черную магию не одобряю. Это мое личное мнение. Не сомневаюсь, что те два ваших проекта, которые содержали ODB, отлично функционировали в области взаимодействия с БД.
Что?
sqlite_orm
как и любая другая sqlite ORM библиотека отправляет запросы в первую очередь в SQLite. И разработчик работая с ORM или еще каким-угодно фронтэндом БД должен понимать в какие запросы его клиентский код вырождается в результате. Так вот: когда классы соответствуют схеме — это делает код очень читабельным в плане "где и какой sql запрос отправляется". Иначе повышается шанс быдлокодинга, так как новичок может не понимать какие запросы под собой подразумевает вот эта простая и удобная в использовании функция. Именно поэтому последний примеридеально иллюстрирует вложенный запрос:
Второе: поддержки
std::experimental::optional<T>
вsqlite_orm
нет пока он не выйдет изexperimental
namespace (ожидается в С++17). Опциональные значения используютstd::shared_ptr<T>
либоstd::unique_ptr<T>
. Это неудобно, согласен. Я думал добавить сырые С-указатели, но этого не будет — вместо этого я добавлю трэйты чтобы можно было подключать свои типы полей — собственные строки, собственные опционалы. Там уже разработчик если хочет — добавит С-указатели илиGlib::ustring
,QString
etc.А вообще у меня мысли такие: сделать возможность хранить вложенную сущность (пока только в одном экземпляре) в виде поля типа
std::shared_ptr<T>
где T это другой класс также связанный с хранилищем. Насчет векторов тут надо подумать. Ваш совет проmake_many_to_many_association
очень интересенПо правде сказать из всех ORM-библиотек, которые я видел, ODB меня впечатлила больше всего. У меня вопрос: кто именно занимается парсингом прагм? Тут, похоже, есть кастомный предкомпилятор как у Qt (MOC) или у IOD (https://github.com/matt-42/iod) парсер символов
Идея отличная, только структуры схеме не соответствуют. Это создает неочевидности. Я не говорю, что вы что-то говорите неверно, я пытаюсь понять как лучше добавить возможность вложенных сущностей.
Вообще при схеме (добавил NOT NULL так как зануляемые значения мапятся в указатели только)
Достать все категории конкретной книги можно так:
Вот так это делается
Точнее "таблично-ориентированность" называется отсутствием вложенных сущностей.
По правде сказать вы уже не первый кто говорит про это, и я задумался как можно вложенность реализовать в
sqlite_orm