К статье
Практический материал

Разработка и Конфигурация · 11 мин

Тип «Хранилище значений» в 1С 8.3: что это и как с ним работать

Что такое тип «Хранилище значения» в 1С 8.3, где он применяется, как сохранить и прочитать данные, ограничения, типичные ошибки и практические примеры кода.

«Хранилище значений» (ValueStorage) — это специальный тип данных в платформе 1С:Предприятие 8.3, который позволяет сохранить в базе данных практически любой объект: картинку, файл, таблицу значений, структуру, массив или даже результат сериализации сложного объекта. В отличие от обычных реквизитов с типами «Строка», «Число» или «Дата», хранилище упаковывает данные во внутренний формат платформы и записывает их в базу как единое бинарное поле.

Разработчик сталкивается с этим типом всякий раз, когда нужно прикрепить к элементу справочника фото товара, сохранить настройки отчёта, запомнить печатную форму документа или спрятать служебные данные, которые не должны участвовать в запросах и отборах. Тип доступен для реквизитов справочников, документов, планов видов характеристик, регистров сведений и других объектов конфигурации.

Важно понимать ключевое ограничение: содержимое хранилища «непрозрачно» для СУБД. По нему нельзя строить индексы, делать отборы в запросах, сортировать и группировать. Чтобы получить данные, объект нужно прочитать целиком и распаковать значение в коде. Это определяет и типичные ошибки применения, о которых речь пойдёт ниже.

Что представляет собой тип «Хранилище значений»

Технически хранилище значений — это объект встроенного языка, который принимает любое значение, сериализует его (преобразует во внутреннее двоичное представление) и при необходимости сжимает. В конфигураторе реквизиту назначается тип «ХранилищеЗначения», после чего этот реквизит может принимать данные любого вида — платформа сама заботится об упаковке и распаковке.

При создании хранилища можно указать степень сжатия данных: Новый ХранилищеЗначения(Значение, Новый СжатиеДанных(9)). По умолчанию сжатие не применяется. Сжатие уменьшает объём занимаемого места, но замедляет запись и чтение, поэтому его разумно включать для крупных текстовых или табличных данных, а для уже сжатых форматов (JPEG, ZIP) оно бесполезно.

Где применяется хранилище значений в типовых конфигурациях

Самый массовый сценарий — хранение файлов и изображений. В типовых конфигурациях (Управление торговлей, ERP, Бухгалтерия) картинки номенклатуры и присоединённые файлы исторически хранились именно в реквизитах типа «ХранилищеЗначения», а в современных версиях — через подсистему «Работа с файлами», которая внутри также опирается на хранилище либо на тома на диске.

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

Третий сценарий — служебные данные обменов: очереди сообщений, тела запросов к веб-сервисам, ответы внешних систем. Здесь хранилище ценно тем, что сохраняет данные «как есть», без необходимости описывать их структуру метаданными.

Как записать значение в хранилище

Запись выполняется в три шага: создаётся объект хранилища конструктором Новый ХранилищеЗначения(), результат присваивается реквизиту объекта, объект записывается. Ниже пример сохранения картинки в реквизит справочника.

bsl
&НаСервере
Процедура СохранитьКартинку(СсылкаНаТовар, ПутьКФайлу)
	ДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу);
	Объект = СсылкаНаТовар.ПолучитьОбъект();
	Объект.ДанныеКартинки = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
	Объект.Записать();
КонецПроцедуры

Аналогично сохраняются и «нематериальные» данные — например, структура с настройками обработки. Перед записью убедитесь, что сохраняемое значение вообще сериализуется: объекты типа «COMОбъект», открытые соединения или запросы с активным результатом сохранить нельзя.

Порядок записи значения
  1. 1
    Определите, какие данные нужно сохранить, и убедитесь, что тип сериализуем (таблица, структура, двоичные данные — да; COM-объекты — нет).
  2. 2
    Добавьте в объект метаданных реквизит с типом «ХранилищеЗначения» или найдите существующий.
  3. 3
    В коде создайте хранилище: Новый ХранилищеЗначения(Значение).
  4. 4
    Присвойте хранилище реквизиту объекта и вызовите Записать().
  5. 5
    Проверьте результат: перечитайте объект и вызовите Получить() — значение должно совпасть с исходным.

Как прочитать значение из хранилища

Чтение выполняется методом Получить(), который возвращает исходное значение в исходном типе. Если хранилище пустое (значение не присваивалось), метод вернёт Неопределено — это нормальная ситуация, которую нужно обрабатывать в коде, иначе получите ошибку при обращении к свойствам результата.

bsl
&НаСервере
Функция ПрочитатьНастройки(СсылкаНаЭлемент)
	Запрос = Новый Запрос("ВЫБРАТЬ ХранилищеНастроек ИЗ Справочник.НастройкиОбмена ГДЕ Ссылка = &Ссылка");
	Запрос.УстановитьПараметр("Ссылка", СсылкаНаЭлемент);
	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Следующий() Тогда
		Настройки = Выборка.ХранилищеНастроек.Получить();
		Если Настройки = Неопределено Тогда
			Возврат Новый Структура;
		КонецЕсли;
		Возврат Настройки;
	КонецЕсли;
КонецФункции

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

Ограничения и подводные камни

Главное ограничение уже упоминалось: по содержимому хранилища нельзя искать. Если бизнес-логика требует отбирать записи по какому-то признаку, этот признак должен лежать в отдельном обычном реквизите. Попытка «спрятать» в хранилище данные, по которым потом понадобится отбор, — одна из самых дорогих ошибок проектирования: придётся перечитывать и распаковывать все записи.

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

Третий нюанс — версионирование формата. Если вы сохранили структуру с одним набором полей, а через год код ожидает другой набор, старые записи начнут давать ошибки или неполные данные. Решение — при чтении проверять наличие нужных свойств и задавать значения по умолчанию.

Типичные ошибки
01
Отбор в запросе по содержимому хранилища — невозможен; проектируйте отдельные реквизиты для поисковых признаков.
02
Отсутствие проверки на Неопределено после Получить() — приводит к ошибкам на пустых записях.
03
Хранение несериализуемых объектов (COM, соединения) — ошибка при записи.
04
Изменение полученного значения без повторной записи — данные в базе не обновляются.
05
Сжатие уже сжатых форматов (JPEG, ZIP) — лишняя нагрузка без выигрыша в объёме.

Хранилище значений против альтернатив

Не каждая задача требует именно хранилища. Если данные представляют собой набор однотипных записей с поиском и отборами — правильнее табличная часть или регистр сведений. Если это один длинный текст — подойдёт реквизит типа «Строка» неограниченной длины, который хотя бы можно частично увидеть в запросе и списках.

Сравнение вариантов

Хранилище значений

Любые данные в одном поле; нет отборов и поиска; подходит для файлов, настроек, служебных структур.

Строка неограниченной длины

Только текст; можно выводить в списки и частично искать через ПОДОБНО; не подходит для двоичных данных.

Табличная часть / регистр сведений

Структурированные данные с полноценными отборами и соединениями; требуют описания структуры метаданными.

Тома хранения файлов

Файлы лежат на диске, в базе только ссылки; база не растёт, но нужна настройка и резервное копирование томов.

Как проверить и отладить работу с хранилищем

Простейший способ убедиться, что запись прошла, — открыть элемент в режиме предприятия или выполнить проверочный код: прочитать реквизит, вызвать Получить() и сравнить тип и содержимое с ожидаемым. Для двоичных данных полезно проверить размер: Данные.Размер() — нулевой размер указывает на проблему на этапе получения файла, а не хранилища.

Для массовой диагностики (например, поиск записей с «битыми» данными) используйте запрос с выборкой поля-хранилища и распаковку в цикле с обработкой исключений через Попытка...Исключение. Записи, где Получить() падает или возвращает неожиданный тип, — кандидаты на исправление.

Контрольные точки

Как проверить результат

Тип значения после Получить()
Норма

Совпадает с типом, который сохраняли (ДвоичныеДанные, Структура и т.д.)

Тревожный признак

Неопределено при гарантированной записи или другой тип

Размер двоичных данных
Норма

Сопоставим с размером исходного файла

Тревожный признак

Ноль или резко меньше ожидаемого

Повторное чтение после записи
Норма

Данные идентичны исходным

Тревожный признак

Расхождения — значит, изменяли копию без повторной записи

При обмене данными между базами учитывайте, что хранилище переносится как двоичный блок целиком. Если в плане обмена состав данных меняется, старые хранилища в уже выгруженных пакетах останутся в прежнем формате — закладывайте обратную совместимость при чтении.
ПЗ
Практическое замечание

Часто задаваемые вопросы

Можно ли в запросе отобрать записи по содержимому хранилища значений?

Нет. Содержимое хранилища недоступно для отборов, сортировок и группировок в запросах — платформа работает с ним как с непрозрачным бинарным полем. Если нужен поиск по признаку, вынесите его в отдельный реквизит примитивного типа и заполняйте одновременно с записью хранилища.

Что вернёт метод Получить(), если значение не записывали?

Метод вернёт Неопределено. Это штатная ситуация, а не ошибка: всегда проверяйте результат перед обращением к его свойствам и методам, иначе получите исключение «Поле объекта не обнаружено» или подобную ошибку.

Какие типы данных нельзя поместить в хранилище значений?

Нельзя сериализовать объекты, привязанные к текущему сеансу или внешним ресурсам: COM-объекты, OLE, открытые соединения, результаты активных запросов. Сериализуемы примитивные типы, структуры, массивы, таблицы и деревья значений, двоичные данные, картинки и большинство универсальных коллекций.

Влияет ли хранилище значений на размер базы?

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

Чем хранилище значений отличается от реквизита типа «Строка» неограниченной длины?

Строка хранит только текст, но её можно выводить в списки, частично искать через ПОДОБНО и читать без распаковки. Хранилище принимает любые типы, включая двоичные данные и коллекции, но полностью непрозрачно для запросов. Выбор зависит от того, нужен ли поиск по содержимому.

Как перенести данные из хранилища при изменении структуры сохраняемых данных?

Напишите сервисную обработку: выберите записи запросом, распакуйте каждое хранилище методом Получить(), преобразуйте старую структуру в новую (добавьте недостающие поля, переименуйте), создайте новое хранилище и запишите объект. Выполняйте на копии базы и обрабатывайте исключения для повреждённых записей.

Итог

Что важно запомнить

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