Разработка и Конфигурация · 11 мин
Тип «Хранилище значений» в 1С 8.3: что это и как с ним работать
Что такое тип «Хранилище значения» в 1С 8.3, где он применяется, как сохранить и прочитать данные, ограничения, типичные ошибки и практические примеры кода.
«Хранилище значений» (ValueStorage) — это специальный тип данных в платформе 1С:Предприятие 8.3, который позволяет сохранить в базе данных практически любой объект: картинку, файл, таблицу значений, структуру, массив или даже результат сериализации сложного объекта. В отличие от обычных реквизитов с типами «Строка», «Число» или «Дата», хранилище упаковывает данные во внутренний формат платформы и записывает их в базу как единое бинарное поле.
Разработчик сталкивается с этим типом всякий раз, когда нужно прикрепить к элементу справочника фото товара, сохранить настройки отчёта, запомнить печатную форму документа или спрятать служебные данные, которые не должны участвовать в запросах и отборах. Тип доступен для реквизитов справочников, документов, планов видов характеристик, регистров сведений и других объектов конфигурации.
Важно понимать ключевое ограничение: содержимое хранилища «непрозрачно» для СУБД. По нему нельзя строить индексы, делать отборы в запросах, сортировать и группировать. Чтобы получить данные, объект нужно прочитать целиком и распаковать значение в коде. Это определяет и типичные ошибки применения, о которых речь пойдёт ниже.
Что представляет собой тип «Хранилище значений»
Технически хранилище значений — это объект встроенного языка, который принимает любое значение, сериализует его (преобразует во внутреннее двоичное представление) и при необходимости сжимает. В конфигураторе реквизиту назначается тип «ХранилищеЗначения», после чего этот реквизит может принимать данные любого вида — платформа сама заботится об упаковке и распаковке.
При создании хранилища можно указать степень сжатия данных: Новый ХранилищеЗначения(Значение, Новый СжатиеДанных(9)). По умолчанию сжатие не применяется. Сжатие уменьшает объём занимаемого места, но замедляет запись и чтение, поэтому его разумно включать для крупных текстовых или табличных данных, а для уже сжатых форматов (JPEG, ZIP) оно бесполезно.
Где применяется хранилище значений в типовых конфигурациях
Самый массовый сценарий — хранение файлов и изображений. В типовых конфигурациях (Управление торговлей, ERP, Бухгалтерия) картинки номенклатуры и присоединённые файлы исторически хранились именно в реквизитах типа «ХранилищеЗначения», а в современных версиях — через подсистему «Работа с файлами», которая внутри также опирается на хранилище либо на тома на диске.
Вторая типичная область — сохранение настроек. Варианты отчётов, настройки обмена, параметры обработок, пользовательские отборы — всё это удобно упаковывать в структуру или таблицу значений и класть в хранилище одного реквизита, вместо того чтобы создавать десятки отдельных полей.
Третий сценарий — служебные данные обменов: очереди сообщений, тела запросов к веб-сервисам, ответы внешних систем. Здесь хранилище ценно тем, что сохраняет данные «как есть», без необходимости описывать их структуру метаданными.
Как записать значение в хранилище
Запись выполняется в три шага: создаётся объект хранилища конструктором Новый ХранилищеЗначения(), результат присваивается реквизиту объекта, объект записывается. Ниже пример сохранения картинки в реквизит справочника.
&НаСервере
Процедура СохранитьКартинку(СсылкаНаТовар, ПутьКФайлу)
ДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу);
Объект = СсылкаНаТовар.ПолучитьОбъект();
Объект.ДанныеКартинки = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
Объект.Записать();
КонецПроцедурыАналогично сохраняются и «нематериальные» данные — например, структура с настройками обработки. Перед записью убедитесь, что сохраняемое значение вообще сериализуется: объекты типа «COMОбъект», открытые соединения или запросы с активным результатом сохранить нельзя.
- 1Определите, какие данные нужно сохранить, и убедитесь, что тип сериализуем (таблица, структура, двоичные данные — да; COM-объекты — нет).
- 2Добавьте в объект метаданных реквизит с типом «ХранилищеЗначения» или найдите существующий.
- 3В коде создайте хранилище: Новый ХранилищеЗначения(Значение).
- 4Присвойте хранилище реквизиту объекта и вызовите Записать().
- 5Проверьте результат: перечитайте объект и вызовите Получить() — значение должно совпасть с исходным.
Как прочитать значение из хранилища
Чтение выполняется методом Получить(), который возвращает исходное значение в исходном типе. Если хранилище пустое (значение не присваивалось), метод вернёт Неопределено — это нормальная ситуация, которую нужно обрабатывать в коде, иначе получите ошибку при обращении к свойствам результата.
&НаСервере
Функция ПрочитатьНастройки(СсылкаНаЭлемент)
Запрос = Новый Запрос("ВЫБРАТЬ ХранилищеНастроек ИЗ Справочник.НастройкиОбмена ГДЕ Ссылка = &Ссылка");
Запрос.УстановитьПараметр("Ссылка", СсылкаНаЭлемент);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Настройки = Выборка.ХранилищеНастроек.Получить();
Если Настройки = Неопределено Тогда
Возврат Новый Структура;
КонецЕсли;
Возврат Настройки;
КонецЕсли;
КонецФункцииОбратите внимание: хранилище можно получить и через запрос — поле выбирается как обычный реквизит, а распаковка происходит уже в коде. Это удобно, когда не нужна вся объектная обвязка (табличные части, подписки на события), и чтение выполняется быстрее, чем через ПолучитьОбъект().
Ограничения и подводные камни
Главное ограничение уже упоминалось: по содержимому хранилища нельзя искать. Если бизнес-логика требует отбирать записи по какому-то признаку, этот признак должен лежать в отдельном обычном реквизите. Попытка «спрятать» в хранилище данные, по которым потом понадобится отбор, — одна из самых дорогих ошибок проектирования: придётся перечитывать и распаковывать все записи.
Второй момент — размер базы. Крупные файлы, складываемые в хранилище, раздувают информационную базу, замедляют резервное копирование, репликацию и обмены. Для файлов большого объёма в клиент-серверном режиме часто выгоднее хранить их в томах на диске, а в базе держать только ссылки.
Третий нюанс — версионирование формата. Если вы сохранили структуру с одним набором полей, а через год код ожидает другой набор, старые записи начнут давать ошибки или неполные данные. Решение — при чтении проверять наличие нужных свойств и задавать значения по умолчанию.
Хранилище значений против альтернатив
Не каждая задача требует именно хранилища. Если данные представляют собой набор однотипных записей с поиском и отборами — правильнее табличная часть или регистр сведений. Если это один длинный текст — подойдёт реквизит типа «Строка» неограниченной длины, который хотя бы можно частично увидеть в запросе и списках.
Сравнение вариантов
Любые данные в одном поле; нет отборов и поиска; подходит для файлов, настроек, служебных структур.
Только текст; можно выводить в списки и частично искать через ПОДОБНО; не подходит для двоичных данных.
Структурированные данные с полноценными отборами и соединениями; требуют описания структуры метаданными.
Файлы лежат на диске, в базе только ссылки; база не растёт, но нужна настройка и резервное копирование томов.
Как проверить и отладить работу с хранилищем
Простейший способ убедиться, что запись прошла, — открыть элемент в режиме предприятия или выполнить проверочный код: прочитать реквизит, вызвать Получить() и сравнить тип и содержимое с ожидаемым. Для двоичных данных полезно проверить размер: Данные.Размер() — нулевой размер указывает на проблему на этапе получения файла, а не хранилища.
Для массовой диагностики (например, поиск записей с «битыми» данными) используйте запрос с выборкой поля-хранилища и распаковку в цикле с обработкой исключений через Попытка...Исключение. Записи, где Получить() падает или возвращает неожиданный тип, — кандидаты на исправление.
Как проверить результат
Совпадает с типом, который сохраняли (ДвоичныеДанные, Структура и т.д.)
Неопределено при гарантированной записи или другой тип
Сопоставим с размером исходного файла
Ноль или резко меньше ожидаемого
Данные идентичны исходным
Расхождения — значит, изменяли копию без повторной записи
При обмене данными между базами учитывайте, что хранилище переносится как двоичный блок целиком. Если в плане обмена состав данных меняется, старые хранилища в уже выгруженных пакетах останутся в прежнем формате — закладывайте обратную совместимость при чтении.
Часто задаваемые вопросы
Можно ли в запросе отобрать записи по содержимому хранилища значений?
Нет. Содержимое хранилища недоступно для отборов, сортировок и группировок в запросах — платформа работает с ним как с непрозрачным бинарным полем. Если нужен поиск по признаку, вынесите его в отдельный реквизит примитивного типа и заполняйте одновременно с записью хранилища.
Что вернёт метод Получить(), если значение не записывали?
Метод вернёт Неопределено. Это штатная ситуация, а не ошибка: всегда проверяйте результат перед обращением к его свойствам и методам, иначе получите исключение «Поле объекта не обнаружено» или подобную ошибку.
Какие типы данных нельзя поместить в хранилище значений?
Нельзя сериализовать объекты, привязанные к текущему сеансу или внешним ресурсам: COM-объекты, OLE, открытые соединения, результаты активных запросов. Сериализуемы примитивные типы, структуры, массивы, таблицы и деревья значений, двоичные данные, картинки и большинство универсальных коллекций.
Влияет ли хранилище значений на размер базы?
Да, напрямую: всё содержимое физически записывается в таблицу базы данных. Массовое хранение файлов в хранилище увеличивает базу, время резервного копирования и объём трафика при обменах. Для больших файлов рассмотрите тома хранения на диске.
Чем хранилище значений отличается от реквизита типа «Строка» неограниченной длины?
Строка хранит только текст, но её можно выводить в списки, частично искать через ПОДОБНО и читать без распаковки. Хранилище принимает любые типы, включая двоичные данные и коллекции, но полностью непрозрачно для запросов. Выбор зависит от того, нужен ли поиск по содержимому.
Как перенести данные из хранилища при изменении структуры сохраняемых данных?
Напишите сервисную обработку: выберите записи запросом, распакуйте каждое хранилище методом Получить(), преобразуйте старую структуру в новую (добавьте недостающие поля, переименуйте), создайте новое хранилище и запишите объект. Выполняйте на копии базы и обрабатывайте исключения для повреждённых записей.
Что важно запомнить
Хранилище значений — универсальный контейнер для любых сериализуемых данных в базе 1С. Записывайте через Новый ХранилищеЗначения(), читайте методом Получить() с проверкой на Неопределено. Помните о главном ограничении — невозможности отборов в запросах — и о влиянии крупных данных на размер базы.