Этот баннер — требование Роскомнадзора для исполнения 152 ФЗ.
«На сайте осуществляется обработка файлов cookie, необходимых для работы сайта, а также для анализа использования сайта и улучшения предоставляемых сервисов с использованием метрической программы Яндекс.Метрика. Продолжая использовать сайт, вы даёте согласие с использованием данных технологий».
Политика конфиденциальности
|
|
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
Здравствуйте. Хотелось бы узнать способы хранения истории изменений. Поделитесь, плиз, кто как хранит историю изменений объектов, как это реализовано. В поиске на форуме находил пару тем, но они древние и не отвечают на все вопросы. Задача такова: при изменении какого-то свойства объекта надо сохранять в базе запись об этом изменении с новым значением, именем юзера, под которым изменилось это свойство, ну и с датой и временем изменения, естественно. Отсюда возникают вопросы: как отследить, когда изменилось конкретное свойство? В чём хранить эти изменения? (в смысле, пользоваться классами или напрямую лучше писать в глобалы) Сейчас на событии OnAfterSave пробегаюсь по свойствам, ищу в базе последнее изменение свойства, сравниваю их и если они не равны, записываю новую запись. Причём такой поиск с использованием классов (ClassHistoryObject) оказался довольно долгим, поэтому думаю писать прямо в глобалы. Проблема ещё и в том, что если свойство представляет ссылку на объект, то его тоже надо как-то разобрать, чтобы хранить не просто айдишник (который со временем может быть удалён и не поймёшь потом, а что было в этом объекте), а описание его полей. Но с этим я как-то ещё разобрался, у всех персистент и сериал обжект классов должен быть обязательным метод ToString(), который возвращает описание объекта. А вот как быть со списками и отношениями... В общем, если кто с этим сталкивался, подскажите, если не трудно, как это лучше организовать. Спасибо. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 28.09.2011, 03:04 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
Vixlerесли кто с этим сталкивался, подскажите, если не трудно, как это лучше организовать Применяем свою "обёртку" для изменения данных в БД... Т.е. ничего из стандартного не трогаем, оставляя возможность его использования но при этом никакие изменения не запишутся никуда... Делаем свои процедурки/функции/методы, коими все должны пользоваться при стандартной модификации БД. Вот эти-то процедурки/функции/методы и ведут "учёт" всех изменений. Всё новое предпочитаем хранить в классах, дабы была возможность работы sql и zen... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 28.09.2011, 08:49 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
в одном из проектов в котором я участвовал, хранение истории было построено довольно просто в ущерб занимаемому месту в бд при изменении объекта будь то через объект или через SQL, в специальном глобале, это был так же как имена глобалов для хранения данных объекта, только на конце "H" и в него хранилось в разрезе каждого ID, времени изменения копия $LB() с данными и именем пользователя под которым произошло изменение, в дальнейшем когда требовалось просмотреть историю изменения то или иного объекта, через интерфейс системы, выводилась таблица с наиболее интересующими полями, где каждая строчка отдельное изменение. и можно всю проследить историю изменения сразу. вроде использовалось %OnAfterSave и триггеры для отлова изменений через SQL. в другом проекте, используется журнал, куда регистрируются события разного вида в том числе и изменения документа, и на каждое поле своя запись в журнале, и когда пользователь хочет просмотреть историю, он видит всю историю по этому объекту хронологически, и для каждого изменения отображается какое значение было до изменения и какое стало после. для всех событий и всех объектов используется один журнал. с таким журналом, администратор может просматривать изменения по одному документу или например, что изменял пользователь в указанный период, так же и попытки авторизации и прочее. только %OnAfterSave, через SQL изменений не происходит. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 28.09.2011, 10:14 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
Vixler, если позволяет логика приложения, можно выгружать перед изменением версию на диск Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 28.09.2011, 17:01 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
DAiMorв другом проекте, используется журнал, куда регистрируются события разного вида в том числе и изменения документа, и на каждое поле своя запись в журнале, и когда пользователь хочет просмотреть историю, он видит всю историю по этому объекту хронологически, и для каждого изменения отображается какое значение было до изменения и какое стало после. для всех событий и всех объектов используется один журнал. с таким журналом, администратор может просматривать изменения по одному документу или например, что изменял пользователь в указанный период, так же и попытки авторизации и прочее. только %OnAfterSave, через SQL изменений не происходит. Вот так же хочу сделать. А как у вас выглядела вообще структура классов или глобалов для хранения истории? Сейчас стоит проблема сравнения текущего значения поля с предыдущим ранее сохранённым значением. Если пробегаться по журналу в поисках последней версии, то чем больше было изменений, тем дольше будет поиск. Как у вас это решалось? И как происходило хранение изменений полей-ссылок, отношений и списков? Ну к примеру. В классе есть поле-отношение один-ко-многим Users, описывающее список юзеров, причём каждый объект списка-отношения - это объект класса User. В какой-то момент список пополняется новым юзером. Как в таком случае у вас бы хранилась информация об этом изменении? Кстати, почему может не работать <propertyName>GetStored? Пишет ошибку: >w a.IsBlockedGetStored() Quit $Select(id'="":$listget($g(^User(id)),18),1:"") } ^ <UNDEFINED>zIsBlockedGetStored+1^User.1 *id doublefintVixler, если позволяет логика приложения, можно выгружать перед изменением версию на диск + Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. Попробовал сделать экспорт объекта в XML-файл функцией XMLExport, когда дошло до поля-отношения, вывалилась ошибка. Не хочет что-то функа эта работать с отношениями. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 29.09.2011, 04:20 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
Vixler, Мы сделали класс, от которого унаследованы все хранимые объекты системы, в нем перегрузили %OnBeforeSave, %OnDelete и %OnAfterSave. В зависимости от типа изменения (создание, изменение, удаление), пишем содержание изменения в историю. Потом можно просмотреть требуемую информацию и выполнить откаты в случае необходимости (простые откаты, разумеется). Вот так примерно: ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 29.09.2011, 05:02 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
2kolesov: Пара вопросов, если можно: 1. Открыли объект через %OpenId, изменили одно/несколько полей, делаем %Save(). Теперь нужно сделать запись в журнал, для этого надо узнать, какие поля были изменены. Каким образом вы это делаете? Через метод <propertyName>GetStored() или через sql-запрос? 2. В приложенном вами скрине, насколько я понял, два поля простых типов %String и %TimeStamp. А как вы обрабатываете поля-ссылки, поля-отношения и поля-списки? Спасибо. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 29.09.2011, 06:30 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
Vixler2kolesov: Пара вопросов, если можно: 1. Открыли объект через %OpenId, изменили одно/несколько полей, делаем %Save(). Теперь нужно сделать запись в журнал, для этого надо узнать, какие поля были изменены. Каким образом вы это делаете? Через метод <propertyName>GetStored() или через sql-запрос? 2. В приложенном вами скрине, насколько я понял, два поля простых типов %String и %TimeStamp. А как вы обрабатываете поля-ссылки, поля-отношения и поля-списки? Спасибо. 1. Через метод <propertyName>GetStored() 2. В примере первое поле - ссылка на объект. В том же суперклассе (где пишется история) есть метод, представляющий этот объект в текстовом виде. В данном случае описание нефтепродукта - это результат работы такого метода, а квадратных скобочках в конце - его айди в БД. По-умолчанию этот метод возвращает "[ID]" а для некоторых наиболее полезных объектов он перегружен, как на картинке - другой пример: "т/х Нарьянмар, рейс 2120, ETA 12-12-11 [1232]", "а там - объект" ;) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 29.09.2011, 08:49 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
Ага, спасибо за ответы. А что насчёт списков и отношений один-ко-многим? Как у вас тогда происходит запись? Допустим поле представляет список из 350 ссылок на объекты какого-либо класса. В определённый момент одна ссылка удаляется(/добавляется/изменяется). Какая запись тогда вносится в журнал? Перечисление всех этих объектов в текстовом виде? Просто такая строка будет огромной и анализировать её наверное будет сложновато. Я тут думаю может писать что-то вроде "field = listOfSomeObjects, operation = 'добавление элемента в список', value = obj.ToString()" ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 29.09.2011, 09:11 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
VixlerАга, спасибо за ответы. А что насчёт списков и отношений один-ко-многим? Как у вас тогда происходит запись? Допустим поле представляет список из 350 ссылок на объекты какого-либо класса. В определённый момент одна ссылка удаляется(/добавляется/изменяется). Какая запись тогда вносится в журнал? Перечисление всех этих объектов в текстовом виде? Просто такая строка будет огромной и анализировать её наверное будет сложновато. Я тут думаю может писать что-то вроде "field = listOfSomeObjects, operation = 'добавление элемента в список', value = obj.ToString()" - Изменения отношений записывается со стороны "много" - Поля-списки, отношения "родитель-потомок" и прочие нестабильные артефакты не используем - система должна быть как риск-процессор (минимум сущностей при максимуме эффективности) и облегчение работы с историей - тому лишнее подтверждение ;) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 29.09.2011, 09:15 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
VixlerПопробовал сделать экспорт объекта в XML-файл функцией XMLExport, когда дошло до поля-отношения, вывалилась ошибка. Не хочет что-то функа эта работать с отношениями. Что за ошибка? Не настроен экспорт связанных объектов в XML? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 29.09.2011, 09:53 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
А списки строк? :) Ну так вроде всё понятно, спасибо, начинает что-то вырисовываться. Списки ссылок мы тоже решили не юзать, но так, на всякий случай, думал обрабатывать и эту ситуацию. К тому же есть ещё %SerialObject-классы, у которых нет функции %Save(), и не получится со стороны таких объектов (в отношениях "один-ко-многим") записать изменение в журнал. Или вы и их не юзаете? ) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 29.09.2011, 10:02 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
VixlerА списки строк? :) Ну так вроде всё понятно, спасибо, начинает что-то вырисовываться. Списки ссылок мы тоже решили не юзать, но так, на всякий случай, думал обрабатывать и эту ситуацию. К тому же есть ещё %SerialObject-классы, у которых нет функции %Save(), и не получится со стороны таких объектов (в отношениях "один-ко-многим") записать изменение в журнал. Или вы и их не юзаете? ) Списки строк конечно используем. Как и всяческие другие коллекции. Но не как свойства хранимых классов. То есть вообще ни одного хранимого свойства типа *List* или *Array* в системе за последних 4 года не появилось. А если бы появилось - программист бы огрёб ;) А "на всякий случай" мы в подобной ситуации просто не запишем изменение - вреда от этого чуть (ну не узнаем мы кто в списке подменил элемент и как). Если решение эффективно на 99,9 процентов, то на оставшуюся десятую долю можно забить - в конце концов есть видео, пыточная комната, полиграф и прочие вполне эффективные и недорогие айти-инструменты - незачем лишать себя разнообразия и удовольствий ;) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 29.09.2011, 10:40 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
VixlerВот так же хочу сделать. А как у вас выглядела вообще структура классов или глобалов для хранения истории? Сейчас стоит проблема сравнения текущего значения поля с предыдущим ранее сохранённым значением. Если пробегаться по журналу в поисках последней версии, то чем больше было изменений, тем дольше будет поиск. Как у вас это решалось? И как происходило хранение изменений полей-ссылок, отношений и списков? Ну к примеру. В классе есть поле-отношение один-ко-многим Users, описывающее список юзеров, причём каждый объект списка-отношения - это объект класса User. В какой-то момент список пополняется новым юзером. Как в таком случае у вас бы хранилась информация об этом изменении? дело в том что у нас поля несколько иного вида, они хранятся в глобале объекта, имеют не стандартный тип а наш, и поэтому мы у себя контролируем что из полей изменилось а что нет, и добавляем соответствующее событие в журнал. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 29.09.2011, 10:58 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
Опять вернулся к этой многострадальной истории... Никак не могу разобраться как сравнивать два значения, новое и сохранённое. Если это простой тип (строка, число) - это ладно. Пофик, если это ссылка на другой объект, сравниваем айдишники. Но если это список (простых типов) или ссылка на объект типа %SerialObject? Например, Код: plaintext 1. 2. 3. 4. 5. 6. 7. SomePeriodGetStored возвращает одно представление объекта, а SomePeriod - другое. Как их привести к одинаковому виду? В документации пишут что-то про StorageToLogical, но её нету у этого класса, как и у всех других... То же самое со списком простых классов. Никак не могу привести два значения к одному виду. Можно конечно использовать вычисляемые поля, которые будут описывать списки и объекты SerialObject, но это надо будет в каждом классе самому отслеживать такие поля. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 08.11.2011, 08:42 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
И всё-таки. Как преобразовать значение к хранимому виду? Вот здесь про функцию LogicalToStorage() сказано, что оно опционально. Но не написано, где эта опция включается, чтобы эта функция была доступна. Может кто-нибудь знает? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 10.11.2011, 01:28 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
VixlerИ всё-таки. Как преобразовать значение к хранимому виду? Вот здесь про функцию LogicalToStorage() сказано, что оно опционально. Но не написано, где эта опция включается, чтобы эта функция была доступна. Может кто-нибудь знает? Опционально - можете добавить методы с этими именами в описание класса и они будут исполняться. Обычно все хранят значения Logical, поэтому никто эти методы не использует. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 10.11.2011, 05:38 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
Ладно, но как тогда корректно сравнить ранее сохранённое значение, полученное по GetStored и текущее значение, когда %Save() ещё не выполнен? Для случая со списком или ссылкой на %SerialObject? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 10.11.2011, 05:52 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
Подниму темку... Решил хранить историю модификации классов таким образом: - создал абстрактный класс Код: vbnet 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. - Применяю наследование Код: vbnet 1. 2. Когда делаю insetr через SQL - данные записываются. Но при update или манипуляций классом ничего не меняется и не записывается... Как бы победить еще и это? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 21.02.2012, 11:16 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
krvsa, s val=$g(%session.Data("userId")) q "" ;а будут ли выполняться нижние строки, вроде выход уже был? s val=##class(mvr.data.employee).%OpenId(val).name q val ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 21.02.2012, 11:21 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
DirksDR , это тестовый вариант... Суть вопроса не в этом. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 21.02.2012, 11:24 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
Пусть будет например так: Код: vbnet 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 21.02.2012, 11:25 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
Всё это срабатывает только при добавлении данных insert... Более ничего данных не меняет. Меня даже устроит если будут изменения по update. Т.к. при классовом подходе я просто создам медод, альтернативный %Save() и проблему т.о. решу... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 21.02.2012, 11:28 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
Прошу прощения Добавление работает и классовым методом, данные записываются Код: vbnet 1. 2. 3. Пришлось усложнить метод Код: vbnet 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. Осталось разобраться с модификацией экземпляра. Дабы всё обновлялось и тогда... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 21.02.2012, 11:40 |
|
||
|
Хранение истории изменений объектов
|
|||
|---|---|---|---|
|
#18+
Update a timestamp property on UPDATE and INSERT via SqlComputeOnChange Отличие в SqlComputeOnChange = (%%INSERT, %%UPDATE) При работе через объекты тоже должно обновляться. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 21.02.2012, 11:46 |
|
||
|
|

start [/forum/topic.php?fid=39&msg=37458232&tid=1557514]: |
0ms |
get settings: |
6ms |
get forum list: |
15ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
152ms |
get topic data: |
9ms |
get forum data: |
2ms |
get page messages: |
72ms |
get tp. blocked users: |
1ms |
| others: | 224ms |
| total: | 487ms |

| 0 / 0 |
