Разработка Java-приложений для работы с данными DB2 XML

Новая версия DB2® Viper предлагает во многом обновленную поддержку хранения, управления и поиска XML-данных. В этой статье вы познакомитесь с основами создания Java-™ приложений для работы с поддерживаемыми в новой версии данными XML. Мы рассмотрим вставку, запрос, обновление и удаление XML-данных, создание хранимых процедур для обращения к данным XML и многое другое.

К. М. Саракко, старший инженер-программист, IBM

К.M.Саракко (C.M.Saracco) работает в Silicon Valley Laboratory IBM в отделе DB2 XML. Она занимается управлением базами данных, XML, разработкой Web-приложений и связанными с этим темами.



04.05.2006

Создание Java-приложений, которые обращаются к данным XML, хранящимся в системе DB2 Viper, не очень отличается от написания Java-приложений, которые работают с реляционными данными. Действительно, если вы хорошо знаете интерфейс JDBC, то многое из того, что необходимо для создания первого приложения DB2 XML, вам уже известно.

В этой статье мы подробно рассмотрим несколько распространенных сценариев программирования, среди которых - вставка XML-данных, поиск XML-данных и других данных, обновление и удаление данных XML, а также создание хранимых процедур, которые обращаются к XML-данным. Но сначала давайте ознакомимся с некоторыми базовыми рекомендациями по разработке любых типов приложений для базы данных DB2.

Следуйте "передовому опыту" стандартного программирования

Хотя собственная поддержка XML является новой функцией в DB2, методика программирования хороших приложений для баз данных не изменилась. Перед тем, как углубиться в подробности технологии XML в DB2, запомните следующие общие принципы:

  • Запрашивайте только то, что необходимо: не извлекайте все содержимое таблицы или нескольких документов XML, если необходима только часть информации; этим вы добьетесь только повышения затрат ресурсов и снижения производительности рабочего цикла;
  • Избегайте дублирования работы сервера базы данных: Инструктируйте DB2 о необходимости фильтрации и обработки данных в соответствии с вашими потребностями, а не перекладывайте эту работу на приложение. Например, если DB2 возвратит результаты в определенном порядке, вам не придется сортировать данные. Аналогично, если вы составите запрос таким образом, чтобы DB2 обеспечила возврат только неповторяющихся результатов, то вам не придется проводить повторную проверку на наличие дублирующих записей. Централизованную обработку данных лучше выполнит сервер базы данных, а не ваше приложение;
  • Обеспечьте легкое обслуживание кода: включите в программу комментарии или Javadoc, особенно в том случае, если приложение содержит сложные запросы;
  • Тщательно продумайте область действия транзакций: по умолчанию JDBC рассматривает любой запрос как независимую транзакцию. Выясните, подходит ли это в вашем случае и подумайте о том, как область действия (и уровень изоляции), который вы определили для транзакций, может влиять на все требования параллелизма;
  • Минимизируйте трафик в сетевой среде: если вам удастся избежать ненужной передачи данных между приложением и DB2, вы добьетесь лучшей производительности рабочего цикла. Один из способов - запрашивать только те данные, которые необходимы. В зависимости от характера работы, может помочь также вызов хранимых процедур базы данных.

Настройте среду программирования

DB2 не требует дополнительной настройки для разработки или выполнения Java-приложений, работающих с данными XML. В самом деле, можно писать, тестировать и отлаживать программы на Java при помощи интегрированной среды разработки (IDE) по вашему выбору или работая напрямую с поддерживаемым комплектом разработчика Java Developer Kit (JDK) из командной строки. Однако поскольку DB2 Viper поставляется с рабочим местом разработчика Developer Workbench, то примеры в этой статье используют именно эту среду разработки. В этом разделе мы рассмотрим, как настроить Developer Workbench, приведем несколько примеров данных и изучим параметры конфигурации базы данных, которые могут вас заинтересовать.

DB2 Developer Workbench

Среда разработки DB2 Developer Workbench построена на платформе Eclipse 3.1, проекта с открытым исходным кодом, который доступен для бесплатной загрузки. Чтобы скомпилировать и выполнить любое приложение DB2 XML в этой среде разработки, необходимо создать проект и включить соответствующие библиотеки DB2 (в том числе, библиотеки, поддерживающие совместимый с JDBC 3.0 драйвер DB2) в путь сборки (build path) проекта. Чтобы настроить среду программирования, выполните следующие шаги:

  1. Запустите DB2 Workbench. Например, в меню Start (Пуск) Windows выберите DB2 > IBM DB2 Developer Workbench V9.1 > Developer Workbench;
  2. Создайте новый проект. Для начала поработаем с простым проектом. Переключитесь в режим Java (Window > Open Perspective -> Java), и выберите File > New > Project. При помощи мастера задайте имя для проекта. Для остальных элементов сохраните настройки по умолчанию;
  3. Добавьте библиотеки DB2 в путь сборки (build path) проекта. Выделите ваш проект, нажмите на нем правой кнопкой мыши и выберите команду Properties. Выберите Java Build Path и перейдите на вкладку Libraries. Добавьте соответствующие внешние файлы .jar, такие как db2jcc.jar, db2jcc_javax.jar и db2jcc_license_cu.jar;
  4. Если хотите, создайте пакет для вашего приложения. Выделите проект, нажмите правой кнопкой мыши и выберите New > Package.

Дополнительную информацию о создании проектов и пакетов можно найти в интернет-справке.

Примеры данных

Примеры, приведенные в этой статье, работают с таблицей "clients", которая была создана в статье "Get off to a fast start with DB2 Viper" (Начинаем работать с программой DB2 Viper) (сайт developerWorks, март 2006 г.). Если коротко, эта таблица была описана как:

Листинг 1. Пример листинга кода при максимальной ширине
create table clients(
  id    		int primary key not null, 
  name  		varchar(50), 
  status 		varchar(10), 
  contactinfo 	xml
)

На рисунке 1 приведен пример файла XML, который через некоторое время будет вставлен в столбец "contactinfo" этой таблицы.

Рисунок 1. Пример файла XML для вставки в таблицу "clients"
Рисунок 1

Параметры конфигурации базы данных

Примеры в данном практическом руководстве простые, они работают с малыми объемами данных XML, поэтому для работы с ними не нужно изменять параметры конфигурации базы данных, установленные по умолчанию. Однако значения по умолчанию могут не подойти для некоторых рабочих сред. В частности, может оказаться необходимым увеличить значения параметров log size, Java heap, query statement heap и application heap. При неправильной настройке этих значений вы можете столкнуться с низкой производительностью рабочего цикла или не сможете вставить большой документ XML в таблицы DB2 из-за недостаточного пространства для ведения журнала.

Параметры конфигурации базы данных DB2 можно просмотреть и изменить через интерфейс DB2 Control Center (выберите Tools > Configuration Assistant) или через обработчик командной строки DB2. Подробную информацию смотрите в руководстве по программе.

Установка соединения с базой данных

Для работы с данными XML необходимо установить соединение с базой данных, в которой хранятся данные. В коде нет ничего особенного - это та же схема, которую вы писали для соединения с любой базой данных DB2.

В листинге 2 показан вспомогательный класс с методами для установления и закрытия соединения с базой данных DB2.

Листинг 2. Вспомогательный класс для установления и сброса соединения с базой данных
public class Conn {
  // for simplicity, I've hard-coded account and URL data.
  private static String user = "user1";
  private static String pwd = "mypassword";
  private static String url = "jdbc:db2:test";

  // this method gets a database connection 	
  public static Connection getConn(){
    Connection conn=null;
		
    //  load the appropriate DB2 driver and 
    //  get a connection to the “test” database  
    try {
       Class.forName("com.ibm.db2.jcc.DB2Driver");
       conn = DriverManager.getConnection(url, user, pwd);
       . . . 	
    }
    catch (Exception e) { e.printStackTrace();	}
    return conn;
		
  }   // end getConn();
	
  // this method closes a database connection 
  public static void closeConn(Connection conn){
    try {
      if(conn == null) { return; }
      conn.close();
    }
    catch (Exception e) { e.printStackTrace(); }
    finally { 
      try { conn.close();  }
      catch (Exception e) { } 
    }
  }  // end closeConn();
}  // end class

Эти методы предназначены для вызова из приложений, которые выполняют общие задачи, такие как вставка и запрос данных XML.

Вставка XML-данных

Поскольку первоначальная спецификация XQuery не включала операции записи в базу данных (например, вставку данных), то для того, чтобы обеспечить программистов возможностью записывать новые данные XML в таблицы, содержащие столбцы XML, DB2 использует обычные предложения SQL INSERT. DB2 может хранить любой правильно сформированный документ XML размером до 2 Гб.

Часто Java-программистам необходимо вставить XML-данные, содержащиеся в файлах, в DB2, хотя можно вставить данные XML из строки символов, двоичных данных (включая большие объекты) и из предложений SQL sub-select. Ниже приводится описание вставки XML-данных из файлов и из простых строк символов. Информацию о других методах вставки данных можно найти в руководстве по DB2 Viper.

DB2 Viper позволяет также вставить XML-документы с проверкой корректности по предварительно зарегистрированным схемам XML или без проверки. Примеры в этой статье охватывают оба подхода.

Вставка данных без проверки корректности

Метод insertFile() в листинге 3 иллюстрирует вставку данных из файла XML в столбец "clients.contactinfo". Этот метод начинается с определения нескольких переменных для дальнейшего использования. Первые три соответствуют столбцам ID, name и status в таблице "clients". Четвертая представляет имя файла XML, который нужно вставить в столбец "contactinfo". Для простоты значения в этом методе жестко запрограммированы; в рабочей среде входные значения получаются разными способами.

После установления соединения с базой данных создадим простую строку для предложения INSERT. Как видите, она не отличается от любого другого предложения INSERT в DB2 и использует маркеры параметров для четырех входных значений столбцов. Предложение INSERT составляется как обычно, поэтому нужно задать четыре маркера параметров. Чтобы задать маркеры для столбца XML, откройте FileInputStream, указав место размещения файла XML. Узнайте также длину файла и используйте эту информацию как входные данные для метода setBinaryStream(). Наконец, выполните это предложение, сделайте проверку ошибок и закройте соединение.

Листинг 3. Вставка XML-данных из файла
public static void insertFile(){
  try {
    // for simplicity, I've defined variables with input data 
    int id = 1885;
    String name = "Amy Liu";
    String status = "Silver";
    String fn = "c:/XMLFiles/Client1885.xml";  // input file

    // get a connection 
    Connection conn = Conn.getConn();

    //   define string that will insert file without validation
    String query = "insert into clients
     (id, name, status, contactinfo) values (?, ?, ? ,?)";

    // prepare the statement
    PreparedStatement insertStmt = conn.prepareStatement(query);
    insertStmt.setInt(1, id);
    insertStmt.setString(2, name);
    insertStmt.setString(3, status);
    File file = new File(fn);
    insertStmt.setBinaryStream(4, new FileInputStream(file), (int)file.length());

    // execute the statement 
    if (insertStmt.executeUpdate() != 1) {
        System.out.println("No record inserted.");
    }
    . . . 
    conn.close();
  }
  catch (Exception e) { . . . }
}

Вставка файла с проверкой корректности

Для вставки XML-файла с проверкой корректности требуется очень небольшая дополнительная работа по программированию. Предположим, что вы создали и зарегистрировали файл ClientInfo.xsd, рассмотренный в статье "Get off to a fast start with DB2 Viper" (Начинаем работать с программой DB2 Viper) (сайт developerWorks, март 2006 г.), вам нужно изменить только одну строчку в коде листинга 3, чтобы проинструктировать DB2 о необходимости вставить XML файл с проверкой корректности. Этот код вызывает описание строки запроса.

Как показано в листинге 4, проверяемое предложение INSERT вызывает функцию XMLValidate перед заданием маркера параметров для данных XML. Эта функция требует также, чтобы вы определили идентификатор схемы XML, используемой для проверки корректности. Здесь упоминается ранее зарегистрированная схема "user1.mysample".

Листинг 4. Вставка XML-данных из файла с проверкой корректности
String query = "INSERT INTO clients (id, name, status contactinfo) " +
    "VALUES (?, ?, ?, xmlvalidate(? according to xmlschema id user1.mysample))";

Если вы введете XNL-файл, содержащий данные, которые являются корректными для определенной схемы, то DB2 вставит запись. В противном случае все предложение не выполняется, и данные из этой записи не вставляются.

Вставка строки символов без проверки корректности

Метод insertString(), показанный в листинге 5, иллюстрирует вставку правильно сформированного документа XML, назначенного переменной типа строки символов, в DB2. Схема не очень отличается от предыдущего примера вставки данных из файла. Вместо метода setBinaryStream() из составленного вами предложения используйте метод setString(). Для облегчения восприятия XML-документ в описании переменной xml в этом примере был жестко запрограммирован.

Примечание: Escape-символы (обратная косая черта) вставляются перед кавычками, являющимися частью документа XML (например, номер версии XML в следующем примере).

Листинг 5. Вставка XML-данных из строки символов
public static void insertString(){
  try {
    // for simplicity, I've defined variables with input data 
    int id = 1885;
    String name = "Amy Liu";
    String status = "Silver";
    String xml = 
      "<?xml version=\"1.0\"?>" + 
      "<Client>" + 
      "<Address> " +
        "<street>54 Moorpark Ave.</street>" +
        "<city>San Jose</city>" +
        "<state>CA</state>" +
        "<zip>95110</zip>" +
      "</Address>" +
      "<phone>" +
        "<work>4084630110</work>" +
        "<home>4081114444</home>" +
        "<cell>4082223333</cell>" +
      "</phone>" +
      "<fax>4087776688</fax>" +
      "<email>sailer555@yahoo.com</email>" +
      "</Client>";

    // get a connection 
    Connection conn = Conn.getConn();

    //   define string that will insert file without validation
    String query = "insert into clients 
    (id, name, status, contactinfo) values (?, ?, ? ,?)";

    // prepare the statement
    PreparedStatement insertStmt = conn.prepareStatement(query);
    insertStmt.setInt(1, id);
    insertStmt.setString(2, name);
    insertStmt.setString(3, status);
    insertStmt.setString(4, xml); 

    // execute the statement 
    if (insertStmt.executeUpdate() != 1) {
        System.out.println("No record inserted.");
    }
    . . . 
    conn.close();
  }
  catch (Exception e) { . . . }
}

Вставка строки символов с проверкой корректности

Как вы можете предположить, проверка корректности документов XML, представленных в виде строки символов, требует небольшой дополнительной работы по программированию. Действительно, требуется изменить всего одну строку кода -- описание переменной query. Вам нужно просто изменить предложение INSERT так, чтобы оно вызывало функцию XMLValidate, так же, как было сделано в листинге 4.

Вот отредактированное предложение:

Листинг 6. Вставка XML-данных из строки символов с проверкой корректности
String query = "INSERT INTO clients (id, name, status contactinfo) " +
    "VALUES (?, ?, ?, xmlvalidate(? according to xmlschema id user1.mysample))";

Запрос XML-данных

Теперь, после того, как вы научились вставлять XML-данные при помощи Java-программ, можно перейти к запросу данных XML. В этом разделе предлагается рассмотреть несколько примеров, от простой задачи (извлечение XML-документа полностью) до более сложных задач (возврат частей XML-документа по предикатам реляционных и XML запросов).

Хотя DB2 поддерживает в качестве языков верхнего уровня как SQL, так и XQuery, последний не предоставляет средств для разрешения маркеров параметров. На практике это означает, что любой запрос XQuery в приложении, требующий большей функциональности, чем может предоставить жестко запрограммированный предикат, должен быть вставлен в предложение SQL при помощи таких функций SQL/XMl, XMLQuery или XMLExists. В статье "Query DB2 XML data with SQL" (Запрос XML-данных в среде DB2 при помощи языка SQL) (сайт developerWorks, март 2006 г.) эти функции описаны очень подробно. Здесь вы увидите, как можно использовать их в Java-программах. А также, только из любви к искусству, здесь показано, как включить XQuery с жестко запрограммированными предикатами запроса в приложение.

Запрос всего XML-документа

Наш первый способ, основанный на запросе, достаточно прост. Он просто извлекает всю контактную информацию по данному клиенту. Запрос такого характера может быть легко написан на SQL. Поэтому, если вы хорошо знаете интерфейс JDBC, вы легко разберетесь в этом коде.

Метод simpleQuery() в листинге 7 объявляет несколько переменных, а затем устанавливает соединение с базой данных при помощи вспомогательного метода, описанного в листинге 2. Строка query содержит простое предложение SQL для выбора всей контактной информации о конкретном клиенте. После выполнения этого предложения приложение распечатывает результаты, которые были записаны в переменную типа строка символов (stringDoc).

Листинг 7. Извлечение документов XML полностью при помощи SQL
import java.sql.*;
 . . . 
public static void simpleQuery() {
  PreparedStatement selectStmt = null;
  String query = null, stringDoc = null;
  ResultSet rs = null;
  int clientID = 1885;
		
  try{	
     // get a connection 
    Connection conn = Conn.getConn(); 
    
    // define, prepare, and execute the query
    // this will retrieve all XML data for a specific client 
    query = "select contactinfo from clients where id = " + clientID
    selectStmt = conn.prepareStatement(query);
    rs = selectStmt.executeQuery();

    // check for results 
    if (rs.next() == false) {
        System.out.println("Can't read document with id " + clientID);
    }

     // fetch XML data as a string and print the results 
    else {
         stringDoc = rs.getString(1);
         System.out.println(stringDoc);
      }
      . . . 
      conn.close(); 
    }
    catch (Exception e) { . . . }	    
}

Эта программа распечатывает отдельную строку данных, содержащую всю контактную информацию XML о конкретном покупателе.

Хотя здесь такая возможность не рассматривается, можно использовать XQuery для запроса одного или нескольких полных XML-документов, при этом вам не нужно включать в запрос XQuery маркеры параметров. Далее в этой статье приводится фрагмент Java-кода, который использует XQuery для извлечения XML-данных.

Извлечение частей XML-документов

Одна из распространенных задач программирования - это извлечение частей XML-документов. Java-программа в этом примере извлекает имя и основной адрес электронной почты покупателя со статусом "Silver". Имя покупателя и информация о статусе сохраняется в столбце SQL VARCHAR, тогда как адреса электронной почты содержатся в документах XML в столбце "contactinfo" column.

Для краткости здесь опущен весь код из предыдущего примера и оставлены только строки, которые являются новыми или имеют какие-либо отличия.

Листинг 8. Запрос реляционных данных и фрагментов XML при помощи SQL/XML
. . . 
String status = "Silver";
		
try{	
     // get a database connection
   . . . .
    // define, prepare, and execute a query that includes 
    // (1) a path expression that will return an XML element and  
    // (2) a parameter marker for a relational column value 
   String query = "SELECT name, xmlquery('$c/Client/email[1]' " + 
       " passing contactinfo as \"c\") " + 
       " from clients where status = ?";
   PreparedStatement selectStmt = conn.prepareStatement(query);
   selectStmt.setString(1, status);
   ResultSet rs = selectStmt.executeQuery();
   
   // iterate over and print the results 
   while(rs.next() ){
        System.out.println("Name: " + rs.getString(1) +
           "   Email:  " + rs.getString(2));
    }
    . . . 
   // release resources 
}  
catch (Exception e) { . . . }

Этот код создает предложение SQL/XML, которое вызывает функцию XMLQuery. Он предоставляет для этой функции выражение пути; выполняя это выражение, DB2 переходит к первому элементу "email" под корневым элементом "Client" нужного XML-документа (обратите внимание на то, что выражение пути является регистрозависимым). Переменная $c и предложение SQL FROM показывают, где можно найти нужные документы - в столбце "contactinfo" таблицы "clients". Предложение WHERE еще более ограничивает круг нужных документов XML документами, которые будут найдены только в тех записях, в которых "status" клиента имеет определенное значение (в этом методе "Silver").

Вывод этой программы может быть таким:

Листинг 9. Пример вывода для приложения из предыдущего примера
Name: Lisa Hansen   Email:  

Name: Amy Liu   Email:  <email>sailer555@yahoo.com</email>
. . . .

В этом примере вывода для соответствующего условиям запроса покупателя (Lisa Hansen) не возвращается информация об адресе электронной почты, поскольку в ее XML-документе "contactinfo" этот элемент не существует.

Фильтрация по реляционным предикатам и предикатам XML

Java-программа может также содержать инструкции DB2 выполнить фильтрацию вывода запроса по условиям, которые применимы как к XML, так и к не-XML данным. Следующий пример, созданный на основе предыдущего, возвращает имена и основные адреса электронной почты покупателей со статусом "Silver", живущих в г. Сан-Хосе, штат Калифорния. Этот отдельный запрос проецирует данные из XML и не-XML столбцов, а также ограничивает данные, основываясь на содержимом обоих столбцов.

Представленный ниже пример включает только те фрагменты кода, которые были изменены по сравнению с предыдущим примером. В этом случае предложение SELECT вызывает функцию XMLExists в составе предложения WHERE, чтобы ограничить результаты покупателями, живущими в конкретном городе и штате (определенными соответственно в переменных city и state).

Листинг 10. Фильтрация XML-данных по значениям элементов XML
. . . 
String status = "Silver";
String state = "CA";
String city = "San Jose";
. . . 	
try{	
    . . . .
   String query = "SELECT name, xmlquery('$c/Client/email[1]' " + 
       " passing contactinfo as \"c\") " + 
       " from clients where status = ?" 
       " and xmlexists('$c/Client/Address[state=$state][city=$city]' " +
       " passing contactinfo as \"c\", " + 
       " cast(? as char(2)) as \"state\", " + 
       " cast(? as varchar(30)) as \"city\" )";
   PreparedStatement selectStmt = conn.prepareStatement(query);
   selectStmt.setString(1, status);
   selectStmt.setString(2, state); 
   selectStmt.setString(3, city); 
   . . . 
}

Большая часть запроса должна быть вам понятна, поэтому в этом разделе мы сконцентрируемся на заключительных четырех строках. Функция XMLExists дает указание DB2 выяснить, содержит ли данный XML-документ адрес клиента, включающий определенные город и штат. Предложение PASSING определяет место, где можно найти XML-документы: в столбце "contactinfo". Функция CAST вызывается дважды для приведения значений входных параметров (для города и штата) к соответствующему типу данных.

Вывод этой программы показан в листинге 9, при условии, что и Lisa Hansen, и Amy Liu проживают в г. Сан-Хосе, штат Калифорния.

Использование XQuery в качестве языка верхнего уровня

Хотя DB2 полностью поддерживает язык XQuery как язык верхнего уровня, первоначальная спецификация XQuery не описывает маркеры параметров. На практике это ограничивает использование XQuery в Java-приложениях. В предыдущем разделе показано, как можно встроить XQuery в SQL (например, при помощи функций XMLQuery и XMLExists), чтобы включить маркеры параметров. В этом разделе мы рассмотрим, что можно сделать с чистым XQuery в Java-приложении.

Следующий пример содержит запрос XQuery, представленный в статье "Query DB2 XML data with XQuery" (Запрос XML-данных в среде DB2 при помощи языка SQL) (сайт developerWorks, апрель 2006 г.). Этот запрос XQuery определяет, кто из покупателей проживает в г. Сан-Хосе, штат Калифорния. Для каждого такого покупателя он создает фрагмент XML, содержащий "emailList" со всеми адресами электронной почты этого покупателя. И, наконец, он возвращает последовательность из списка emailLists.

Листинг 11. Извлечение фрагментов XML при помощи XQuery
try{	
    // get a database connection 
    Connection conn = Conn.getConn();

   // define, prepare, and execute an XQuery (without SQL). 
   // note that we must hard-code query predicate values. 
   String query = "xquery for $y in db2-fn:xmlcolumn" + 
      "('CLIENTS.CONTACTINFO')/Client " +
      "where $y/Address/city=\"San Jose\" and $y/Address/state=\"CA\"  " +  
      "return <emailList> { $y/email } </emailList>";
    PreparedStatement selectStmt = conn.prepareStatement(query);
    ResultSet rs = selectStmt.executeQuery();
   
     // iterate over all items in the sequence and print results.
     while(rs.next() ){
         System.out.println(rs.getString(1)); 
      }

      // release all resources 
      . . .  
     // catch and handle any exceptions 
     . . . 
}

Следует остановиться на двух аспектах этого запроса. Во-первых, строка запроса начинается с ключевого слова "xquery". Это инструктирует DB2 о том, что для обработки запроса нужно использовать синтаксический анализатор XQuery. Это необходимо делать во всех случаях, когда вы используете XQuery в качестве основного языка. Во-вторых, в запросе упоминаются имена таблиц и столбцов в верхнем регистре. XQuery является регистрозависимым языком. Поскольку DB2 обычно переводит имена таблиц и столбцов в верхний регистр при записи этой информации во внутренние каталоги, XQuery должен сопоставить эту информацию.

Пример вывода этой программы показан в листинге 12. Поскольку для каждого покупателя, соответствующего критериям запроса, возвращается один элемент "emailList", быстрый просмотр этого вывода показывает, что запросу соответствуют четыре покупателя. Первая запись содержит один адрес электронной почты. Вторая не содержит ни одного адреса (может быть, потому, что покупатель не предоставил этой информации); в результате элемент emailList пуст. Третья запись показывает, что в записи об этом покупателе имеется два адреса электронной почты. Четвертая запись содержит один адрес электронной почты для покупателя.

Листинг 12. Простой вывод предыдущего приложения
<emailList><email>newemail@someplace.com</email></emailList>

<emailList/>

<emailList><email>beatlesfan36@hotmail.com</email>
<email>lennonfan36@hotmail.com</email></emailList>

<emailList><email>sailer555@yahoo.com</email></emailList>

Может быть, вам интересно, почему имена каждого покупателя, сооветствующего критериям запроса, не были включены в результаты. Ответ прост: XQuery работает с данными XML, а имена покупателей хранятся в столбце SQL VARCHAR . Поэтому если вы хотите, чтобы в выводе присутствовали имена покупателей, соответствующих критериям запроса, а также их адреса электронной почты, вам нужно написать запрос, включающий элементы XQuery и SQL.

Обновление и изменение данных XML

Чтобы обновить и удалить данные XML, хранящиеся в DB2, используйте предложения UPDATE и DELETE. Эти предложения могут включать функции SQL/XML, ограничивающие нужные записи и столбцы по значениям элементов XML, хранящихся в столбцах XML. Например, можно удалить записи, содержащие информацию о покупателях, живущих в определенном городе, или обновить XML (и не-XML) данные только для тех покупателей, которые живут в данном штате.

Поскольку синтаксис использования функций SQL/XML в предложениях UPDATE и DELETE не отличается от синтаксиса их использования в предложениях SELECT, мы не будем повторять полные примеры кода. Вместо этого приводим краткие фрагменты. Сначала рассмотрим оператор DELETE.

Примеры удаления

Удаление записи, содержащей данные XML, не представляет сложности. Просто используйте предложение SQL DELETE с выражением WHERE (если нужно) для ограничения удаляемых записей. Например, следующий код удаляет запись о клиенте ID 1885:

Листинг 13. Удаление данных по значению реляционных данных
. . . 
 int clientID = 1885;
String query = "delete FROM clients WHERE id = ?";
 . . .  
PreparedStatement stmt = conn.prepareStatement(query);
stmt.setInt(1, clientID);
if (stmt.executeUpdate() == 0) {
   System.out.println("No records deleted.");
}
else { System.out.println("Record(s) deleted."); }
 . . .

Если нужно ограничить действия DELETE по значениям элемента XML, просто вызовите соответствующую функцию SQL/XML в предложении WHERE. Листинг 14 использует функцию XMLExists , чтобы определить, что информация обо всех клиентах, проживающих в штате Мен (сокращенно "ME"), должна быть удалена:

Листинг 14. Удаление данных с условием на значение элемента XML
String state = "ME";
String query = "delete from clients " + 
" where xmlexists('$y/Client/Address[state=$state]' " +
" passing clients.contactinfo as \"y\", " + 
" cast(? as char(2)) as \"state\" )";
. . . 
PreparedStatement stmt = conn.prepareStatement(query);
stmt.setString(1, state);
. . .

Примеры обновления

Данные в столбце XML можно обновить при помощи предложения SQL UPDATE или хранимой процедуры DB2XMLFUNCTIONS.XMLUPDATE. В обоих случаях, изменение столбца XML происходит на уровне документа, а не на уровне элемента. Однако программистам, выполняющим обновление при помощи хранимой процедуры, не нужно доставлять полный документ XML в DB2. Необходимо только определить элементы XML, подлежащие обновлению, и DB2 сохранит неизмененные данные документа, а также обновит определенные элементы. Программистам, создающим предложения UPDATE, необходимо определить документ полностью (а не только те элементы, которые они хотят изменить).

В одной из следующих статей будет рассматриваться хранимая процедура XMLUPDATE с примером кода, поэтому здесь она не обсуждается. Вместо этого в данном разделе приводятся два примера кода, выполняющего предложения UPDATE. Вам следует уяснить логическую схему двух этих примеров. В одном из примеров для обновления таблицы "clients" используется файл XML, а в другом - строка символов, содержащая XML.

Листинг 15 обновляет контактную информацию о клиенте ID 1333 с использованием XML-данных, содержащихся в файле. Обратите внимание, что новые данные ХML в ходе операции обновления проверяются на корректность по зарегистрированной схеме:

Листинг 15. Обновление XML-данных из файла
int clientID = 1333;
String fn = "c:/XMLFiles/Client1333.xml";  // input file
String query = "update clients set contactinfo = " +
  "xmlvalidate(? according to xmlschema id user1.mysample) " + 
  "where id = ?";
. . . 
PreparedStatement stmt = conn.prepareStatement(query);
stmt.setInt(2, clientID);
File file = new File(fn);
stmt.setBinaryStream(1, new FileInputStream(file), (int)file.length());
. . .

Конечно, для определения контактных записей покупателя, подлежащих обновлению, вы можете использовать и предикат запроса XML. И снова с этой целью вам придется использовать функции SQL/XML. Представьте себе, что покупательница хочет, чтобы вы изменили номер факса, но при этом не помнит свой идентификатор клиента. Чтобы помочь найти информацию о себе, она предоставляет номер домашнего телефона. Следующий фрагмент кода использует функцию XMLExists, чтобы ограничить обновления только записью, содержащей данный номер телефона. Обратите внимание, что полная контактная информация покупательницы представлена как строка кода Java, содержащая отредактированный документ XML.

Листинг 16. Обновление данных XML при помощи строки символов
String homeph = "4081114444";
String xml = 
   "<?xml version=\"1.0\"?>" +
   "<Client>" +
   "<Address> " +
      "<street>54 Moorpark Ave.</street>" +
      "<city>San Jose</city>" +
      "<state>CA</state>" +
      "<zip>95110</zip>" +
   "</Address>" +
   "<phone>" +
      "<work>4084630110</work>" +
      "<home>4081114444</home>" +
      "<cell>4082223333</cell>" +
   "</phone>" +
      "<fax>4087773111</fax>" +
   "<email>sailer555@yahoo.com</email>" +
   "</Client>";

String query =  "update clients set contactinfo = ?" + 
   "where xmlexists('$y/Client/phone[home=$homeph]' " +
   " passing clients.contactinfo as \"y\", " + 
   " cast(? as varchar(11)) as \"homeph\" )";
. . . 
PreparedStatement stmt = conn.prepareStatement(query);
stmt.setString(1, xml);
stmt.setString(2, homeph);
. . .

Построитель запросов Query builder

Если при написании запросов для приложений вам нужна помощь, Developer Workbench предоставляет специальный мастер, который генерирует запросы SQL/XML и XQueries. Поскольку большинство программистов на Java пишут приложения, которые требуют использования маркеров параметров, они часто используют SQL/XML. В этом разделе мы подробно рассмотрим краткий пример использования построителя запросов SQL для генерации предложений SQL/XML, подобных показанным в листинге 8.

Чтобы сгенерировать предложение SQL/XML, выполните следующие шаги:

  1. Подготовьте рабочую среду;
  2. Определите характеристики запроса;
  3. Выполните запрос.

Выполним все эти шаги по очереди.

Подготовьте рабочую среду.

Предложения SQL создаются в составе проекта "Data project", который можно открыть из режима Data среды разработки Workbench. Чтобы создать такой проект, выполните следующие шаги:

  1. Откройте режим Data. Выберите Window > Open Perspective > Other > Data;
  2. Установите соединение с нужной базой данных. Нажмите правой кнопкой мыши в панели обозреватель базы данных Database Explorer в нижнем левом углу. Выберите New Connection и задайте имя базы данных, имя пользователя и пароль;
  3. Создайте новый проект данных. Нажмите правой кнопкой мыши в панели обозревателя проектов данных Data Project Explorer в верхнем левом углу. Выберите New > Project > Data > Data Development Project. По предложению программы введите имя проекта на ваш выбор и ассоциируйте его с соединением с базой данных, которую вы предварительно создали.

После установления соединения с базой данных и создания проекта можно перейти к построению запроса.

Построение запроса

Вернемся к предыдущему примеру и создадим предложение SQL/XML, которое возвратит основной адрес электронной почты клиентов, имеющих определенный статус. Запрос получится примерно таким:

Листинг 17. Пример запроса SQL/XML
SELECT name, xmlquery('$c/Client/email[1]'  
passing contactinfo as "c")   
from clients where status = ?

Чтобы сгенерировать запрос, выполните следующие шаги:

  1. Запустите построитель запросов SQL Builder. В окне проекта данных выделите папку SQL Scripts и нажмите на ней правой кнопкой мыши. Выберите New > SQL Statement. В открывшемся окне согласитесь с именем, присвоенным проекту по умолчанию и задайте имя для предложения SQL. Примите тип предложения, установленный по умолчанию (SELECT) и выберите использование построителя SQL. Нажмите Finish;
  2. Определите таблицу, к которой будет обращен запрос. Нажмите правой кнопкой мыши в центральной панели и выберите Add Table. Разверните вашу папку схем и выберите таблицу "clients";
  3. Определите столбец, который вас интересует. В нашем примере вам нужно включить в результирующее множество один столбец и вывод одной функции (XMLQuery). Для этого выполните следующие шаги:
    1. Установите флажок в столбце "names", отображаемом в центральной панели;
    2. Нажмите мышью на первой записи, отображаемой на вкладке Column под центральной панелью. Нажмите мышью в правом углу этой ячейки, чтобы появилась клавиша со стрелкой, и выберите Build Expression. Нажмите Enter;
    3. Выберите из открывшегося меню команду Function;
    4. Выберите категорию функции XML и функцию XMLQuery. Рядом с полем Parameter Value 1 нажмите стрелку в ячейке Value и выберите Edit Expression;
    5. Определите соответствующее выражение пути в построителе строковых констант String Constant Builder: $c/Client/email[1], и дважды нажмите Finish;
    6. Измените сгененированное предложение SQL, добавив выражение PASSING в функцию XQuery. Окончательная функция XQuery должна выглядеть так: '$c/Client/email[1]' passing contactinfo as "c".
  4. Определите предикат запроса (предложение WHERE). В нашем примере необходимо добавить один предикат запроса для реляционного столбца;
    1. Под вкладкой Conditions и создаваемым предложением SQL/XML, нажмите мышью на первой записи, отображаемой на вкладке Column. Нажмите кнопку со стрелкой в правом углу этой ячейки и выберите столбец status;
    2. Нажмите мышью на ячейке Operator и выберите оператор равенства ("=");
    3. Нажмите кнопку со стрелкой в правом углу ячейки Value и выберите Build Expression. Нажмите Enter;
    4. В открывшемся окне выберите Constant, а затем String Constant;
    5. Определите серверное имя переменной для пользовательского ввода (например, "status"). Нажмите Finish.

Выполните запрос.

После того, как запрос создан, можно перейти к его выполнению.

  1. Найдите запрос в проекте Data, нажмите на нем правой кнопкой мыши и выберите команду Run SQL;
  2. В открывшемся окне определите входное значение для статуса покупателя (например, "Gold" или "Silver") и нажмите OK;
  3. Просмотрите результаты в панели вывода данных Data Output.

Хранимые процедуры

В сетевой среде хранимые процедуры часто уменьшают количество коммуникаций между клиентским приложением и DB2. Это, безусловно, приводит к повышению производительности. При использовании программы Viper хранимые процедуры могут содержать параметры и переменные XML.

Поскольку в этой статье не предполагается подробное рассмотрение разработки хранимых процедур, мы включили в нее один простой сценарий, чтобы продемонстрировать, как можно написать хранимую процедуру DB2 для запроса фрагментов XML-документов. Этот сценарий использует мастеров Developer Workbench, чтобы сгенерировать, разместить и выполнить необходимый код хранимой процедуры SQL. При желании можно разрабатывать и размещать равноценные хранимые процедуры SQL через обработчик командной строки. Кроме того, вы можете писать хранимые процедуры для XML на Java.

В нашем примере вы напишете хранимую процедуру, которая выполнит извлечение имен и основных адресов электронной почты клиентов с заданным статусом, как вы это делали раньше. Несмотря на то, что это достаточно простая процедура, она поможет понять, как при помощи встроенных мастеров создать процедуру SQL, которая будет запрашивать и возвращать данные.

Чтобы создать эту процедуру, выполните следующие шаги:

  1. Подготовьте рабочую среду;
  2. Определите содержание вашей процедуры;
  3. Разработайте и протестируйте процедуру.

Выполним все эти шаги по очереди.

Подготовьте рабочую среду.

Хранимые процедуры определяются в составе проекта Data project. Если вы еще не создали проект, откройте режим Data, установите соединение с базой данных и создайте проект данных Data project. Подробности можно найти в предыдущем разделе Подготовьте рабочую среду.

Создайте процедуру

Наша хранимая процедура на SQL вызывает отдельное предложение SQL/XML для запроса таблицы "clients" на основе входных данных от вызывающей процедуры. Эта процедура возвращает одно результирующее множество, которое содержит столбец SQL VARCHAR (для имени клиента) и столбец XML (для адреса электронной почты). Запрос получится примерно таким:

Листинг 18. Пример запроса SQL/XML
SELECT name, xmlquery('$c/Client/email[1]'  
passing contactinfo as "c")   
from clients where status = ?

Процесс построения хранимой процедуры SQL, которая обращается к данным XML, не отличается от построения процедуры SQL, обращающейся к прочим данным. Вот один из способов построения:

  1. Определите новую хранимую процедуру. Разверните новый проект данных Data project, выделите Stored Procedures и нажмите правой кнопкой мыши. Выберите New > Stored Procedure. В открывшемся окне проверьте имя проекта и определите имя хранимой процедуры. Оставьте тип языка по умолчанию SQL;
  2. Определите предложение (я) SQL. После того, как появится приглашение, вы можете ввести предложение SQL самостоятельно или воспользоваться мастерами, которые помогут создать его. Следующие шаги описывают последний вариант:
    1. Нажмите Create SQL;
    2. Согласитесь с типом предложения по умолчанию (SELECT) и вариантом процесса разработки (создание предложения с помощью мастеров);
    3. Выберите таблицу clients в качестве цели предложения;
    4. На вкладке Columns включите в окончательное результирующее множество два столбца. Выберите names, а затем Add > Function > Next. В следующем окне определите категорию функции XML и сигнатуру функции XQuery. Нажмите Finish;
    5. На вкладке Conditions постройте предложение SQL WHERE. Задайте столбец clients.status, оператор равенства ("=") и значение :input;
    6. Измените результирующее предложение SQL, включив в него соответствующее выражение пути для извлечения первого адреса электронной почты в столбец "contactinfo". Точнее, измените строку XMLQUERY следующим образом: xmlquery('$c/Client/email[1]' passing contactinfo as "c");
    7. Выполните анализ запроса, чтобы убедиться, что он не содержит синтаксических ошибок.
  3. Определите информацию для размещения. Особенно полезной может оказаться функция Enable Debugging;
  4. Если хотите, просмотрите сгенерированный код SQL. Нажмите Show SQL. (См. рисунок 2: пример того, что должно отображаться на мониторе);
  5. Завершите хранимую процедуру. Нажмите Finish.
Рисунок 2. Пример кода, сгенерированного для хранимой процедуры SQL, работающей с данными XML
Рисунок 2

Разместите и протестируйте процедуру.

Закончив разработку процедуры, можно перейти к размещению и тестированию. Выполните следующие шаги:

  1. Разместите процедуру. Найдите процедуру в проекте Data project, нажмите правой кнопкой мыши и выберите команду Deploy. Согласитесь с параметрами по умолчанию и нажмите кнопку Finish. В панели Data Output в нижнем правом углу должно появиться уведомление о том, что процедура была успешно размещена;
  2. Выполните процедуру. Найдите процедуру в проекте Data project, нажмите правой кнопкой мыши и выберите команду Run. В открывшемся окне определите входное значение для статуса покупателя (например, "Gold" или "Silver"). Нажмите OK и просмотрите результаты, возвращенные хранимой процедурой, в панели Data Output.

При желании вы можете вызвать хранимую процедуру за пределами среды Developer Workbench. Например, если имя вашей процедуры "getInfo", вы можете вызвать обработчик командной строки DB2, установить соединение с базой данных и выполнить следующее предложение:

Листинг 19. Вызов хранимой процедуры
call getInfo('Silver')

Заключение

При создании Java-приложений для работы с XML-данными используется обычный код JDBC для выполнения запросов и обработки их результатов. IBM поставляет вместе с DB2 среду разработки Developer Workbench на базе платформы Eclipse, чтобы помочь вам писать, тестировать и отлаживать программы. В эту среду разработки включены мастера для просмотра содержимого баз данных, написания хранимых процедур для работы с данными XML и другими данными, создания запросов XQuery для доступа к данным XML и создания предложений SQL/XML для доступа к данным XML и не-XML.

Благодарности

Благодарим Дона Чамберлина (Don Chamberlin), Гранта Хатчисона (Grant Hutchison)и Брайана Пейтона (Brian Payton) за рецензирование этой статьи.

Ресурсы

Научиться

Получить продукты и технологии

Обсудить

  • Примите участие в блогах developerWorks и присоединяйтесь к сообществу developerWorks.

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Information Management, XML
ArticleID=171865
ArticleTitle=Разработка Java-приложений для работы с данными DB2 XML
publish-date=05042006