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

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

В практике разработки на платформе 1С:Предприятие часто возникает задача сохранения произвольных данных, которые не вписываются в стандартные типы реквизитов (число, строка, дата) — для этого подойдёт редактор полей типа ХранилищеЗначения в регистрах. Это могут быть многострочные структуры, массивы, таблицы значений или двоичные данные (картинки, файлы). Для этих целей используется специальный тип — ХранилищеЗначения. Однако начинающие разработчики часто путают механизмы работы с постоянным хранилищем в базе данных и временным хранилищем в оперативной памяти. Рассмотрим подробно, как избежать типичных ошибок и правильно организовать работу с этим объектом.

Разбор типичной ошибки: Временное хранилище vs ХранилищеЗначения

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

Проанализируем некорректный код:


// ОШИБОЧНЫЙ ПОДХОД
РеквизитОбъект = РеквизитФормыВЗначение("Объект");
РеквизитОбъект.Платеж = ПоместитьВоВременноеХранилище(Платеж); // Здесь записывается строка-адрес

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

Правильная запись данных в реквизит

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


&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    // Предположим, что "Платеж" — это структура, сформированная на форме
    СтруктураДанных = Новый Структура;
    СтруктураДанных.Вставить("Дата", Объект.ДатаПлатежа);
    СтруктураДанных.Вставить("Сумма", Объект.СуммаПлатежа);
    
    // Записываем структуру в реквизит объекта
    ТекущийОбъект.Платеж = Новый ХранилищеЗначения(СтруктураДанных);
КонецПроцедуры

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

Чтение данных из ХранилищаЗначения

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

Рекомендуется использовать процедуру ПриЧтенииНаСервере:


&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
    // Проверяем, заполнено ли хранилище, чтобы не тратить ресурсы
    Если ЗначениеЗаполнено(ТекущийОбъект.Платеж) Тогда
        // Извлекаем структуру из хранилища
        ДанныеПлатежа = ТекущийОбъект.Платеж.Получить();
        
        // Если данные успешно получены, заполняем реквизиты формы
        Если ТипЗнч(ДанныеПлатежа) = Тип("Структура") Тогда
            Объект.ДатаПлатежа = ДанныеПлатежа.Дата;
            Объект.СуммаПлатежа = ДанныеПлатежа.Сумма;
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Важные технические нюансы работы

Рассмотрим более глубокие аспекты, которые помогут сделать работу с базой данных эффективнее:

  1. Сжатие данных: По умолчанию 1С сжимает данные в хранилище — есть готовое управление изображениями и массовое сжатие в 1С. Если вы сохраняете очень большие текстовые данные или сложные структуры, вы можете управлять степенью сжатия через второй параметр конструктора: Новый ХранилищеЗначения(Данные, Новый СжатиеДанных(9)). Однако для уже сжатых файлов (JPG, ZIP) лучше устанавливать уровень 0, чтобы не нагружать процессор бесполезными операциями.
  2. Ограничения запросов: Помните, что вы не можете использовать реквизит типа ХранилищеЗначения в условиях ГДЕ, СГРУППИРОВАТЬ ПО или УПОРЯДОЧИТЬ ПО в языке запросов. Платформа просто "не видит" содержимое этого поля для нужд поиска и сортировки.
  3. Производительность (Ленивая загрузка): Платформа 1С оптимизирована: при получении объекта из базы (например, через ПолучитьОбъект()) само содержимое ХранилищаЗначения не загружается в оперативную память сразу. Чтение бинарных данных из SQL-таблицы происходит только в момент вызова метода .Получить() — для такой диагностики есть анализ физического объема таблиц SQL. Это называется "отложенным чтением" и позволяет быстро перебирать тысячи объектов в цикле, если вы не обращаетесь к тяжелым данным внутри них.
  4. Сериализация: В хранилище можно поместить только те объекты, которые поддерживают сериализацию. Структуры, Массивы, Таблицы значений — подходят. Объекты типа Запрос или ComObject — нет. При попытке сохранить несериализуемый объект вы получите ошибку или пустое значение при чтении.
  5. Очистка реквизита: Чтобы полностью удалить данные из реквизита и освободить место в базе данных, присвойте ему значение Неопределено: — также задачу оптимизации решает перенос присоединенных файлов из базы на диск.
    
    Объект.Платеж = Неопределено;
    Объект.Записать();
    

Особенности работы на Веб-клиенте

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

  1. На сервере извлеките данные методом .Получить().
  2. Поместите их во временное хранилище функцией ПоместитьВоВременноеХранилище(Данные).
  3. Верните полученный адрес-строку на клиент.
  4. На клиенте используйте этот адрес (например, для ПолучитьФайл()).

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

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

← На главную