XML-сериализация для деплоймента начальных данных в Caché. Часть IИсточник: habrahabr intersystems
Думаю, не преувеличением будет сказать, что почти каждый разработчик информационной системы сталкивается с задачей формирования начальных данных при внедрении. У Caché-разработчиков есть несколько стандартных подходов к инициализации начальных данных:
Для инициализации статических данных, небольших справочников или каких-либо конфигурационных данных системы, есть еще один способ, о котором пойдет речь в статье. В Caché есть возможность включать в код класса блоки XML данных - XDATA блоки. Обычно эти данные используются для хранения вместе с классом данных о формах Zen-страниц и Zen-отчетов. В этих блоках можно также прекрасно хранить начальные данные для персистентных классов. Десериализация из XDataРассмотрим пример простого хранимого класса с одним свойством. Это будет класс регионов с названиями - типичный пример справочника. Код этого класса в Caché выглядит следующим образом: Class map.Region Extends %Persistent { /// Название Property Name As %String; } Добавим в класс начальные данные в XML-виде в блок XData: XData populate { <xml> <item> <Name>Красноярский край</Name> </item> <item> <Name>Свердловская область</Name> </item> <item> <Name>Хабаровский край</Name> </item> </xml> } Для того, чтобы загрузить эти данные из класса, добавляем метод класса Populate. Кроме того, для работы с XML-данными необходимо класс сделать XML-enabled - добавляем в список наследования также класс %XML.Adaptor. В итоге преобразованный код класса выглядит следующим образом: Class map.Region Extends (%Persistent, %XML.Adaptor) { /// Название Property Name As %String; ClassMethod Populate() As %Status { #dim sc As %Status = $$$OK // очистка существующих данных класса s sc=..%DeleteExtent() if $$$ISERR(sc) quit sc // загрузка xml-данных блока XData из библиотеки скомпилированных классов #dim stream As %Stream.Object = ##class(%Dictionary.CompiledXData).%OpenId(..%ClassName(1) _ "//" _ "populate").Data // создание инстанса XML ридера #dim reader As %XML.Reader = ##class(%XML.Reader).%New() // открытие в ридере поток xml set sc = reader.OpenStream(stream, "literal") if $$$ISERR(sc) quit sc // указание ридеру в каком элементе искать данные класса do reader.Correlate("item", ..%ClassName(1)) #dim obj as %Persistent // загрука в цикле элементов класса while reader.Next(.obj, .sc) { if $$$ISERR(sc) quit // сохранение прочитанного из xml инстанс в базу данных set sc = obj.%Save() if $$$ISERR(sc) quit set obj = "" } quit sc } XData populate { <xml> <item> <Name>Красноярский край</Name> </item> <item> <Name>Свердловская область</Name> </item> <item> <Name>Хабаровский край</Name> </item> </xml> } } Как видно из кода, всю работу выполняет класс %XML.Reader, который позволяет вычитывать "объектные" данные из XML. Для загрузки данных в класс при деплойменте достаточно выполнить метод класса Populate: w ##class(map.Region).Populate() Убедимся, что инстансы класса действительно созданы. Выполним запрос в SQL-шелле терминала:
Заполнение XDataОчевидно, что блок XData можно заполнить "вручную". И это удобно, если объектов в классе мало. Но если их много, это может сделать программа. Воспользуемся классом %XML.Writer, который позволяет сериализовать в XML инстансы класса. Для этого добавляем в класс метод сериализации: ClassMethod SerializeToFile(file as %String) as %Status { #dim sc as %Status #dim wr as %XML.Writer set wr=##class(%XML.Writer).%New() // установка файла, как устройство вывода set sc=wr.OutputToFile(file) if $$$ISERR(sc) quit sc // открытие корневого тега set sc=wr.RootElement("xml") if $$$ISERR(sc) quit sc #dim rset as %ResultSet // выполнение запроса Extent, содержащего все объекты класса set rset = ##class(%ResultSet).%New("map.Region:Extent") do rset.Execute() #dim obj while (rset.Next()) { set obj=##class(map.Region).%OpenId(rset.Data("ID")) // сериализация объекта set sc=wr.Object(obj) if $$$ISERR(sc) quit } if $$$ISERR(sc) quit sc // закрытие корневого тега set sc=wr.EndRootElement() quit sc } Метод выводит в файл все объекты класса, сериализованные в XML. Выполним метод в терминале:
А затем откроем файл любым редактором/вьювером: Полученный XML уже нетрудно вставить в блок XData. ИтогоАналогичным образом можно снабдить подобными методами все классы, требующие ввод начальных данных при деплойменте. В итоге, с помощью приведенной техники начальные данные хранятся вместе с классом, для которого они нужны, в технологичном XML-формате. Клиенту можно поставлять только код классов, а генерацию начальных данных выполнять с помощью метода Populate. А что делать, если классы связаны друг с другом? Этот и другие сценарии рассмотрим во второй части статьи. Продолжение следует… |