[JAVA]Hibernate O/R без уникального primary key

puare

Отцы! Никто не в курсе, как можно сабж реализовать?

puare

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

Hastya

как ты себе это представляешь в идеологии Hibernate?
попробуй composite-id, если уж нет выделенного ключа
Primary key есть всегда, надо только его выделить.

puare

Primary Key есть всегда? И всегда он уникальный? Позволь не согласиться. В моем случае у каждого объекта есть идентификатор, но т.к. в системе хранится несколько версий объекта он не является уникальным (т.е. все версии одного и того же объекта имеют один и тот же ид). Разные объекты - разные ид, в рамках версий одного и того же объекта ид совпадает.
Композитный ключ тоже не подходит, потому что инфа о версии хранится в виде пары дат (время действия версии и поэтому пара ид+нач. дата хоть и будет идентифицировать объект, но выборку по ней делать абсолютно бесполезно...
Так что тут либо добавочную колонку, либо... либо хрен его знает что!
Я новичок в Хибернэйте, так что советы и поучения - велкам очень дажэ!

Hastya

У тебя проблемы не с Hibernate, а с моделью данных. В реляционной модели любая запись имеет ключ. Вырожденный случай - когда ключом являются все поля записи (объекта). Попробуй сам проанализировать.

puare

Вполне возможно. Тогда что ты можешь посоветовать для реализации версионности объектов? Какую модель данных? Модель, которой хочу воспользоваться я (см. выше) была использована в одной достаточно крупной системе, которая работатет и продается уже не первый год. Там, правда, без Hibernate написано все было, на Oracle Forms (ну PL/SQL короче и все просто ок было. Вот я и решил перенести этот подход на свой проект.

Sanjaz

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

puare

Два инта? ну там структура счас примерно такая:
id -- id идентификатор (non-unique)
fd -- timestamp дата начала действия версии
td -- timestamp дата окончания действия версии
.
.
при выборке запросы типа where id=? and fd<date and td>date для получения версии на дату date. Куда здесь еще один инт совать? И с чем его сравнивать?
я пока сделал так- добавил еще одну колонку с уник. ид, а все выборки делаю не учитывая эту колонку, т.е. она нужна только для внутренних целей хибернейта.
Может, я не прав в модели данных? Поправьте, если кто может.

Hastya

Так у тебя версионность одной сущности или нескольких?

puare

нескольких етессно...
в таблице лежит набор версий для набора объектов
а что такое сущность в твоем понимании?
я несколько в терминах еще путаюсь, так что...

Hastya

Короче ты ведешь разговор про одну-единственную таблицу?

puare

таблиц таких может быть много (столько же, сколько объектов в системе, вообщето).
Идея в чем - при изменении данных объекта мы не меняем его запись, а открываем новую версию, т.е. на последней версии время "по" ставим текущим и вставляем новую запись с тем же ид объекта и соотв временами действия.
между несколькими таблицами такого рода вполне можно делать связи, при этом выборка (в терминах sql) делается примерно так:


select ... from tbl1, tbl2 where tbl2.master=tbl1.id and tbl2.fd<date and tbl2.td>date and tbl1.fd<date and tbl1.td>date


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

Hastya

Ну я бы это нормализовал так:
версию выделяем в отдельную таблицу, например, VERSION, с числовым идентификатором версии. fd/td суть атрибуты версии. Далее, каждый объект в качестве одного из атрибутов первичного ключа имеет номер версии. Чтобы попасть в какой-то период времени, надо сначала узнать нужный номер версии. Чтобы вытащить объект, надо знать его идентификатор и номер версии.
Недостатки очевидны - не может быть объектов, покрывающих сразу несколько версий. Переход к следующей версии усложняется. Зато - полная определенность с доступом к объекту.

puare

а... понял... но запросы на выборку усложнятся серьезно в этом случае, а как следствие и время выборок. Так как система будет достаточно большой, это критично...

bansek

Может я чего-то не понимаю, но ведь в твоем случае как раз набор из трех аттрибутов: id, fd, td и будет primary key (вообще говоря, по определнию)
Насколько я помню, hibernate поддерживает кастом запросы для поиска объекта по ключу (в твоем случае для hibernate`а имхо ключом будет пара id, date, а то, что один и тот же объект можно получить по разным парам id, date - не его, хибернейта, собачье дело).

Hastya

На самом деле все хуже В его модели можно за какой-то период получить сразу два объекта. Или ни одного.
Но все это цветочки, а вот подумай, что будет, если между объектами есть отношения, т.е. references. Как в этом случае поведет себя база, предсказать не берусь

puare

В его модели можно за какой-то период получить сразу два объекта

как, если не секрет?
для ссылочных объектов я выше приводил пример запроса
На такой версионности построена одна из очень известных биллинговых систем, кстати, так что модель эта то что называется, проверена в полевых условиях. Просто появилось желание прикрутить Hibernate к проекту, вот и маюсь.

bansek

> В его модели можно за какой-то период получить сразу два объекта.
Да, но какое это имеет отношение к primary key?
А второе замечание я вообще не понял

Hastya

1. Если для тройки (id, from, to) существуют сразу несколько объектов, удовлетворяющих условию выборки => ergo эта тройка не является ключом.
2. Допустим, объект A ссылается на объект B. Как выглядит внешний ключ объекта B? На какую конкретно версию объекта B ссылается объект A?
3. Биллинговые системы - плохой пример нормализации, там все сделано из соображений performance.

zya369

id, fd, td и будет primary key

если я прально понял, то один и тот же объект не может иметь две версии с пересекающимися промежутками "жизни", так что это будет не primary key а unique просто...
а primary будет минимум 2 - id,td id,fd
а касаемо версий - имхо для версий вести отдельную таблицу, а в основной хранить только текущую версию
+ плюс ввести поле "номер версии"
ЗЫ это все касаемо модели данных...
а хибернэйт ваш я не рюхаю

puare

2. Допустим, объект A ссылается на объект B. Как выглядит внешний ключ объекта B? На какую конкретно версию объекта B ссылается объект A
Ссылка идет не на конкретную версию, а на сам объект. При выборке данных берется версия, актуальная на заданую дату. Я ж приводил пример запроса выше.
Ну да, по идее {id, from, to} будет ключом записи. Только выборка по такому ключу лишена смысла в силу того, что выборки делаются (ну по большому счету) по 2ум параметрам - id объекта и дате. Что-то типа
id=? and fd<? and td>? в where части запроса стоит

Hastya

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

puare

Как это нет такой гарантии? Гарантия есть, она в бизнес-логике реализуется.

Hastya

Так зачем тебе тогда Hibernate, если у тебя все в бизнес-логике реализуется?

6yrop

Допустим, объект A ссылается на объект B. Как выглядит внешний ключ объекта B?

В реляционной модели "ссылки" реализуется не всегда через внешние ключи. Вообще, в основном внешние ключи используются СУБД для поддержания целостности БД, прямого (теоретического) отношения к "связям" они не имеют. Просто, в часто распространенной ситуации, когда нет бизнес-правил, соответствующих 4-ой и 5-ой нормальным формам, связь по внешнему ключу используется для навигации (например, ADO.Net).
Если в рассматриваемом примере бизнес-правило таково, что любая версия A ссылается на объект B, то это случай 4-й нормальной формы и внешнего ключа не построишь.
Вопрос автора треда я не совсем понял (я не знаком с хивернейт ) . Если первичные и внешние ключи используются для навигации как Ralation в DataSet.Net, то, да, такого Relation не построишь, придется делать select. (хотя может и построишь )
P.S для поддержания правила целостности -- объект B не может существовать без объекта A) -- имхо, придется писать триггер (или забить на него ).

Hastya

В реляционной модели "ссылки" реализуется не всегда через внешние ключи. Вообще, в основном внешние ключи используются СУБД для поддержания целостности БД, прямого (теоретического) отношения к "связям" они не имеют.

Оба-на. А как же реализуются ссылки в реляционной модели?

6yrop

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

Hastya

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

6yrop

соединение можно делать по любым полям, они не обязатнльно должны быть первичным и внешним ключом, вообщем, об этом я и говорил выше

Hastya

потому я и написал "в частности".
а вообще это злостный оффтопик

6yrop

имхо, нет

Hastya

тогда опиши мне как ты в этой модели будешь делать взаимосвязанные версионные объекты

6yrop

надо уточнить, словосочетание "объект A ссылается на B", как оно звучит, когда у объектов есть версии?

Hastya

вот когда ты ответишь на этот вопрос, вскроются все недостатки решения с этими from/to

6yrop

ты думаешь, я сам себе вопросы в форуме задаю? на этот вопрос должен ответить постановщик задачи.
Если бизнес-правило таково, что одна версия A ссылается на одну версию B, то будут две таблицы (id_a, date_a, ..... (id_b, date_b, id_a, date_a, ....... в принципе можно убрать столбец date_a из второй таблицы, но тогда нельзя будет построить внешний ключ и поддерживать ограничение целостности, и в запросах придется явно указывать дату.
Если бизнес-правила другие, то и таблицы, возможно, будут другими.
P.S. date_a и date_b это даты начала версии, вводить в первичный ключ даты окончания нехорошо, поскольку можно разрешить им принимать значение null

6yrop

что такое
from/to

puare

В запросах дату и так надо явно указывать в этой модели.
Версия объекта ссылаться ни на кого не может. Ссылаться может объект И не на версию другого объекта, а на сам другой объект.
from/to это даты начала и окончания жизни версии объекта

6yrop

В запросах дату и так надо явно указывать в этой модели.

с джойнить по двум полям и всё. Указывать явно, я имел ввиду, указывать значение даты типа '01/01/2002' или типа того

6yrop

ааа, ну вот и мудохайтесь со своими "объектами" и "ссылается", пока вы сами не поймете, что под этими словами вы имеете ввиду и не объясните другим, у вас так и будут возникать проблемы. В реальности ни объектов ни ссылок не существует.

Hastya

Просто, ИМХО, легкость использования from/to кажущаяся, и очень много сил придется потратить на поддержание корректности этих временных интервалов в базе (т.к. информация дублируется, а следовательно, денормализована). Если это делается из соображений скорости, то на Hibernate сразу рекомендую забить, ибо он не для этого, и система запросов и кэширования у него, кстати, целиком построена на первичных ключах.
Мы в свое время делали систему, где реально поддерживалась версионность деревьев объектов как на уровне компонентов (отношений часть-целое так и на уровне произвольных связей типа один-ко-многим. Писали месяца два как минимум. Я это к тому, что задача сама по себе совершенно недетская.
Оставить комментарий
Имя или ник:
Комментарий: