Как быстро и надежно сравнить объекты 1С для выявления изменений?

Программист 1С v8.3 (Управляемые формы)
← На главную

Мы часто сталкиваемся с необходимостью определить, были ли изменены данные объекта в системе 1С. Это требуется для самых разных задач: от предотвращения ненужной записи объекта при закрытии формы до сложной регистрации изменений для обмена со сторонними информационными системами или ведения версионирования. В этом тексте мы рассмотрим различные подходы к быстрому и надежному сравнению объектов, выясним их преимущества и недостатки, а также разберем конкретные примеры реализации.

1. Использование свойств "Модифицированность" и "ВерсияДанных"

Начнем с самых простых и встроенных механизмов, которые предоставляет платформа 1С.

Свойство Модифицированность

Это свойство формы или объекта метаданных, которое сигнализирует о том, были ли изменены данные объекта с момента его открытия или создания. Оно удобно для интерактивного взаимодействия с пользователем, например, чтобы предложить сохранить изменения перед закрытием формы.

Пример использования:

Предположим, пользователь открыл документ, что-то изменил, но потом решил закрыть его без проведения. В этом случае свойство Модифицированность будет истина, и мы можем задать вопрос о сохранении.

Однако, у Модифицированность есть свои ограничения:

  1. Оно реагирует на любые изменения, даже если они затем были отменены, или на изменения, которые не влияют на "суть" объекта (например, изменение несущественного реквизита, который не должен влиять на логику обмена).
  2. Модифицированность не всегда устанавливается автоматически при изменении реквизитов объекта на сервере, особенно если изменение происходит программно вне прямого взаимодействия с элементами формы. В таких случаях может потребоваться принудительная установка Модифицированность = Истина.
  3. Это свойство не подходит для надежной регистрации изменений для обмена данными или версионирования, так как оно не предоставляет детальной информации о том, что именно изменилось, и может быть сброшено.

Свойство ВерсияДанных

Свойство ВерсияДанных объекта содержит уникальный идентификатор, который изменяется при каждой записи объекта. Оно может использоваться для быстрого определения факта изменения объекта, но не предоставляет информации о характере изменений. Помимо отдельных объектов, иногда требуется проводить глобальный аудит, например, выполнять сравнение значений констант для поиска расхождений в настройках двух баз, чтобы убедиться в идентичности функциональных опций.

Пример использования:

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

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

2. Сравнение через сериализацию (ЗаписьFastInfoset, ЗаписьJSON)

Перейдем к более надежным, но и более ресурсоемким методам. Использование сериализации объектов в форматы FastInfoset или JSON для последующего сравнения их контрольных сумм или самих сериализованных представлений является распространенным подходом для определения изменений.

Принцип работы:

Мы берем объект, преобразуем его в строку (или бинарные данные) с помощью методов сериализации, а затем сравниваем эту строку с ранее сохраненной строкой того же объекта. Если строки отличаются, значит объект изменился. В случае работы с XML-форматом можно использовать специализированный инструмент для сравнения XML-документов, который находит отличия в атрибутах, текстах и даже порядке узлов.

Производительность ЗаписьFastInfoset и ЗаписьJSON:

В общем случае, производительность сериализации может варьироваться. Для однократной сериализации JSON может быть немного быстрее, но при многократных операциях сериализации XML (и, вероятно, FastInfoset как его бинарный аналог) может показать лучшую производительность из-за возможного кэширования или особенностей реализации библиотеки.

Надежность сравнения через сериализацию:

Сериализация объекта в строку или бинарные данные, а затем сравнение этих данных или их хешей, позволяет надежно определить, были ли изменены свойства объекта. Это особенно полезно для сложных объектов со множеством вложенных свойств (табличных частей, реквизитов-ссылок).

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

Применение:

Этот подход хорошо подходит для регистрации изменений в объектах для обмена со сторонними информационными системами или для версионирования, когда необходимо отслеживать каждое изменение объекта.

3. Хеширование данных объекта (КонтрольнаяСумма)

Один из наиболее эффективных и гибких подходов — это хеширование данных объекта. Мы создаем уникальную "контрольную сумму" или хеш-значение на основе содержимого объекта. Даже небольшое изменение в объекте приведет к совершенно другому хешу. Сравнение хешей является очень эффективным способом быстрого определения, изменился ли объект.

Принцип работы:

Разберем по шагам, как это работает:

  1. Мы определяем, какие именно данные объекта являются значимыми для сравнения. Это ключевой момент, который позволяет избежать ложных срабатываний при изменении "незначащих" реквизитов (как, например, "ДатаОперации" в примере из форума).
  2. Эти значимые данные передаются в функцию хеширования.
  3. Функция хеширования генерирует уникальную строку (хеш-сумму).
  4. Мы сохраняем эту хеш-сумму (например, в регистре сведений) при первой записи объекта или при последнем успешном сравнении.
  5. При последующих проверках мы генерируем новый хеш для текущего состояния объекта и сравниваем его с сохраненным. Если хеши разные, объект был изменен.

Пример реализации функции хеширования и сравнения:

Рассмотрим пример кода, который был предложен в теме на форуме и адаптирован для нашего случая. Здесь используется объект ХешированиеДанных с алгоритмом MD5.

Сначала определим вспомогательную функцию КонтрольнаяСумма, которая будет принимать данные и возвращать их хеш:


// Функция: КонтрольнаяСумма
// Назначение: Вычисляет контрольную сумму (хеш) для переданных данных.
// Параметры:
//   Данные - Произвольные данные, для которых нужно вычислить хеш.
// Возвращаемое значение: Строка - хеш-сумма данных.
Функция КонтрольнаяСумма(Данные) Экспорт
	ХешированиеДанных = Новый ХешированиеДанных(ХешФункция.MD5);
	
	Если ТипЗнч(Данные) = Тип("Структура") Тогда
		// Предполагаем, что структура содержит ключи "Объект" и, возможно, "ДополнительныеРеквизиты".
		// Мы можем адаптировать это для любых значимых реквизитов.
		ХешированиеДанных.Добавить(Данные.Объект);
		Если Данные.Свойство("ДополнительныеРеквизиты") Тогда
			// Важно: сложные типы данных (структуры, массивы, коллекции) нужно сериализовать
			// перед добавлением в хеш, чтобы порядок и представление были консистентными.
			ХешированиеДанных.Добавить(ОбщегоНазначения.ЗначениеВСтрокуXML(Данные.ДополнительныеРеквизиты));
		КонецЕсли;
	Иначе
		// Для простых типов данных или уже сериализованных строк.
		ХешированиеДанных.Добавить(Данные);
	КонецЕсли;
	
	Возврат СтрЗаменить(ХешированиеДанных.ХешСумма, " ", "");
КонецФункции

Теперь рассмотрим функцию ВерсияОтличаетсяОтРанееЗаписанной, которая использует КонтрольнаяСумма для сравнения текущего объекта с его последней сохраненной версией:


// Функция: ВерсияОтличаетсяОтРанееЗаписанной
// Назначение: Определяет, отличается ли текущая версия объекта от последней записанной.
// Параметры:
//   Объект - Объект базы данных (например, ДокументОбъект, СправочникОбъект).
// Возвращаемое значение: Булево - Истина, если версия отличается, Ложь - в противном случае.
Функция ВерсияОтличаетсяОтРанееЗаписанной(Объект)
	
	// Для хранения контрольной суммы и других данных версии объекта нам потребуется
	// специальный регистр сведений, например, "ВерсииОбъектов".
	ТекстЗапроса = 
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ВерсииОбъектов.КонтрольнаяСумма
	|ИЗ
	|	РегистрСведений.ВерсииОбъектов КАК ВерсииОбъектов
	|ГДЕ
	|	ВерсииОбъектов.Объект = &Объект
	|
	|УПОРЯДОЧИТЬ ПО
	|	ВерсииОбъектов.НомерВерсии УБЫВ"; 
	
	Запрос = Новый Запрос(ТекстЗапроса);
	Запрос.УстановитьПараметр("Объект", Объект.Ссылка);
	Выборка = Запрос.Выполнить().Выбрать();
	
	ТекущаяКонтрольнаяСумма = КонтрольнаяСумма(ОбщегоНазначения.ЗначениеВСтрокуXML(Объект));
	
	Если Выборка.Следующий() И Не ПустаяСтрока(Выборка.КонтрольнаяСумма) Тогда
		Возврат Выборка.КонтрольнаяСумма <> ТекущаяКонтрольнаяСумма;
	КонецЕсли;
	
	Возврат Объект.ЭтоНовый() ИЛИ ТекущаяКонтрольнаяСумма <> КонтрольнаяСумма(ОбщегоНазначения.ЗначениеВСтрокуXML(Объект.Ссылка.ПолучитьОбъект()));
	
КонецФункции

Выбор алгоритма хеширования:

Хотя MD5 является распространенным алгоритмом, для сценариев, где важна криптографическая стойкость, рекомендуется использовать более современные решения.

Что хешировать:

Мы выяснили, что важно определить, какие данные объекта должны быть включены в хеш. Исключение "незначащих" реквизитов позволяет избежать ложных срабатываний. Для сложных объектов может потребоваться предварительная сериализация данных в определенный формат (например, JSON или FastInfoset), а затем хеширование полученной строки или бинарных данных. Это обеспечивает консистентность представления данных для хеширования.

Производительность:

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

4. Механизм версионирования объектов в БСП

Библиотека Стандартных Подсистем (БСП) предлагает готовый механизм версионирования объектов, предназначенный для хранения истории их изменений и позволяющий откатываться к предыдущим версиям.

Механизм работы:

Стандартное версионирование в БСП часто использует сериализацию объектов, например, через ЗаписьFastInfoset, для сохранения полной версии объекта в специальном регистре сведений. Если вы используете устаревшие платформенные решения, может быть полезно перейти с платформенного механизма История данных на механизм Версионирование объектов БСП для более гибкого управления хранением версий.

Влияние на производительность:

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

Оптимизации и альтернативы:

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

5. Обнаружение изменений для обмена со сторонними системами

Когда речь идет об обмене данными со сторонними информационными системами, требования к обнаружению изменений могут быть специфичными.

Регистрация в планах обмена:

В типовых конфигурациях 1С для регистрации изменений объектов используются планы обмена. Объекты регистрируются в плане обмена при каждой записи. Чтобы упростить ручное управление этим процессом, можно использовать расширение, позволяющее выполнять регистрацию или удаление объектов из плана обмена прямо с формы элемента или списка.

Использование хешей для контроля перед выгрузкой:

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

Рассмотрим, как это работает:

  1. При первой успешной выгрузке объекта мы вычисляем его хеш-сумму.
  2. Эту хеш-сумму мы храним в отдельном регистре сведений, привязанном к объекту и узлу обмена.
  3. При последующих выгрузках мы сначала проверяем, зарегистрирован ли объект в плане обмена.
  4. Если объект зарегистрирован, мы вычисляем его текущий хеш и сравниваем с сохраненным.
  5. Если хеши совпадают, значит, значимых изменений нет, и мы можем не выгружать данные, удалив регистрацию из плана обмена.

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

Заключение

Мы рассмотрели несколько подходов к быстрому и надежному сравнению объектов в 1С. Выбор конкретного метода зависит от ваших задач:

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

← На главную