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