| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Java EE (J2EE) и Spring > hibernate пометить поле удалённым |
| Автор: Samotnik 30.4.2011, 15:50 | ||
| Привет Есть приложение работающее с БД через хибер. Нужно, при удалении записей не удалять их физически из БД, а помечать как удаленные, для того чтобы в последующем, можно было делать запросы на удалённые записи. Сделал это так: добавил в таблицы поле boolean deleted, по дефолту оно false, как только запись удаляется, я сеччу в это поле null, таким образом я избегаю unique constraint violated эксепшена, выглядит это так:
Всё отлично работало с MySQL, в БД спокойно ложились записи вида: ('test1', null) ('test1', null) ('test1', null) ('test1', null) и т.д. Но вот беда, перешёл на Oracle, а там это не прокатывает, получается что там null == null если этот null в состовном ключе находится, и как следствие вылетает constraint violated эксепшен. Вопрос, что можно придумать в данной не простой ситуации, что можете подсказать или посоветовать Как вариант, можно попробовать использовать http://www.jboss.org/envers, но может другие решения есть ? |
| Автор: Старовъръ 30.4.2011, 16:01 |
| Зачем нужны Unique constraints на deleted? |
| Автор: carper 3.5.2011, 08:18 |
Null, строго говоря, не следует использовать как флаг, т.к. null - означает неопределенное состояние. Поле - метка удаленной записи не должно нести на себе каких-то уникальных констрейнов, как вам сказал Старовъръ. В сухом остатке - создайте boolean field named as DELETED default false, при удалении заносите туда true, а не null. И поле не должно быть частью первичного ключа! |
| Автор: Старовъръ 3.5.2011, 21:21 |
| Ну тогда располагай хотя бы true/false, а не true/null. Ну или в таком случае введи понятие версии, а не флажка. Если мы удалили или что-то изменили, то создается новая версия объекта с таким же ID и с инкременченной версией. Или может имеет смысл это хранить в каком-то архивном хранилище дабы не снижать производительность БД. |
| Автор: Samotnik 4.5.2011, 00:33 |
| Старовъръ, т.е. использовать http://www.jboss.org/envers ? |
| Автор: carper 4.5.2011, 09:14 | ||
Введите ограничение на то, что поле name просто должно быть уникальным, независимо от того есть ли такое поле помеченное как "удаленное" или нет. Т.к. иначе вы получаете сразу два Васи, отличающихся только тем, что один из них помечен как удаленный - спрашивается и какой из 2-х Васей в таком случае помечен? Более того, поскольку у вас запись только помечена как удаленная, это автоматически предполагает, что вы можете передумать, тогда совсем уж глупо иметь двух Васей, отличающихся только признаком удаленной записи. Я, кстати, вообще подозреваю, что вы на самом деле хотите запись помечать не как удаленную, а как скрытую, или неактуальную, предполагая, что с прежним Васей что-то связано. У вас 100% проблемы не с пометкой полей, а с архитектурой. В частности с первичными ключами. P.S. И причем тут envers? Вам таки нужна версионность и куча записей в архиве на каждого удаленного Васю? Во-первых, это просто делается и без envers - просто введите соотв. поле, во-вторых, версионность для одного и того же Васи, скажем для адресов его проживания, совсем не одно и тоже, что и версионность для ряда совсем разных Вась, в последнем случае, это вообще чушь какая-то получится. P.P.S. И наличие синтетического первичного ключа по ID в большинстве случаев совсем не освобождает от необходимости "человекопонимаемого" АК. |
| Автор: MisterCleric 4.5.2011, 10:59 |
| Привет всем. Я делаю так: При пометке записи как удаленная в те поля, которые должны быть уникальны, к существующему значению добавляю метку времени, а также проставляю статус как удаленная. Таким образом поле всегда уникально, избавляемся от композитный констрейнтов и ключей. А также такие записи всегда можно отфильтровать по like. Естественно, что у меня заведомо длина поля больше чем, я предоставляю для ввода пользователем... |
| Автор: Samotnik 4.5.2011, 11:21 | ||||
не понимаю, как это можно сделать без композитных ключей ? Ведь поле name должно быть само по себе уникально, т.е. что бы пользователь не смог вставить запись Dima дважды Поясни плиз. И вот это я совсем не понял:
|
| Автор: MisterCleric 4.5.2011, 11:52 | ||
| Поясню на примере: 1. Было у нас в записи значение поля name Вася. 2. Я помечаю эту запись как удаленную. 3. Апдейчу поле name приблизительно так:
Благодаря тому, что время всегда идет вперед, пользователь никогда не сможет нарушить уникальность Добавлено через 2 минуты и 10 секунд И насчет композитный ключей: у тебя должно быть поле ID с генерируемым числовым значением - автоинкремент, сиквенс, или что там у тебя поддерживает БД |
| Автор: Samotnik 4.5.2011, 12:06 | ||||||
| MisterCleric, прикольно, эванокак А чем хуже, если объединить name и deleteTimeStamp в UniqueConstraint ? Вроде ж тоже уникальность будет.
С Id тут вообще целая история, он у меня вот так генерится:
где
Вроде так работает и для MySQL и для Oracle |
| Автор: MisterCleric 4.5.2011, 12:19 | ||
Можно и так, но какое тогда у тебя будет там значение у "неудаленных" записей? Ведь уже обсудили, что NULL не нормально использовать в уникальных констрейнтах Добавлено через 1 минуту и 10 секунд И по твоему генератору. Какая тогда проблема? Ведь поле name у тебя не участвует в PK записи. |
| Автор: Samotnik 4.5.2011, 13:10 | ||
MisterCleric, ясно. А что делать, если есть сущности в которых уже есть композитные ключи, например:
Как в таком случае ее помечать удалённой ? |
| Автор: MisterCleric 4.5.2011, 14:08 |
| Но у тебя же здесь композитный ключ состоит из внешних ключей. На что у тебя здесь идет уникальность? Где то бизнес-поле, которое требует уникальности? У тебя же такая конкретна сущность относится к конкретным внешним сущностям. Если ты их помечаешь как удаленные эта тоже должна быть таковой. Или у тебя эти внешние ключи неуникальны? |
| Автор: Samotnik 4.5.2011, 14:25 |
| MisterCleric, да, всё верно, спасибо, должно работать. Еще вопрос, генерить hashCode() и equals() лучше всего по id или uniqueConstraints или id + uniqueConstraints |
| Автор: MisterCleric 4.5.2011, 14:54 |
| Вообще советуют по какому-нибудь бизнес-ключу. В твоем случае, наверное, uniqueConstraint достаточно. Но это, конечно, если оно у тебя всегда заполнено |
| Автор: Samotnik 4.5.2011, 15:54 | ||||
MisterCleric, понял, спасибо. На счет сущностей, которые связывают другие, всё же способ не работает:
Создаю запись: id, device_id, app_id, deleted (1; 5; 9; false) Удаляю (1; 5; 9; true) Создаю опять точно такую же - (2; 5; 9; false)
|
| Автор: MisterCleric 4.5.2011, 16:24 |
| Не понял. Почему "Создаю такую же"? Ведь это же новая запись: должен быть id новый. Что у тебя за бардак такой? |
| Автор: Samotnik 4.5.2011, 16:54 |
| MisterCleric, да, извини не дописал, уже исправил, id конечно же новый. Но при чем он тут ? У меня уникальность стоит по полям device_id + app_id именно по этому я не могу создать нового subscriber если уже существует такой же удалённый. |
| Автор: Samotnik 4.5.2011, 17:13 |
почему бардак Вот представь, очень простой пример. Тебе дали задание разработать сущность "Пользователь" с двумя полями Имя, Фамилия. При условии, что нельзя создать двух одинаковых записей с одной именем + фамилией(например нельзя создать Дмитрий Петров дважды Плюс предусмотреть сохранение удалённых пользователей, т.е. не удалять их физически из БД, а каким-либо образом помечать что они удалены и не мешали созданию новых полей (и их удаление) Т.е. БД должна себя вести, как будто этих записей нету в таблице. Это я релизовал с помощью флага deleted, путем выставления его в null при удалении записи. С MySQL всё превосходно работало, а вот с Oracle нет. |
| Автор: MisterCleric 4.5.2011, 17:19 |
| Тогда добавь к этой уникальности еще и ID. Или вообще убери эту уникальность на внешние ключи. Не понимаю ее смысла. У тебя что по логике нельзя создать несколько Subscriber на один и тот же Device в одном и том же App? Да и вообще самый идеальный вариант - провести нормализацию: удаленные записи переносить в архивные таблицы. |
| Автор: Samotnik 4.5.2011, 17:55 | ||||
не вариант, тогда можно будет создавать Дмитрий Петров сколько угодно раз. смысл в том, чтобы запретить создавать более одной записи Дмитрий Петров
а это как ?
нет, нельзя, а какой смысл создавать их несколько ? Зачем столько записей ? Подписчик должен быть один. id, device_id, app_id, deleted (1; 5; 9) (2; 5; 9) (3; 5; 9) (4; 5; 9) (5; 5; 9) (6; 5; 9) .... |
| Автор: MisterCleric 4.5.2011, 18:05 |
| Не знаю таких ссылок, но на пальцах это так: 1. Копия оригинальной таблицы с префиксом ARC 2. Убираем констрейнты, окромя PK 3. Вызываем обычное удаление 4. В БД на удаление должен быть триггер на каждую таблицу 5. Он должен просто взять удаляемую запись и проинсертить в соответствующую архивную таблицу. 6. Потом ты сможешь эти записи доставать через named-query, а результат повесить на твою основную сущность. Польза: разделяем данные по логике, повышается производительность на основной таблице. Минусы: много работы на БД, написание named-query на удаленные записи. Нужно здесь хорошенько подумать, стоит ли все данные оставлять: может будет нормальным, если какие-то связи все-таки будут удаляться навсегда? |
| Автор: Samotnik 5.5.2011, 12:23 |
триггер - это ведь sql код. т.е. вручную нужно прописывать, а через хибер можно как-нибудь написать триггер ? |
| Автор: Samotnik 6.5.2011, 16:03 | ||
| Если кому интересно, то проблема решилась как всегда - очень просто. Поле deleted - сделать типом long по дефолту = -1L, при удалении сетить в это поле тот id, который удаляется Добавлено через 6 минут и 7 секунд
так может тогда лучше по id ? |
| Автор: MisterCleric 6.5.2011, 16:30 | ||
| Привет. Решение красивое. Сам додумался? Век живи - век учись... По поводу equals & hashCode. У меня вот такой вот класс для всех сущностей:
Я, кажись, его тут уже на форуме выкладывал. Он хорошо работает в рамках логики, которую пишу я. Но для самого Hibernate оно не годиться, так как тот у себя в коде использует класс http://grepcode.com/file/repo1.maven.org/maven2/org.hibernate/hibernate/3.2.0.ga/org/hibernate/util/IdentityMap.java, который для hashCode вызывает System.identityHashCode, а для equals еще хуже - идет сравнение ссылок. |
| Автор: Samotnik 6.5.2011, 17:06 |
| спс |