Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Реализация на C++ объектного представления таблиц в БД  (Прочитано 22521 раз)
0 Пользователей и 3 Гостей смотрят эту тему.
RuNTiME
Помогающий

ru
Offline Offline
Пол: Мужской

« : 28-01-2012 15:57 » 

Предлагаю обсудить возможные пути реализации объектного представления таблиц в базе данных. Суть в том, чтобы создать объект с набором свойств, после задания/изменения которых, можно было сохранить его в БД. Количество свойств объекта может быть меньшим либо равным количеству полей в соответствующей таблице БД. Каждый объект и таблица БД в обязательном порядке имеют поле ID, которое будет использовано для изменения данных и для связей между объектами.

Поясню на псевдокоде чего хотелось бы добиться:
Код: (C++)
// базовая модель
class Model {
protected:
    int64 _id;
public:
    bool Save();
};

class Book: public Model {
public:
    Property name; // название книги
    Property autor; // автор книги
    Property year; // год издания
};

// создаем новый объект книга
Book book;
book.name = "The C++ Programming Language";
book.autor = "Bjarne Stroustrup";
book.year  = 2000;

// сохраняем объект в БД
book.Save();

Итак класс Property должен представлять собой некоторую реализацию Variant. Метод Save() класса Book должен взять значения из свойств и сформировать SQL запрос для вставки/изменения записи в БД.
Что касаясь загрузки объекта из БД, базовая модель должна иметь возможность загружать объекты по их ID. Так же очень желательно с возможностью указать набор полей, которые нужно загрузить.
Записан

Любимая игрушка - debugger ...
Finch
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #1 : 28-01-2012 17:11 » 

1) id записи не обязательное, хоть и желательное поле Улыбаюсь
2) В базах данных есть понятие "обязательные поля" При вставке новых записей в таблицу, без упоминания этих полей, ведет к ошибке.
3) Это все задуманно в качестве изобретения велосипеда?
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
RuNTiME
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #2 : 28-01-2012 17:22 » 

Finch,
1) id обязательное поле только в данном случае, оно необходимо для правильного функционирования объектов, т.к. должно быть заранее определенное поле для любого объекта, по которому можно однозначно его идентифицировать (первичный ключ).
2) можно реализовать проверку в объекте на заполнение обязательных полей и например генерировать исключение в случае их незаполнения.
3) велосипед - да согласен, например для языка Python (https://docs.djangoproject.com/en/dev/topics/db/models/) , но реализацию чего - то подобного под C++ я не видел, впрочем может плохо искал...

И прекрасно понимаю, что в следствии "статичности" C++ провернуть такое довольно трудно. Собственно по этому и хочу обсудить возможности реализации на C++ подобного подхода построения модели данных и её плюсы и минусы.
Записан

Любимая игрушка - debugger ...
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #3 : 28-01-2012 17:37 » 

Эта идея имеет какие-то принципиальные отличия от обычного ORM?
Записан

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

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
RuNTiME
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #4 : 28-01-2012 17:41 » 

Dale, ORM - именно то что мне нужно Улыбаюсь
Записан

Любимая игрушка - debugger ...
Finch
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #5 : 28-01-2012 17:42 » 

Ну Qt как-то решали данную проблему Улыбаюсь В библиотеке есть набор классов для работы с базами данных. Можно начать с изучения "как они это сделали"
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #6 : 28-01-2012 17:59 » 

Dale, ORM - именно то что мне нужно Улыбаюсь

Тогда следующий вопрос: какие существенные преимущества предложенная идея имеет перед многочисленными имеющимися реализациями ORM?

Одно только предложение использовать единственный тип Property для всех полей, на мой взгляд, способно порушить всю целостность данных. Ведь у каждого поля должен быть собственный реляционный домен. А в данном случае никто не мешает с таким же успехом написать:

Код: (C++)
book.autor = 2000;
book.year  = "Bjarne Stroustrup";

Если, конечно, сам тип Property не будет содержать в себе информацию о домене и проверять совместимость типов на этапе выполнения. Но это такая громоздкая конструкция в результате получится...
Записан

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

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
RuNTiME
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #7 : 28-01-2012 18:11 » 

Finch, В Qt используется собственный препроцессор расширяющий возможности языка C++.
Dale,
Цитата
Тогда следующий вопрос: какие существенные преимущества предложенная идея имеет перед многочисленными имеющимися реализациями ORM?
Думаю в том что нужна реализация именно на C++, а таковой в готовом виде я не видел Улыбаюсь

Цитата
Одно только предложение использовать единственный тип Property для всех полей, на мой взгляд, способно порушить всю целостность данных.
Верно подмечено. Сам думал над этим. Можно отказаться от Variant и использовать встроенную проверку типов C++.

Код: (C++)
class Book: public Model {
public:
    StringProperty name; // название книги
    StringProperty autor; // автор книги
    IntProperty year; // год издания
};
Записан

Любимая игрушка - debugger ...
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #8 : 28-01-2012 18:21 » 

Думаю в том что нужна реализация именно на C++, а таковой в готовом виде я не видел Улыбаюсь

Сам лично на этот счет ничего посоветовать не могу, ибо давным-давно завязал с попытками работать с данными на C++, это далеко не лучший язык для такого применения.

Википедия предлагает несколько вариантов: http://en.wikipedia.org/wiki/List_of_object-relational_mapping_software#C.2B.2B . Думаю, при должном усердии найдется на порядок больше готовых решений, поскольку тема ORM не вчера возникла, задача довольно типовая.
Записан

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

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Finch
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #9 : 28-01-2012 18:59 » 

RuNTiME, В Qt в основном препроцессор обрабатывает понятие сигналов и слотов. Ну и формочки из ui формирует. Большего я не видел в .moc файлах. Ну если хочеш поизобретать велосипед. Чтож начнем Улыбаюсь
В любой базе данных ограниченное количество базовых типов. Можно на пальцах пересчитать. Делаем базовый тип, от него наследуем описание всех остальных. Пример
BaseType -> IntType
BaseType -> StringType
BaseType -> DateTimeType

Теперь класс Variant будет хранилешем BaseType и фабрикой классов.

Класс Record будет соответсвовать записи в базе данных. Хранить данные будет в std::map<string, Variant>  Работа осушествляется примерно так:
Код: (C++)
Record rec;
// Тут как-то загоняем запись
rec["mail"] = "My mail";
Если все таки мы сделаем
Код: (C++)
rec["mail"] = 2000;
То должен выкинутся Exception. Так как StringType знает, что он получает данные только с FromString.
« Последнее редактирование: 28-01-2012 19:06 от Finch » Записан

Не будите спашяго дракона.
             Джаффар (Коша)
RuNTiME
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 28-01-2012 19:42 » 

Finch, еще думаю BaseType должен содержать свойство is_modified, чтобы знать какие поля были изменены и при обновлении данных в запрос включать только измененные.

P.S. Посмотрел на готовую реализацию ORM на C++ - LiteSQL, в принципе понравилось, но довольно таки громоздко Улыбаюсь Вот с таких мыслей и начинается изобретение велосипедов... Пожалуй попробую чего - нибудь написать.... Улыбаюсь
Записан

Любимая игрушка - debugger ...
Finch
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #11 : 28-01-2012 20:26 » 

Это уже тонкости реализации Улыбаюсь
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #12 : 29-01-2012 07:33 » new

Если я правильно понял, в схеме, описанной автором, Property по сути играет роль атрибута, помечающего дублируемые в БД поля.

Предлагаю отделить мух от котлет, и по аналогии с MVC сделать Model-DataController пары. Классы с данными (Model) оставить очищенными от всяких Save и т.п. Однако в каждом таком классе нужно установить friend-доступ для другого (DataController) класса и описать события обновления данных или что-нибудь в этом духе, если требуется автоматическое срабатывание DataController на событие.

Внутри DataController, во-первых, завести коллекцию с обрабатываемыми в БД полями. Каждое поле может быть описано отдельным объектом, хранящим информацию об отображении подмножества связанных полей модели в подмножество связанных полей БД, правила преобразования данных (если нужно), соответствия типов. Во-вторых, реализовать select, insert, update и delete запросы по коллекции полей с известным ключом - это несложная задача. В-третьих, реализовать фабрику Model-объектов, когда при создании нового объекта (в памяти), на его события автоматически подписывается DataController, и происходит загрузка данных из БД в поля, после чего объект поступает в работу.

Один объект DataController может обслуживать много объектов Model одного класса. В параллельных потоках нужна весьма аккуратная реализация DataController - то, что называется thread safe. При использовании событий тоже нужно аккуратно отработать сохранения и загрузки данных, чтобы каскадные операции не замыкались в цикл, и чтобы любой чих не приводил к полной загрузке/выгрузке БД в/из память.

Для множества классов моделей будет множество соответствующих классов DataController - как правило 1 к 1. Но в DataController при помощи templates можно написать достаточно богатый класс-предок, чтобы его потомки, настраиваемые на конкретные классы Model, были простыми и чисто конфигурационными. Формирование кода настроечных классов тоже можно упростить при помощи макросов, сведя ручное написание к минимуму.

Ещё одна интересная вещь - это классы-ссылки. В их объектах можно хранить указатель на целевой объект, а также ID целевого объекта. Если указатель NULL, при обращении автоматически загружать объект из БД. Будет ленивая загрузка объектов в память - по мере надобности. По аналогии со сборщиком мусора можно реализовать стратегию очистки памяти за счёт сброса старых, давно неиспользуемых объектов в БД. Выйдет система, где операции с БД происходят в "фоновом" режиме без вмешательства программиста.

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

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
RuNTiME
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #13 : 29-01-2012 14:59 » 

Dimka,
1) Как я понял DataController представляет собой коллекцию объектов Model и каждый объект Model содержит указатель на коллекцию, но тогда мне не понятно:
Цитата
Внутри DataController, во-первых, завести коллекцию с обрабатываемыми в БД полями
Какие поля в коллекции, если поля находятся в классах Model?

2)
Цитата
Каждое поле может быть описано отдельным объектом, хранящим информацию об отображении подмножества связанных полей модели в подмножество связанных полей БД
Либо я что - то не понял, либо тут говорится об объектах Model хранящих наборы полей, отображаемых на поля таблиц в БД? И по сути получается, что каждый класс Model отображается на свою таблицу в БД.

3)
Цитата
Во-вторых, реализовать select, insert, update и delete запросы по коллекции полей с известным ключом - это несложная задача.
Хорошая мысль перенести Save непосредственно в коллекцию, но как быть с крупномасштабными изменениями коллекции, например нужно изменить 10000 записей? Хранить все изменения в памяти пока в конце цикла изменений не будет вызван метод Save для коллекции? Есть конечно вариант установить ограничение используемой памяти и при превышении лимита "сбрасывать" объекты в БД, но все же....

4)
Цитата
В-третьих, реализовать фабрику Model-объектов, когда при создании нового объекта (в памяти), на его события автоматически подписывается DataController, и происходит загрузка данных из БД в поля, после чего объект поступает в работу.
Как быть в случае, если нужно загрузить всего 2 из 10 полей объекта, а в другом все 10?

5)
Цитата
Для множества классов моделей будет множество соответствующих классов DataController
Если каждый объект Model соответствует своей таблице в БД, а DataController представляет собой коллекцию объектов Model, то получается объект DataController соответствует одной базе данных, а несколько объектов DataController будут уже представлять набор баз данных... по - моему это уже перебор Улыбаюсь

Цитата
Всё же лучше использовать готовые библиотеки, подобрав под свои нужды наиболее подходящую.
Это лучше, только в том случае, когда поджимают сроки, начальство и т.д. и т.п. Но когда время есть и пишется это для себя, лучше разработать собственное решение и пусть оно будет далеко от совершенства и возможно даже просто примитивно, но позволит значительно повысить свой уровень знаний. Ведь использовать API готовой библиотеки может любой Улыбаюсь
Записан

Любимая игрушка - debugger ...
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #14 : 29-01-2012 17:49 » 

Цитата: RuNTiME
Как я понял DataController представляет собой коллекцию объектов Model
В корне не так. DataController представляет собой наблюдателя и посетителя, который берёт какой-нибудь объект Model и совершает над ним действия по обмену данными между Model и БД. Model про БД ничего не знает. Максимум, что есть у Model - это поле ID для нужд БД, и средства оповещения наблюдателей об изменениях данных.

Цитата: RuNTiME
Какие поля в коллекции, если поля находятся в классах Model?
Правильно, поля с данными находятся в Model. В БД могут быть немного (или много) другие поля. DataController знает, какие поля Model каким полям в БД соответствуют, и как соответствуют, какие нужно делать преобразования. Чтобы эту информацию хранить, в DataController есть коллекции полей и правил преобразования.

Вот в Model что-то поменялось, Model сигнализирует об изменениях своим наблюдателям. По полученному сигналу DataController заходит посетителем в Model, берёт из своей коллекции списки интересующих его полей, смотрит - пусть, поле X его интересует. Тогда он читает из Model значение поля X и что-то с ним делает по своим правилам, куда-то что-то пишет.

Цитата: RuNTiME
Хорошая мысль перенести Save непосредственно в коллекцию, но как быть с крупномасштабными изменениями коллекции, например нужно изменить 10000 записей?
Мысль может и хорошая, но не моя Улыбаюсь

Цитата: RuNTiME
Как быть в случае, если нужно загрузить всего 2 из 10 полей объекта, а в другом все 10?
Вот для этого и нужны DataController.

Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
RuNTiME
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #15 : 29-01-2012 19:11 » 

Цитата
DataController знает, какие поля Model каким полям в БД соответствуют, и как соответствуют, какие нужно делать преобразования. Чтобы эту информацию хранить, в DataController есть коллекции полей и правил преобразования.
Но в таком случае DataController выступает в роли одной глобальной модели, в которой сосредоточена вся логика по работе с БД, а классы Model уже фактически не являются моделями. Классы Model выступают в виде простых структур для хранения данных. Не лучше ли будет перенести логику преобразования и проверки данных в классы Model, а коллекция DataController уже будет забирать готовенькие данные из Model? К тому же в таком случае не придется хранить на мой взгляд довольно объемные правила преобразования данных в коллекции.

Каким образом коллекция должна узнать, что конкретный объект Model принадлежит конкретной таблице БД? Это означает, что информацию о принадлежности Model к таблице нужно хранить именно в классе Model, а коллекция должна уметь читать эту информацию. В таком случае Model уже начинает кое - что знать о структуре БД.

Да и на счет мысли о фоновом режиме работы с БД. Каким образом тогда реализовать работу с транзакциями, если невозможно будет точно сказать попали данные в БД или нет? Представим такую ситуацию, приложение запустило транзакцию, произвело некоторые действие с данными удалило/изменило/добавило. Часть этих данных были выгружены в БД в фоновом режиме, а часть осталась ждать в памяти и тут возникает исключение, программа откатывает транзакцию и откатывается только та часть, которая была выгружена. А что сделается с тем что было в памяти можно только догадываться.... Получается чтобы корректно отрабатывались транзакции, половину механизма транзакций придется перенести в DataController, а это уже выходит далеко за рамки модели данных...

Цитата
Вот для этого и нужны DataController.
Мне кажется набор полей, которые нужно загрузить должно задавать основное приложение, которое работает с моделью данных. Например при запросе объекта Model из коллекции, а модель данных должна позволять загружать произвольное количество полей из набора содержащегося в конкретном объекте Model.
Записан

Любимая игрушка - debugger ...
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #16 : 29-01-2012 19:25 » 

Цитата: RuNTiME
Но в таком случае DataController выступает в роли одной глобальной модели, в которой сосредоточена вся логика по работе с БД, а классы Model уже фактически не являются моделями. Классы Model выступают в виде простых структур для хранения данных.
Я этого не понимаю. Model - по определению объект предметной области. Т.е. структура любой сложности с методами. Единственное, что в общем случае про неё точно можно сказать - она не знает и знать не должна про существование БД, GUI и т.п. вещей, к предметной области не относящихся. Если это не так, то всё преимущество архитектур типа MVC теряется.

Цитата: RuNTiME
Не лучше ли будет перенести логику преобразования и проверки данных в классы Model, а коллекция DataController уже будет забирать готовенькие данные из Model?
Опять же, нужно различать преобразования и проверки предметной области и БД-специфичные.

И ещё: модель данных и модель предметной области - это разные вещи. Если у тебя первична модель данных, а классы в программе ты собираешься жёстко подчинять существующим в модели данных таблицам, выйдет неудобная в использовании конструкция.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
RuNTiME
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #17 : 29-01-2012 20:00 » 

Цитата
Model - по определению объект предметной области.

Да в общем случае это так. Но например ORM реализованный в django (выше есть ссылка). В моделях используется смешанный подход: задается структура таблицы (БД специфичная модель) и реализуется модель предметной области. При этом не нарушаются законы MVC.

Есть и другой подход реализованный в LiteSQL http://sourceforge.net/apps/trac/litesql/wiki/QuickStart на основе описания структуры БД и связей создается модель данных в которой каждый объект Model соответствует своей таблице и выполняет БД специфичные проверки и преобразования. Такой подход мне и хотелось бы использовать. В таком случае получается что объекты Model и DataController относятся именно к модели данных. А уже в свою очередь часть классов приложения, работающих с объектами Model будут относится к модели предметной области, а View и Controller уже в свою очередь работать с ними.

Говоря о проверке и преобразовании данных в объектах Model я имел ввиду БД специфичные проверки. Такой подход упрощает реализацию модели данных.

PS: И все же пока не ясно, каким образом коллекция должна узнавать к какой таблице принадлежит конкретный объект Model.
Записан

Любимая игрушка - debugger ...
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #18 : 30-01-2012 05:06 » 

Цитата: RuNTiME
И все же пока не ясно, каким образом коллекция должна узнавать к какой таблице принадлежит конкретный объект Model.
Не знаю, это ты выдумал коллекцию Улыбаюсь Я говорил лишь о коллекции полей. Улыбаюсь

А так каждый класс соответствует таблице, следовательно, информация о принадлежности жёстко определена на уровне компиляции.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines