В разработке на платформе 1С:Предприятие 8 одной из самых частых задач является работа с данными, полученными в результате выполнения запроса. Часто разработчики сталкиваются с необходимостью не просто прочитать значения полей из выборки, а получить полноценный объект метаданных (например, ДокументОбъект, СправочникОбъект, БизнесПроцессОбъект) для его последующего изменения, записи или вызова методов.
В данной статье подробно разберем, как правильно получить объект по ссылке, полученной в запросе, какие существуют подходы, нюансы производительности и типичные ошибки.
Теоретические основы: Ссылка vs Объект
Прежде чем переходить к коду, важно четко разграничивать два понятия:
- Ссылка (Ref) — это легковесный идентификатор объекта в базе данных (аналог первичного ключа). Она содержит только уникальный идентификатор (UUID) и указатель на метаданные (вид объекта). Ссылки возвращаются запросом по полям типа «Ссылка». Они не содержат данных объекта (реквизитов, табличных частей). Чтение ссылок очень быстрое.
- Объект (Object), это полноценный управляемый объект платформы (
ДокументОбъект.Имя,СправочникОбъект.Имяи т;д.). Он содержит все реквизиты, табличные части, наборы записей и предоставляет методыЗаписать,Удалить,Провестии др. Получение объекта — это дорогая операция (обращение к БД за полным набором данных).
Ключевой момент: Запрос (Запрос) никогда не возвращает объекты. Он возвращает только ссылки (или примитивные типы: строки, числа, даты, булево). Преобразование ссылки в объект происходит после выполнения запроса, в коде 1С.
Основной метод: ПолучитьОбъект
У любого значения типа Ссылка (например, СправочникСсылка.Номенклатура, ДокументСсылка.РеализацияТоваровУслуг) есть метод ПолучитьОбъект.
Синтаксис:
Объект = Ссылка.ПолучитьОбъект; Пример базового использования в цикле выборки:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг
|ГДЕ
| Проведен = ИСТИНА
| И Дата МЕЖДУ &НачПериода И &КонПериода";
Запрос.УстановитьПараметр("НачПериода", НачалоДня(ТекущаяДата));
Запрос.УстановитьПараметр("КонПериода", КонецДня(ТекущаяДата));
Результат = Запрос.Выполнить;
Выборка = Результат.Выбрать;
Пока Выборка.Следующий Цикл
// Выборка.Ссылка имеет тип ДокументСсылка.РеализацияТоваровУслуг
ДокументОбъект = Выборка.Ссылка.ПолучитьОбъект;
// Теперь можем работать с полным объектом
ДокументОбъект.Комментарий = "Обработано скриптом " + ТекущаяДата;
// Важно: запись объекта — отдельная операция
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
КонецЦикла; Сценарии использования и лучшие практики
Чтение данных: Не используйте ПолучитьОбъект без необходимости
Это самая частая ошибка новичков. Если вам нужно только прочитать несколько реквизитов (Наименование, Сумма, Контрагент), не вызывайте ПолучитьОбъект. Укажите нужные поля прямо в тексте запроса.
Плохо (медленно, нагружает БД и память):
Запрос.Текст = "ВЫБРАТЬ Ссылка ИЗ Справочник.Номенклатура";
Выборка = Запрос.Выполнить.Выбрать;
Пока Выборка.Следующий Цикл
Объект = Выборка.Ссылка.ПолучитьОбъект; // Дорогое чтение ВСЕХ полей
Сообщить(Объект.Наименование);
Сообщить(Объект.Артикул);
КонецЦикла; Хорошо (быстро, оптимально):
Запрос.Текст = "ВЫБРАТЬ Наименование, Артикул ИЗ Справочник.Номенклатура";
Выборка = Запрос.Выполнить.Выбрать;
Пока Выборка.Следующий Цикл
// Выборка.Наименование, Выборка.Артикул доступны сразу
Сообщить(Выборка.Наименование);
Сообщить(Выборка.Артикул);
КонецЦикла; Массовое получение объектов: Метод Выгрузить
Если вам нужно получить массив объектов для дальнейшей пакетной обработки (например, для передачи в ЗаписатьПакетом или массового проведения), удобнее использовать метод Выгрузить результата запроса в массив или таблицу значений, а затем пройтись по нему.
Результат = Запрос.Выполнить;
// Выгружаем колонку "Ссылка" в массив
МассивСсылок = Результат.ВыгрузитьКолонку("Ссылка");
МассивОбъектов = Новый Массив;
Для Каждого Ссылка Из МассивСсылок Цикл
МассивОбъектов.Добавить(Ссылка.ПолучитьОбъект);
КонецЦикла;
// Пример пакетной записи (если объекты изменялись)
// Документы.РеализацияТоваровУслуг.ЗаписатьПакетом(МассивОбъектов); Работа с табличными частями
При получении объекта через ПолучитьОбъект загружаются все табличные части документа/справочника. Если объект большой (много строк ТЧ), это существенно увеличивает потребление памяти и время выполнения.
Если нужно изменить только шапку документа — это нормально. Если нужно проанализировать строки ТЧ — иногда эффективнее выбрать данные ТЧ отдельным запросом (через Документ.Товары), а объект получать только для записи изменений.
Особенности в подписках на события (Event Subscriptions)
Контекст, упомянутый в ваших исходных данных (подписка на событие ОбработкаПолученияФормы), требует особого внимания. В подписках на события (особенно ПередЗаписью, ПриЗаписи) параметр Источник часто уже является Объектом, а не ссылкой.
- Источник в подписке на событие менеджера (ДокументМенеджер) — это Менеджер.
- Источник в подписке на событие объекта (ДокументОбъект) — это сам Объект.
- В параметрах формы (
Параметры.Ключ) передается именно Ссылка.
Правильный паттерн для подписки на ОбработкаПолученияФормы (Управляемые формы):
Процедура ОбработкаПолученияФормы(Источник, ВидФормы, Параметры, ВыбраннаяФорма, ДополнительнаяИнформация, СтандартнаяОбработка) Экспорт
// 1; Проверяем, что это нужный нам вид формы и источник
Если ВидФормы = "ФормаОбъекта" И ТипЗнч(Источник) = Тип("ДокументМенеджер.РеализацияТоваровУслуг") Тогда
// 2. Пытаемся получить ссылку из параметров формы (Ключ)
Если Параметры.Свойство("Ключ") И Параметры.Ключ <> Неопределено Тогда
СсылкаНаДокумент = Параметры.Ключ; // Это Ссылка
// 3. Получаем объект, ЕСЛИ это необходимо для логики формы
// ВНИМАНИЕ: В событии ПолученияФормы объект еще не создан на клиенте/сервере в привычном смысле,
// лучше менять форму через ВыбраннаяФорма или Параметры, а не читать объект БД здесь.
// Пример: если нужно запретить редактирование реквизита на форме
// Правильнее работать с элементами формы ВыбраннаяФорма:
ЭлементОтветственный = ВыбраннаяФорма.Элементы.Найти("Ответственный");
Если ЭлементОтветственный <> Неопределено Тогда
ЭлементОтветственный.Доступность = Ложь;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры Важно: В обработчиках форм (
ОбработкаПолученияФормы) старайтесь не вызыватьПолучитьОбъектбез крайней необходимости. Форма сама загрузит данные. ИспользуйтеВыбраннаяФормадля управления UI (доступность, видимость, условное оформление).
Типичные ошибки и подводные камни
Ошибка 1: Попытка вызвать ПолучитьОбъект внутри текста запроса
Неверно:
Запрос.Текст = "ВЫБРАТЬ Ссылка.ПолучитьОбъект КАК Объект ИЗ Документ.Продажа"; // ОШИБКА КОМПИЛЯЦИИ ЗАПРОСА Язык запросов 1С не знает методов платформы ПолучитьОбъект. Запрос работает на уровне СУБД.
Ошибка 2: Получение объекта для ссылки, которой нет в БД (Удаленная/Пустая)
Ссылка = Документы.РеализацияТоваровУслуг.ПустаяСсылка;
Объект = Ссылка.ПолучитьОбъект; // Вернет НОВЫЙ пустой объект (аналог СоздатьДокумент), не ошибку!
// Но если ссылка указывала на удаленный объект:
Объект = УдаленнаяСсылка.ПолучитьОбъект; // Исключение: "Объект не найден" Всегда проверяйте Ссылка.Пустая или используйте Попытка ... Исключение ... КонецПопытки при работе с внешними данными.
Ошибка 3: Модификация объекта «Только чтение»
Объекты, полученные через ВыборкаСсылки.ПолучитьОбъект в отчетах или обработках (где нет блокировки), могут быть в режиме «Только чтение» при попытке записи, если не установлены соответствующие права или режим блокировки.
Для гарантированной записи используйте:
Объект = Ссылка.ПолучитьОбъект(РежимБлокировкиОбъекта.Запись); // Явный запрос блокировки на запись
Объект.Реквизит = Значение;
Объект.Записать; Ошибка 4: Бесконечные циклы при записи в подписке «ПередЗаписью»
// ОПАСНО!
Процедура ПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения)
// Источник ― это уже ОБЪЕКТ (ДокументОбъект)
// Если здесь вызвать Источник.ПолучитьОбъект ― получим копию или ошибку рекурсии
// Если здесь вызвать Источник.Записать — будет рекурсивный вызов ПередЗаписью -> StackOverflow
// Правильно: меняем реквизиты Источника напрямую
Источник.Комментарий = "Изменено в подписке";
КонецПроцедуры Производительность: Запрос vs ПолучитьОбъект
| Подход | Скорость | Память | Когда применять |
|---|---|---|---|
| Поля в SELECT (Запрос.Выполнить.Выбрать) | Очень высокая | Минимальная | Отчеты, списки, чтение данных, фильтрация, расчеты. |
| Выборка.Ссылка.ПолучитьОбъект в цикле | Низкая (N запросов к БД) | Высокая (все реквизиты + ТЧ) | Изменение объектов, запись, проведение, сложная логика, требующая методов объекта. |
| Результат.ВыгрузитьКолонку(«Ссылка») + цикл ПолучитьОбъект | Низкая | Высокая | Подготовка массива для ЗаписатьПакетом / ПровестиПакетом. |
Золотое правило: Сначала отберите максимум данных запросом (поля, агрегаты, соединения). Получайте объекты (ПолучитьОбъект) только для тех записей, которые реально требуют изменения или вызова методов объекта.
Продвинутые техники
Временные таблицы и пакетная обработка
Для обработки тысяч документов неэффективно вызывать ПолучитьОбъект в цикле по одному. Используйте временные таблицы менеджера записи:
// 1. Запрос отбирает ссылки на документы для обработки
Запрос.Текст = "ВЫБРАТЬ Ссылка ИЗ Документ.ПеремещениеТоваров ГДЕ Проведен = ЛОЖЬ";
МассивСсылок = Запрос.Выполнить.ВыгрузитьКолонку("Ссылка");
// 2. Менеджер записи позволяет работать с набором объектов эффективно
НаборЗаписей = Документы.ПеремещениеТоваров.СоздатьНаборЗаписей;
НаборЗаписей.Отбор.Ссылка.Установить(МассивСсылок); // Загружаем объекты пачкой (оптимизированно)
НаборЗаписей.Прочитать; // Один запрос к БД на чтение всех объектов
Для Каждого ДокументОбъект Из НаборЗаписей Цикл
ДокументОбъект.Проведен = Истина;
// Логика заполнения...
КонецЦикла;
НаборЗаписей.Записать; // Пакетная запись в одну транзакцию ПолучитьОбъект с параметром РежимБлокировкиОбъекта
Позволяет избежать ошибок «Объект изменен другим пользователем» (конфликт блокировок) при параллельной работе.
Попытка
// Пытаемся получить объект с блокировкой на запись (помещаем в очередь ожидания)
ДокументОбъект = Ссылка.ПолучитьОбъект(РежимБлокировкиОбъекта.Запись, Истина); // Истина = Ждать разблокировки
ДокументОбъект.Склад = НовыйСклад;
ДокументОбъект.Записать;
Исключение
Сообщить("Не удалось заблокировать документ для редактирования: " + ОписаниеОшибки);
КонецПопытки; Резюме (Checklist)
- Запрос возвращает Ссылки. Объектов в результате запроса не существует.
- Используйте
Ссылка.ПолучитьОбъекттолько в коде 1С (модуле) послеВыборка.Следующий. - Для чтения данных — выбирайте поля в запросе. Не читайте объект ради одного реквизита.
- Для массовых изменений, используйте
НаборЗаписейилиЗаписатьПакетом. Это в разы быстрее цикла сПолучитьОбъект. - В подписках на события проверяйте тип
Источник: часто это уже Объект, и повторное получение не нужно. - В формах управляйте UI через
ВыбраннаяФорма.Элементы, а не через чтение объекта БД вОбработкаПолученияФормы. - Обрабатывайте исключения при получении объекта (объект мог быть удален другим пользователем).
Грамотное разделение этапов «Чтение данных (Запрос)» и «Работа с бизнес-логикой (Объект)» — залог быстрых, надежных и масштабируемых решений на платформе 1С.