XML-сериализация для развёртывания начальных данных в Caché. Часть I

    image
    Думаю, не преувеличением будет сказать, что почти каждый разработчик информационной системы сталкивается с задачей формирования начальных данных при внедрении.
    У 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
      
    // очистка существующих данных класса
      
    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:
    ##class(map.Region).Populate()

    Убедимся, что инстансы класса действительно созданы. Выполним запрос в SQL-шелле терминала:

    XMLDEPLOY>d $System.SQL.Shell()
    SQL Command Line Shell
    ----------------------------------------------------
     
    The command prefix is currently set to: <<nothing>>.
    Enter q to quit, ? for help.
    XMLDEPLOY>>select * from map.Region
    1.      select * from map.Region
     
    ID      Name
    1       Красноярский край
    2       Свердловская область
    3       Хабаровский край
     
    3 Rows(s) Affected
    statement prepare time: 0.9125s, elapsed execute time: 0.0687s.
    ---------------------------------------------------------------------------
    XMLDEPLOY>>quit
     
    XMLDEPLOY>
    


    Заполнение XData

    Очевидно, что блок XData можно заполнить “вручную”. И это удобно, если объектов в классе мало. Но если их много, это может сделать программа.
    Воспользуемся классом %XML.Writer, который позволяет сериализовать в XML инстансы класса.
    Для этого добавляем в класс метод сериализации:

    ClassMethod SerializeToFile(file as %Stringas %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.
    Выполним метод в терминале:
    XMLDEPLOY>D $System.OBJ.DisplayError(##class(map.Region).SerializeToFile("C:\cache\region.xml"))
    
    XMLDEPLOY>
    

    А затем откроем файл любым редактором/вьювером:
    image
    Полученный XML уже нетрудно вставить в блок XData.

    Итого

    Аналогичным образом можно снабдить подобными методами все классы, требующие ввод начальных данных при деплойменте. В итоге, с помощью приведенной техники начальные данные хранятся вместе с классом, для которого они нужны, в технологичном XML-формате. Клиенту можно поставлять только код классов, а генерацию начальных данных выполнять с помощью метода Populate. А что делать, если классы связаны друг с другом? Этот и другие сценарии рассмотрим во второй части статьи. Продолжение следует…
    InterSystems
    87,00
    Вендор: СУБД Caché, OLAP DeepSee, шина Ensemble
    Поделиться публикацией

    Комментарии 4

      0
      Про продолжение:
      Особенно интересно про ваш вариант решения следующих вопросов:
      Версионность данных (Ленинградскую область переименуют Санкт-Петербургскую). Как эти данные загрузятся из XML?
      Что если в системе уже загружены данные (записи) и нужно какую-то часть этих данных удалить?
      Классы связаны друг с другом, да ещё и с циклами, Сотрудники-Отделы (а у отдела есть начальник — сотрудник)
        0
        Спасибо за интересный вопрос.
        Про версионность — не совсем понятно. Когда переименуют? Уже в ходе эксплуатации системы или при начальном развертывании? Конкретизируйте.
        Про удаление существующих данных — на мой взгляд это не очень относится к теме XML-сериализации, но возможно добавить в класс метод, выполняющий анализ существующих данных, удаление, конвертацию данных и проч. и вызывать этот метод перед десерализацией из ресурса.
        Со связями интереснее — но ничего фантастического: сначала создаем инстансы во всех классах, потом устанавливаем связи между ними. Об этом подробнее во второй части.
          0
          Не знаю как насчет заполнения начальных данных, но по-моему это отличный способ вставлять в код форматированный текст, например тексты запросов к БД: pastebin.com/Jp2DcTur Насколько я понял, в COS нет реализации многострочных строк.
            0
            Красиво. Как замена кавычек и конкатенации — прекрасно, спасибо!

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое