| Каталог | Индекс раздела |
©IBM deweloperWork
© А.С.Деревянко (перевод)
Этот учебник создан для того, чтобы помочь тем Java-разработчикам, которым нужно выделить информацию из XML-документа и поместить ее в базу данных.
Учебник предполагает, что вы уже знакомы с Java и XML в общем и с Объектной Моделью Документа (DOM), в частности. Вы должны быть знакомы с языком программирования Java, но предварительные знания о JDBC для основных приемов, описанных в этом учебнике, не требуются. Это учебник кратко рассматривает основы SQL. Знания в программировании GUI не являются необходимыми, поскольку ввод-вывод приложения работает через командную строку. Ссылки в Ресурсах включают в себя указания на учебники по основам XML и DOM и на подробные ресурсы по SQL.
В данном учебнике демонстрируется метод выборки XML-данных и вставки их в базу данных, используя пример заказов, в записанных в XML-файл. Другой XML-файл используется для того, чтобы определить, какие элемент в файле заказов соответствуют каким таблицами и столбцам в базе данных. JDBC является независимым от производителя методом для доступа к базам данных из языка Java. Этот учебник объясняет, как создать экземпляр драйвера JDBC и использовать его для соединения с базой данных для сохранения информации. Он также объясняет основы SQL и как создать и использовать методы JDBC. Наконец, учебник демонстрирует приемы непосредственного изменения данных через результат запроса в Этот учебник поможет вам понять тему даже, если вы только прочитаете примеры, не пытаясь выполнить их. Если вы хотите выполнять примеры по мере прохождения через этот учебник, убедитесь, что у вас установлены и корректно работают следующие инструменты:
Для представления материала в данном учебнике используются некоторые соглашения:
Nicholas Chase участвовал в разработке Web-сайтов для таких компаний, как Lucent Technologies, Sun Microsystems, Oracle и Tampa Bay Buccaneers. Nick был преподавателем физики в высшей школе, менеджером низшего звена по использованию радиоактивных отходов, редактором онлайнового журнала научной фантастики, инженером по мультимедиа и инструктором по Oracle. В последнее время он - руководитель технического отдела фирмы Site Dynamics Interactive Communications в Clearwater, Florida, USA и автор четырех книг по Web-разработке, включая Java and XML From Scratch Он любит слышать отзывы читателей, с ним можно связаться по адресу nicholas@nicholaschase.com.
Если вы уже прошли через учебник "Использование JDBC для выборки данных XML ", вы можете перейти дальше к Файл orders.XML.
Не так давно для того, чтобы взаимодействовать с базой данных, разработчик должен был использовать специфический API этой базы данных. Это делало создание независимых от базы данных приложений трудным, если не невозможным.
JDBC похоже на ODBC (Open Database Connectivity), которое предоставляет стандартный API, являющийся посредником при доступе к базе данных. Как видно из рисунка, могут использоваться стандартные команды JDBC, и драйвер JDBC переводит их в естественный API для базы данных.
Вы заметите, что нигде в данном учебнике не упоминается конкретная база данных, потому что выбор ее не имеет значения. Все команды являются стандартными командами JDBC, которые переводятся драйвером в естественные команды для той базы данных, которую вы выбрали. Благодаря этой независимости API, вы можете легко использовать другую базу данных, не изменяя ничего в своем приложении, кроме имени драйвера и, возможно, URL соединения, используемого при Создании соединения.
Информацию о выгрузке соответствующих драйверов JDBC для вашей базы данных см. в Ресурсах. Доступно более 150 драйверов JDBC для виртуально любой базы данных.
Есть даже варианты для баз данных, которые не имеют доступных драйверов JDBC.
Нет необходимости иметь специфический драйвер JDBC для базы данных, если для нее доступен драйвер ODBC; вы можете использовать вместо этого мост JDBC-ODBC. Приложение вызывает мост, который переводит команды в ODBC, а драйвер ODBC переводит их в естественный API.
Мост JDBC-ODBC не является рекомендуемым способом обращения к базе данных по разным причинам, включая конфигурацию и производительность, - команды должны проходить через два API, и на каждом клиенте должен быть установлен и сконфигурирован драйвер ODBC - но он применим для тестирования и разработки, если нет чистого драйвера JDBC.
Если вы выбрали использование моста, создайте System Data Source Name (DSN) в системе Windows, выбрав Прежде всего, убедитесь, что база данных, которую вы выбрали, установлена и работает, и что драйвер доступен. Драйверы JDBC могут быть выгружены из http://industry.java.sun.com/products/jdbc/drivers.
После того, как вы создали базу данных, создайте необходимые таблицы. В конечном счете, структура не важна, так как она может быть определена из XML-файла отображения. Данный учебник предполагает наличие двух таблиц, Взаимодействие с базой данных при помощи Java обычно состоит из следующих шагов:
Пакет В данном учебнике используются только классы из JDBC 2.0 Core API.
Для обращения к базе данных сначала загрузите драйвер JDBC. В любой момент могут быть доступны много разных драйверов; имеется Первый способ - непосредственно загрузить его, используя Успешно загрузив драйвер, приложение может соединиться с базой данных.
Второй способ, которым Когда вы загрузили драйвер, приложение может соединиться с базой данных.
Однако, URL может быть написан в любом формате, который понимает драйвер. Посмотрите формат URL в документации для вашего драйвера JDBC.
Одной из причин введения под-протокола является соединение через ODBC. Если обращение к базе данных примера, с DSN Это означает, что для соединения через JDBC URL должен быть:
Реальное соединение создается ниже:
Когда соединение успешно установлено, можно выполнять любые операции с базой данных (например, вставку или изменение данных).
Поскольку Вполне возможно, что само Java-приложение имеет достаточно доступных ресурсов, что означает редкое выполнение сборки мусора. Возможно также, что хотя Java-приложение имеет достаточно ресурсов, ресурсы базы данных ограничены. Много ресурсов базы данных могут быть забраны объектами Java, которые легко могут быть закрыты приложением.
Важно быть уверенным, что эти объекты закрыты, независимо от того, были или не были ошибки, так что добавим блок Смешно, но сам метод Данные заказов для данного учебника находятся в файле Чтение XML-данных начинается с разбора файла и создания объекта Этот пример показывает создание объекта После создания Прежде, чем вы сможете построить приложение, которое читает XML-файл на основе файла отображения, вам нужно сначала прочитать XML без файла отображения.
В данном примере элемент Чтобы выбрать информацию из Поскольку каждый Далее выведите данные.
Выборка действительных данных включает в себя комбинацию Сначала выберите атрибут Выборка значений дочерних элементов чуть более сложна. Сначала выберите Теперь, когда вы имеете данные, вы готовы начать добавлять их в базу данных.
Наиболее очевидным способом добавить любые данные в базу данных является применение оператора SQL. Наиболее общий объект, используемый для выполнения SQL-оператора на базе данных - Объект Вставка данных при помощи SQL предполагает использование оператора Вы можете использовать объект В общем случае для выполнения SQL-оператора при помощи объекта В данном учебнике будет использоваться Создайте оператор В конце концов, это действие будет "обобщено" для использования XML-файла отображения, который вводится в следующем разделе.
Теперь, когда вы успешно ввели данные в базу данных, пора обобщить эту информацию так, чтобы можно было использовать то, что есть в XML-файле отображения.
Назначением файла отображения является задание всех аспектов передачи данных, включая структуру и имя файла, содержащего заказы, и местоположение и структуру базы данных.
Файл отображения может быть весьма сложным, так что в данном учебнике мы сделаем определенные допущения:
В данном учебнике предполагается, что даже если действительные типы данных несколько различаются, они остаются в пределах общей классификации.
Имея в виду эти ограничения, вы можете построить файл отображения на основе имеющегося файла Файл отображения должен различать два разных типа информации: информацию базы данных и информацию XML-файла. Это может быть сделано при помощи двух дочерних элементов корня. Первый имеет дело с информацией базы данных:
Элемент Столбцы базы данных и таблицы будут представляться как переменные Файл отображения также задает информацию XML.
XML-порция файла содержит несколько больше информации; данные могут быть в элементах или в атрибутах:
Благодаря способу структурирования приложения, нет необходимости показывать родителей или, в случае атрибутов, элементы к которым они относятся. Базисная точка, так сказать, уже доступна в приложении. Все, что вам нужно знать, это имя составляющей и является она элементом или атрибутом.
Файл отображения содержит даже информацию о том, в каком файле содержатся заказы и с какой базой данных нужно соединяться. В результате этого этот файл нужно разобрать и проанализировать до того, как вы сможете выполнить что-либо еще.
Данный пример показывает, как выбрать информацию файла заказов:
Первое, что нужно отметить, что В предыдущих примерах В блоке Вам нужна также информация базы данных.
Получение основной информации базы данных понятно, поскольку драйвер и URL являются атрибутами элемента Заметьте, что в динамической загрузке класса есть одно преимущество. Если вы меняете драйверы JDBC, вам не нужна перекомпиляция; только измените файл отображения.
Остаток информации базы данных, такой как имена таблиц и столбцов, повторяющийся и лучше всего для него подходит отдельный метод.
Все переменные базы данных хранятся как дочерние элементы одного и того же элемента, так что для выборки их подходит отдельный метод:
Метод Чтобы использовать переменные, просто замените жестко закодированные их эквиваленты в SQL-операторе:
Здесь обрабатываются имена столбцов. Имена столбцов уже поступили из Получение основных переменных XML-файла отображение достаточно просто благодаря допущениям, сделанным выше. Общая структура документа предопределена, так что все, что вам нужно, чтобы начать, это имена элементов, хранящих заказы и товары. Эта информация имеется в файле отображения как текст в элементах Когда установлена основа, пора искать элементы отображения.
Сами элементы отображения сами должны также быть выделены в метод по двум причинам. Во-первых, эта логика будет использоваться многократно. Во-вторых, поскольку включается логика, в ваших же интересах обеспечить модульность операций. Таким образом, если вы решите изменить структуру файлов отображения или заказов, изменить логику чтения файла отображения будет легче.
Сама логика принимает во внимание тот факт, что данные, которые вы ищете, могут находиться и в элементах, и в атрибутах:
Предыдущие примеры показывали данные, находящиеся на фиксированных местах, но этот пример выбирает данные из многих узлов. К счастью, вы всегда имеете базовый узел, либо Теперь вы должны сделать операцию типовой. Используя файл отображения, вы можете изменить имена таблиц и столбцов или даже самой базы данных. Возможно также изменить структуру файла Комбинирование XML-файлов и базы данных, однако, включает в себя больше, чем только файл отображения. Приложения базы данных часто группируют операции в транзакции, которые могут быть отменены или откачены, если есть ошибки в данных или в самом XML-файле. Следующий раздел объясняет транзакции и пакетные операции.
Теперь, когда приложение собрано, есть несколько относительно простых вещей, которые могут сделать его более надежным.
Часто при манипуляциях с базой данных существует точка зрения "все или ничего". Когда запись изменяется, частичные изменения нежелательны; трудно вернуть частично измененную запись в "исправное" состояние. Так что, когда изменяется запись базы данных, приложение должно либо сохранять все изменения, либо иметь средства возврата записи в предыдущее состояние. В контексте нашего примера, если вы вставляете заказы, и один из операторов приводит к ошибке, вы захотите иметь инструмент для отмены других изменений, которые вы уже сделали.
Возможность делать это известна как использование транзакций. В транзакциях все операторы, которые выполняются на базе данных, существуют в состоянии "неопределенности", пока они либо не зафиксируются и изменения не сделаются постоянными, либо не откатятся и изменения не будут отменены. Реальная транзакция начинается либо с первого SQL-оператора, либо немедленно после фиксации или отката.
Обычно Вы можете использовать транзакции для создания приложения, которое будет включать все Группирование отдельных вставок в транзакция просто. В данном примере приложение просто отслеживает, есть ли какие-либо ошибки, и, если есть, откатывает транзакцию:
Чтобы отслеживать, есть ли ошибки, создайте переменную типа Вы можете также включить и другие операции для принятия решения о фиксации транзакции.
Поскольку вы непосредственно управляете тем, что транзакция фиксируется, на это решение могут влиять любые действия, которые могут выбрасывать исключения. Например, вы хотите добавлять каждый заказ в базу данных только один раз, так что вы можете захотеть удалить файл Фактически, вы можете установить приложение так, что любая ошибка приведет к откату:
Таким образом, любое исключение будет устанавливать Однако перед включением транзакций вы должны убедиться, что база данных и драйвер могут их поддерживать.
Не все базы данных и драйверы поддерживают транзакции, так что перед встраиванием этой возможности в ваше приложение вы должны убедиться, что ваши это делают.
Вы можете определить поддержку транзакций и другие продвинутые свойства, проверив метаданные базы данных. Выберите эту информацию из В данном приложении, есть на самом деле только два места, где имеет значение, поддерживаются ли транзакции: когда выключается autocommit и когда транзакция либо фиксируется, либо откатывается. Все остальное обрабатывается обычным образом, исключая то, что без транзакций операции фиксируются немедленно.
Если транзакции не поддерживаются, приложение прекрасно продолжает работать без них. Но в некоторых случаях вам нужно писать две отдельные ветви в зависимости от поддержки каких-то свойств. Одним из таких свойств, которое может быть использовано для повышения производительности, является пакетная обработка.
Раз вы фиксируете все или никакие вставки, зачем использовать пропускную способность для базы данных для каждой единичной операции? Вместо этого вы можете создать пакет операций, который весь будет выполняться как один шаг на сервере:
Если база данных и драйвер поддерживают пакетные изменения, добавляйте операторы в пакет вместо того, чтобы выполнять их. Когда цикл закончится, если есть поддержка, выполните пакет. Если нет поддержки, операторы уже должны быть выполнены.
Как видите, процесс все еще остается относительно простым; все данные добавляются в базу данных, независимо от того, существуют ли они в ней уже. На самом деле ситуация не всегда так проста.
Чтобы собрать воедино информацию заказов, вам нужно интергировать ее с информацией, которая уже существует в базе данных, а не просто создавать и выполнять SQL-операторы. Одним из способов делать это являются изменяемые (В Ресурсах есть ссылка на углубленный учебник по использованию только-читаемых Данный фрагмент кода демонстрирует шаги, необходимые для использования Есть методы для выборки данных любого доступного типа. Как приведенный здесь getString(), каждый из них может принимать либо имя столбца (более удобно, но менее эффективно), либо позицию столбца в По-настоящему мощность Созданный с установками по умолчанию, Этот Первый параметр задает тип Второй параметр устанавливает признак, определяющий, может ли Замтьте, что если вы только изменили Создание и изменение Создайте Эти изменения не воздействуют на базу данных, пока не будет вызван метод Теперь вы можете легко применить информацию отображения.
Одним из преимуществ использования изменяемого В данном случае SQL-оператор является единственным местом, где строки должны комбинироваться сложным способом. Переменные, представляющие имена столбцов просто указываются в методе Если не существует записи для данного товара, вам понадобится вставить новую запись в базу данных.
Вы проделываете это со специальной строкой в Данный пример показывает, как проверить существование текущей записи и как создать новую, если проверка показала, что ее нет:
Как и при модификации, никакие изменения в базе данных на самом деле при вызове методов updateXXX не производятся. Только когда вы выполняете insertRow(),база данных получает новые данные. (В данном случае getSum() и getRev использованы для преобразования тиров.)
Теперь вы имеете совершенно типовой способ и для модификации, и для вставки данных в базу данных.
Хотя XML-файлы превосходны для хранения небольших объемов данных, в конечном счете, вы захотите использовать XML вместе с базой данных. В этом учебнике обсуждены основы соединения с базой данных при помощи JDBC, а также вставка данных в базу данных через JDBC и создание транзакций базы данных.
Данные вводятся в базу данных на основе внешнего XML-файла отображения, который определяет имена базы данных, таблиц, столбцов и имена элементов и атрибутов в XML-файле, хранящем данные, вставляемые в базу данных.
В этом учебнике также обсуждены изменяемые И JDBC, и XML являются мощными инструментами, каждый в своем роде. Места, где можно найти дополнительную информацию, такие:
Информация по XML
Информация по JDBC
Что можно выгрузить
О чем этот учебник?
ResultSet.
Инструменты
Вы можете найти список из более, чем 150 драйверов JDBC на http://industry.java.sun.com/products/jdbc/drivers. (Если у вас есть драйвер ODBC, вы можете пропустить этот шаг и использовать мост JBDC-ODBC.) Этот учебник использует JDataConnect, доступный на http://www.j-netdirect.com/Downloads.htm.
Соглашения, используемые в данном учебнике
жирным моноширинным шрифтом. В некоторых кодовых примерах жирный шрифт используется, чтобы привлечь внимание к тегу или элементу, на который ссылается сопроводительный текст.
Моноширинный шрифт представляет имена файлов и маршруты.
...)
Об авторе
Раздел 2. Доступ к базе данных через JDBC
Что такое JDBC?
Мост JBDC-ODBC
Start>Settings>Control>ODBC Data Sources. Запишите имя DSN, так как вы будете на него ссылаться при Создании соединения.
Установка базы данных и драйвера
orders и productOrders. Ниже показана ее структура. Создайте таблицы, выполнив соответствующие шаги для вашей базы данных.
create table orders (
orderid numeric
primary key,
userid varchar(50),
productid varchar(10),
quantity numeric,
unitprice numeric )
create table productOrders (
productid varchar(10),
quantity numeric,
revenue numeric )
Процесс обращения к базе данных
Connection с базой данных.
Statement. Этот объект реально выполняет SQL или хранимые процедуры.
ResultSet и заполнение его результатами выполнения запроса (если целью является получение или непосредственное изменение данных).
ResultSet.
java.sql содержит JDBC 2.0 Core API для доступа к базе данных, как часть поставки JavaTM 2 SDK, Standard Edition. Пакет javax.sql, который поставляется, как часть Java 2 SDK, Enterprise Edition, содержит JDBC 2.0 Optional Package API.
Создание экземпляра драйвера
DriverManager, который знает о каждом драйвере и решает, какой из них будет использоваться для создания соединения. Первый, способный успешно создать соединение, будет использоваться приложением. Есть два способа, которыми DriverManager может узнать о существующих драйверах.
Class.forName(), как показано в данном примере. Когда класс драйвера загружен, он регистрируется при помощи DriverManager, как показано здесь:
public class SaveOrders extends Object {
public static void main (String args[]){
//Для моста JDBC-ODBC используйте
//driverName = "sun.jdbc.odbc.JdbcOdbcDriver"
String driverName = "JData2_0.sql.$Driver";
try {
Class.forName(driverName);
} catch (ClassNotFoundException e) {
System.out.println("Error creating class: "+e.getMessage());
}
}
}
DriverManager может найти драйвер, является цикл через все драйверы, имеющиеся в системном свойстве sql.drivers. Свойство sql.drivers является разделенным запятыми списком потенциальных драйверов. Этот список всегда проверяется прежде, чем класс загружается динамически, так что, если вы хотите использовать определенный драйвер, убедитесь, что свойство sql.drivers либо пустое, либо начинается с вашего драйвера.
Создание соединения
DriverManager выполняет соединение через статический метод getConnection(), который принимает в качестве аргумента URL базы данных. URL обычно представляется как:
jdbc:<sub-protocol>:имя базы данных
pricing, выполняется через ODBC, URL может быть:
odbc:orders
jdbc:odbc:orders
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class SaveOrders extends Object {
public static void main (String args[]){
//Для моста JDBC-ODBC используйте
//driverName = "sun.jdbc.odbc.JdbcOdbcDriver"
//и
//connectURL = "jdbc:odbc:orders"
String driverName = "JData2_0.sql.$Driver";
String connectURL = "jdbc:JDataConnect://127.0.0.1/orders";
Connection db = null;
try {
Class.forName(driverName);
db = DriverManager.getConnection(connectURL);
} catch (ClassNotFoundException e) {
System.out.println("Error creating class: "+e.getMessage());
} catch (SQLException e) {
System.out.println("Error creating connection: "+e.getMessage());
}
}
}
Закрытие соединения
Statement и Connection являются объектами, Java будет включать их в сборку мусора, освобождая ресурсы базы данных, которые они занимают. Это может ввести вас в заблуждение и заставить думать, что вы не должны заботиться о закрытии этих объектов, но это не так.
finally к уже имеющемуся блоку try-catch:
...
Connection db = null;
try {
Class.forName(driverName);
db = DriverManager.getConnection(connectURL);
} catch (ClassNotFoundException e) {
System.out.println("Error creating class: "+e.getMessage());
} catch (SQLException e) {
System.out.println("Error creating connection: "+e.getMessage());
} finally {
System.out.println("Closing connections...");
try {
db.close();
} catch (SQLException e) {
System.out.println("Can't close connection.");
}
}
}
}
close()может генерировать SQLException, так что для него нужен свой блок try-catch.
Раздел 3. Чтение XML-файла
Файл orders.xml
orders.xml. Каждый заказ содержит информацию, специфичную собственно для заказа, такую как номер заказа, id пользователя и общую сумму. Каждый заказ содержит также информацию об отдельных составляющих заказа, такую как id товара, цена за единицу и количество.
Файл orders.xml приводится ниже:
<?xml version="1.0" encoding="UTF-8"?>
<orders>
<order orderid="1">
<customerid>12341</customerid>
<status>pending</status>
<product productid="SA15">
<name>Silver Show Saddle, 16 inch</name>
<price>825.00</price>
<qty>1</qty>
</product>
<product productid="C49">
<name>Premium Cinch</name>
<price>49.00</price>
<qty>1</qty>
</product>
</order>
<order orderid="2">
<customerid>251222</customerid>
<status>pending</status>
<product productid="WB78">
<name>Winter Blanket (78 inch)</name>
<price>20</price>
<qty>10</qty>
</product>
<product productid="C49">
<name>Premium Cinch</name>
<price>49.00</price>
<qty>5</qty>
</product>
</order>
</orders>
Разбор файла
Document:
...
import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
public class SaveOrders extends Object {
public static void main (String args[]){
//Determine the file location
String ordersFile = "orders.xml";
//Создание XML-документа
Document ordersDoc = null;
try {
//Создание экземпляра парсера и разбор файла
DocumentBuilderFactory docbuilderfactory =
DocumentBuilderFactory.newInstance();
DocumentBuilder docbuilder =
docbuilderfactory.newDocumentBuilder();
ordersDoc = docbuilder.parse(ordersFile);
} catch (Exception e) {
System.out.println("Cannot read the orders file: "+e.getMessage());
}
Connection db = null;
...
Document для XML-документа при помощи Java API for XML Processing (JAXP). Сначала создайте DocumentBuilderFactory, затем используйте его для создания DocumentBuilder. Этот DocumentBuilder и является парсером, который берет файл orders.xml и читает каждый элемент для создания объекта Document.
Document, выполните перебор данных в цикле.
Перебор элементов
order содержит каждый заказ, а элемент product содержит каждую составляющую в заказе. Чтобы обратиться к этим данным, конструируется цикл:
...
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
public class SaveOrders extends Object {
...
Connection db = null;
try {
Class.forName(driver);
db = DriverManager.getConnection(connectURL);
//Получить корневой элемент и все элементы заказов
Element ordersRoot = ordersDoc.getDocumentElement();
NodeList orders = ordersRoot.getElementsByTagName("order");
for (int i = 0; i < orders.getLength(); i++) {
//Для каждого заказа получить элемент order
Element thisOrder = (Element)orders.item(i);
//Цикл по товарам в заказе
NodeList products = thisOrder.getElementsByTagName("product");
for (int j=0; j < products.getLength(); j++) {
}
}
} catch (ClassNotFoundException e) {
System.out.println("Error creating class: "+e.getMessage());
...
Document, вам нужно выбрать корневой элемент, который содержит все данные. Если вы имеете этот элемент, приложение может выбирать данные по имени элемента, используя getElementsByTagName(). Этот метод возвращает NodeList, который может быть использован для обращения к каждому элементу.
order содержит элементы product, повторите процесс для создания второго цикла для каждого order.
Выборка данных
getElementsByTagName() и аккуратное обращение к потомкам узла, как показано здесь:
...
Element ordersRoot = ordersDoc.getDocumentElement();
NodeList orders = ordersRoot.getElementsByTagName("order");
for (int i = 0; i < orders.getLength(); i++) {
//Для каждого заказа получить элемент order
Element thisOrder = (Element)orders.item(i);
//Получение информации заказа
String thisOrderid = thisOrder.getAttribute("orderid");
System.out.println("Order: " + thisOrderid);
//Получение информации о покупателе
String thisCustomerid = thisOrder.getElementsByTagName("customerid")
.item(0)
.getFirstChild().getNodeValue();
System.out.println("Customer: " + thisCustomerid);
//Цикл по товарам в заказе
NodeList products = thisOrder.getElementsByTagName("product");
for (int j=0; j < products.getLength(); j++) {
//Выборка каждого товара
Element thisProduct = (Element)products.item(j);
// Получение информации о товаре для
// атрибутов и дочерних элементов
String thisProductid = thisProduct.getAttribute("productid");
System.out.println(" Product: " + thisProductid);
String priceStr = thisProduct.getElementsByTagName("price")
.item(0)
.getFirstChild().getNodeValue();
System.out.println(" Price: " + priceStr);
String qtyStr = thisProduct.getElementsByTagName("qty")
.item(0)
.getFirstChild().getNodeValue();
System.out.println(" Quantity: " + qtyStr);
System.out.println();
}
}
} catch (ClassNotFoundException e) {
System.out.println("Error creating class: "+e.getMessage());
...
orderid из элемента order. Это значение просто возвращается методом getAttribute().
NodeList всех дочерних элементов с желаемыми именами, такими как customerid и productid. Таким образом вы обходите все потенциальные текстовые узлы. Но поскольку есть только один нужный для нас потомок, выбирайте item(0). Это обеспечивает нужные элементы. Чтобы получить действительное значение, нужно значение текстового узла первого потомка элемента.
Раздел 4. Изменение базы данных при помощи SQL
Создание оператора
Statement.
Statement создается Connection:
...
import java.sql.Statement;
public class SaveOrders extends Object {
...
Connection db = null;
try {
Class.forName(className);
db = DriverManager.getConnection(connectURL);
//Создание объекта Statement
Statement statement = db.createStatement();
Element ordersRoot = ordersDoc.getDocumentElement();
...
Statement выполняет SQL-оператор INSERT.
Анатомия оператора INSERT
INSERT. Рассмотрим такой пример:
insert into orders (orderid, userid) values (2333, 'nchase')
insert into orders: Показывает таблицу, в которую добавляются данные.
(orderid, userid): Хотя в некоторых ситуациях это не обязательно, но стоит перечислить столбцы в базе данных, которые будут принимать вставляемые данные, в том же порядке, в каком они появляются в фразе values.
values (2333, 'nchase'): Фраза values перечисляет фактические значения, которые должны быть добавлены в базу данных. Если в этот оператор не были включены имена столбцов, фраза values должна перечислять значения для всех столбцов таблицы в том порядке, в каком они появляются в определении таблицы в базе данных.
Statement, чтобы выполнить этот оператор.
Выполнение оператора INSERT
Statement используется один из четырех методов:
executeQuery(): Используется для выполнения оператора выборки, который будет возвращать единственный ResultSet.
executeUpdate(): Используется для выполнения оператора, который выполняет изменения в базе данных. Возвращает целое число, которое представляет число записей, затронутых изменением.
executeBatch(): Используется для посылки в база данных нескольких операторов за раз. Возвращает массив результирующих счетчиков.
execute(): Используется для более сложных операций над базой данных, которые могут возвращать более одного ResultSet.
executeUpdate():
...
String qtyStr = thisProduct.getElementsByTagName("qty")
.item(0)
.getFirstChild()
.getNodeValue();
//Создание и выполнение SQL-оператора
String sqlTxt = "insert into orders "+
"(orderid, userid, productid, unitprice, quantity) "+
"values ("+thisOrderid+", "+thisCustomerid+", '"+
thisProductid+"', "+priceStr+", "+qtyStr+")";
int results = statement.executeUpdate(sqlTxt);
}
}
} catch (ClassNotFoundException e) {
...
INSERT на базе таблицы orders и значений, взятых из файла orders.xml.
Раздел 5. Использование XML-файла отображения
Структура файла отображения
orders.xml и таблиц базы данных.
Файл отображения: информация базы данных
<?xml version="1.0"?>
<map>
<!-- Для моста JDBC-ODBC используйте
driver = "sun.jdbc.odbc.JdbcOdbcDriver"
и
url = "jdbc:odbc:orders" -->
<database url="jdbc:JDataConnect://127.0.0.1/orders"
driver="JData2_0.sql.$Driver">
<order_table>orders</order_table>
<order_orderid>orderid</order_orderid>
<order_customer>userid</order_customer>
<order_productid>productid</order_productid>
<order_quantity>quantity</order_quantity>
<order_price>unitprice</order_price>
<products_table>productOrders</products_table>
<products_productid>productid</products_productid>
<products_quantity>quantity</products_quantity>
<products_total>revenue</products_total>
</database>
...
database содержит информация о URL базы данных и об используемом драйвере. Это может быть для вас полезным, если вы не уверены в том, какой вид базы данных будет использован для хранения информации. Добавление ее в файл отображения предохраняет вас от необходимости перекомпиляции приложения при изменении баз данных.
String, поскольку они будут использоваться только для построения строк SQL. В файле отображения перечислены все переменные и таблицы или столбцы, которым они соответствуют в базе данных. Заметьте, что в существующих таблицах, orders и products есть общие имена столбцов, но файл отображения делает это неважным.
Файл отображения: информация XML
...
<xmlfile url="orders.xml">
<order>order</order>
<orderid type="attribute">orderid</orderid>
<customer type="element">customerid</customer>
<products>product</products>
<productid type="attribute">productid</productid>
<quantity type="element">qty</quantity>
<unitprice type="element">price</unitprice>
</xmlfile>
</map>
Выборка основное информации файла
...
//Создание элементов для использования их в методах
static Element databaseEl = null;
static Element xmlfileEl = null;
public static void main (String args[]){
//Обявление элементов отображения и документа
String mapFile = "map.xml";
Document mapDoc = null;
Element mapRoot = null;
Document ordersDoc = null;
try {
DocumentBuilderFactory docbuilderfactory =
DocumentBuilderFactory.newInstance();
DocumentBuilder docbuilder =
docbuilderfactory.newDocumentBuilder();
//Разбор файла отображения и получение корневого элемента
mapDoc = docbuilder.parse(отображениеFile);
mapRoot = mapDoc.getDocumentElement();
//Выборка базы данных и элементов и информации XML-файла
databaseEl = (Element)mapRoot.getElementsByTagName("database").item(0);
xmlfileEl = (Element)mapRoot.getElementsByTagName("xmlfile").item(0);
System.out.println("Reading orders file...");
String ordersFile = xmlfileEl.getAttribute("url");
ordersDoc = docbuilder.parse(ordersFile);
} catch (Exception e) {
System.out.println("Cannot read the orders file:"+e.getMessage());
}
...
databaseEl, который хранит главный элемент отображения база данных, и xmlfileEl, который хранит главный элемент отображения XML-файла, объявлены как static вне метода main, так что их можно использовать в методах, создаваемых позже.
ordersFile была первой главной переменной, устанавливаемой приложением. Теперь такой переменной является mapFile, которая указывает на действительный файл отображения, где может быть найдено значение ordersFile. Вам также понадобится создать основные XML-структуры, такие как mapRoot, вне блока try-catch, поскольку они тоже понадобятся вам позже.
try-catch сначала DocumentBuilder разбирает файл отображения, а не файл заказов, и заполняет два главных элемента отображение, databaseEl и xmlfileEl. Вы затем можете использовать xmlfileEl, чтобы определить имя файла заказов и наконец разобрать его.
Выборка информации базы данных
databaseEl:
...
} catch (Exception e) {
System.out.println("Cannot read the orders file:"+e.getMessage());
}
String driverName = databaseEl.getAttribute("driver");
String connectURL = databaseEl.getAttribute("url");
Connection db = null;
try {
Class.forName(driverName);
db = DriverManager.getConnection(connectURL);
//Создание объекта Statement
Statement statement = db.createStatement();
Element ordersRoot = ordersDoc.getDocumentElement();
NodeList orders = ordersRoot.getElementsByTagName("order");
...
Использование переменных базы данных
...
public class SaveOrders extends Object {
static Element databaseEl = null;
static Element xmlfileEl = null;
private static String getDatabaseInfo (String varname) {
//Выборока текстового узла заданного элемента
String tempStr = databaseEl.getElementsByTagName(varname)
.item(0)
.getFirstChild().getNodeValue();
return tempStr;
}
public static void main (String args[]){
...
} catch (Exception e) {
System.out.println("Cannot read the orders file:"+e.getMessage());
}
String driverName = databaseEl.getAttribute("driver");
String connectURL = databaseEl.getAttribute("url");
//Выборка имен столбцов
String order_table = getDatabaseInfo("order_table");
String order_orderid = getDatabaseInfo("order_orderid");
String order_customer = getDatabaseInfo("order_customer");
String order_productid = getDatabaseInfo("order_productid");
String order_quantity = getDatabaseInfo("order_quantity");
String order_price = getDatabaseInfo("order_price");
String products_table = getDatabaseInfo("products_table");
String products_productid = getDatabaseInfo("products_productid");
String products_quantity = getDatabaseInfo("products_quantity");
String products_total = getDatabaseInfo("products_total");
Connection db = null;
try {
...
getDatabaseInfo() просто получает дочерний элемент с заданным именем и возвращает значение его дочернего текстового узла. Используйте этот метод для заполнения других переменных базы данных, чтобы они могли быть использованы в приложении.
...
String qtyStr = thisProduct.getElementsByTagName("qty")
.item(0)
.getFirstChild().getNodeValue();
//Создание и выполнение SQL-оператора
String sqlTxt = "insert into "+order_table+
" ("+order_orderid+", "+order_customer+", "+
order_productid+", "+order_price+", "+order_quantity+") "+
"values ("+thisOrderid+", "+thisCustomerid+", '"+
thisProductid+"', "+priceStr+", "+qtyStr+")";
int results = statement.executeUpdate(sqlTxt);
...
orders.xml, но они еще не выбраны на основании файла отображения. Это немного сложнее.
Использование основных переменных XML
order и product. Это все делает основные отображения легкими в использовании:
...
try {
Class.forName(driverName);
db = DriverManager.getConnection(connectURL);
Statement statement = db.createStatement();
//Получение имен элементов product и order
String orderElName = xmlfileEl.getElementsByTagName("order").item(0)
.getFirstChild().getNodeValue();
String productElName = xmlfileEl.getElementsByTagName("products").item(0)
.getFirstChild().getNodeValue();
//Получение корневого элемента и всех элементов order
Element ordersRoot = ordersDoc.getDocumentElement();
NodeList orders = ordersRoot.getElementsByTagName(orderElName);
for (int i = 0; i < orders.getLength(); i++) {
//Для каждого заказа получить элемент order
Element thisOrder = (Element)orders.item(i);
//Получение информации заказа
String thisOrderid = thisOrder.getAttribute("orderid");
System.out.println("Order: " + thisOrderid);
//Получение информации покупателя
String thisCustomerid = thisOrder.getElementsByTagName("customerid")
.item(0)
.getFirstChild().getNodeValue();
System.out.println("Customer: " + thisCustomerid);
//Перебор всех товаров для заказа
NodeList products = thisOrder.getElementsByTagName(productElName);
for (int j=0; j < products.getLength(); j++) {
Element thisProduct = (Element)products.item(j);
String thisProductid = thisProduct.getAttribute("productid");
...
Использование элементов отображения
...
public class SaveOrders extends Object {
...
private static String getDatabaseInfo (String varname) {
String tempStr = databaseEl.getElementsByTagName(varname).item(0)
.getFirstChild().getNodeValue();
return tempStr;
}
private static String getXMLInfo (Element start, String varname) {
//Получение заданного элемента и типа
Element mapEl = (Element)xmlfileEl.getElementsByTagName(varname).item(0);
String type = mapEl.getAttribute("type");
String mappedName = mapEl.getFirstChild().getNodeValue();
//Определение, возвращается ли атрибут или текстовый узел
String tempStr = null;
if (type.equals("attribute")) {
tempStr = start.getAttribute(mappedName);
} else if (type.equals("element")) {
tempStr = start.getElementsByTagName(mappedName)
.item(0)
.getFirstChild().getNodeValue();
}
return tempStr;
}
public static void main (String args[]){
...
Element ordersRoot = ordersDoc.getDocumentElement();
NodeList orders = ordersRoot.getElementsByTagName(orderElName);
for (int i = 0; i < orders.getLength(); i++) {
Element thisOrder = (Element)orders.item(i);
//Получение информации заказа и покупателя
String thisOrderid = getXMLInfo(thisOrder, "orderid");
String thisCustomerid = getXMLInfo(thisOrder, "customer");
NodeList products = thisOrder.getElementsByTagName(productElName);
for (int j=0; j < products.getLength(); j++) {
//Выборка всех товаров
Element thisProduct = (Element)products.item(j);
//Получение информации товара
String thisProductid = getXMLInfo(thisProduct, "productid");
String priceStr = getXMLInfo(thisProduct, "unitprice");
String qtyStr = getXMLInfo(thisProduct, "quantity");
//Создание и выполнение SQL-оператора
String sqlTxt = "insert into "+order_table+
" ("+order_orderid+", "+order_customer+", "+
order_productid+", "+order_price+", "+order_quantity+") "+
"values ("+thisOrderid+", "+thisCustomerid+", '"+
thisProductid+"', "+priceStr+", "+qtyStr+")";
...
thisOrder, либо thisProduct. Передавайте этот узел в метод и, когда вы определите, ищете вы атрибут или элемент, выбирайте данный, относящиеся к этому узлу.
orders.xml. Пока вы модифицируете map.xml, приложение будет по-прежнему работать так, как ожидается.
Раздел 6. Использование транзакций и пакетных операций
Что такое транзакция?
Statement автоматически фиксирует каждый SQL-оператор, но эти установки можно изменить, используя объект Connection. Определенные действия, такие как изменение структуры базы данных или закрытие объекта Statement, также будут фиксировать транзакцию немедленно - даже без выполнения метода commit().
INSERT'ы (если в них нет проблем) или ни один из них (если что-то не так).
Группирование вставок в транзакцию
...
Connection db = null;
//Установка флага ошибки
boolean dbErrors = false;
try {
...
} catch (ClassNotFoundException e) {
System.out.println("Error creating class: "+e.getMessage());
} catch (SQLException e) {
System.out.println("Error creating connection: "+e.getMessage());
//Произошла ошибка, устанавливается флаг ошибки
dbErrors = true;
} finally {
System.out.println("Closing connections...");
try {
//Определяется, были ли ошибки и, соответственно
//фиксируется или откатывается транзакция
if (dbErrors) {
db.rollback();
} else {
db.commit();
}
db.close();
} catch (SQLException e) {
System.out.println("Can't close connection.");
}
...
boolean вне блока try-catch-finally и установите ее в false. Если все операторы завершились без ошибок, приложение получит блок finally с boolean-переменной, все еще установленной в false. В этом случае транзакция может быть зафиксирована. Если, с другой стороны, произошли ошибки, блок catch установит dbErrors в true, и приложение откатит все операции.
Включение в транзакцию файловых операций
orders.xml, когда вы закончите работу с ним.
Если это удаление почему-то будет неудачным, вы захотите откатить транзакцию.
...
} catch (SQLException e) {
System.out.println("Error creating connection: "+e.getMessage());
//Произошла ошибка, устанавливается флаг ошибки
dbErrors = true;
} catch (Exception e) {
//Display the error message and set the error flag for any errors
System.out.println(e.getMessage());
dbErrors = true;
} finally {
System.out.println("Closing connections...");
try {
//Определяется, были ли ошибки и, соответственно
//фиксируется или откатывается транзакция
if (dbErrors) {
db.rollback();
} else {
db.commit();
}
...
dbErrors в true. Вы можете также устанавливать значение этой переменной программно по любым причинам, относящимся к бизнес-логике.
Проверка поддержки транзакций
Connection:
...
import java.sql.DatabaseMetaData;
public class SaveOrders extends Object {
...
Connection db = null;
DatabaseMetaData dbmeta = null;
//Установка флага ошибок
boolean dbErrors = false;
try {
Class.forName(driverName);
db = DriverManager.getConnection(connectURL);
//Получение метаданных базы данных
dbmeta = db.getMetaData();
//Если транзакции поддерживаются, выключить autocommit
if (dbmeta.supportsTransactions()) {
db.setAutoCommit(false);
}
Statement statement = db.createStatement();
Element ordersRoot = ordersDoc.getDocumentElement();
NodeList orders = ordersRoot.getElementsByTagName(orderElName);
...
} catch (Exception e) {
System.out.println(e.getMessage());
dbErrors = true;
} finally {
System.out.println("Closing connections...");
try {
//Определяется, были ли ошибки и, соответственно,
//фиксируется или откатывается транзакция
if (dbmeta.supportsTransactions()) {
if (dbErrors) {
db.rollback();
} else {
db.commit();
}
}
db.close();
} catch (SQLException e) {
System.out.println("Can't close connection.");
}
...
Использование пакетных изменений
...
Element ordersRoot = ordersDoc.getDocumentElement();
NodeList orders = ordersRoot.getElementsByTagName(orderElName);
for (int i = 0; i < orders.getLength(); i++) {
Element thisOrder = (Element)orders.item(i);
...
NodeList products = thisOrder.getElementsByTagName(productElName);
for (int j=0; j < products.getLength(); j++) {
...
//Если транзакции поддерживаются, добавить в пакет.
//Иначе выполнить оператор
if (dbmeta.supportsBatchUpdates()) {
statement.addBatch(sqlTxt);
} else {
int results = statement.executeUpdate(sqlTxt);
}
}
//Если есть пакет, выполнить его
if (dbmeta.supportsBatchUpdates()) {
System.out.println("Executing batch ...");
statement.executeBatch();
}
}
} catch (ClassNotFoundException e) {
...
Раздел 7. Изменяемые ResultSet'ы
Использование ResultSet
ResultSet'ы.
ResultSet является объектом, который возвращает запрос к базе данных. Это структура данных, позволяющая вам перебирать ее в цикле, обращаясь к данным по индексу или по имени столбца. Например, вы можете перебирать в цикле содержимое таблицы товаров:
ResultSet resultset = statement.executeQuery("select * from productorders");
while (resultset.next()) {
System.out.print(resultset.getString("productid"));
System.out.print("|");
System.out.print(resultset.getString("quantity"));
System.out.print("|");
System.out.println(resultset.getString("revenue"));
}
ResultSet с XML.)
ResultSet. Сначала создайте его заполнением его результатами запроса. Затем перебирайте в цикле строки и выводите результаты. Используйте метод next() для продвижения на следующую запись; "указатель" исходно устанавливается перед первой записью, так что, когда вы вызываете next() в первый раз, он переходит на первую запись (ели она есть). Пока next()находит следующую запись, он будет возвращать true.
ResultSet (менее удобно, но более эффективно).
ResultSet проявляется, когда вы непосредственно изменяете базу данных.
Подготовка Statement для изменяемого ResultSet
ResultSet является объектом одноразовым, только-читаемым, читаемым только вперед. Вы получаете одну порцию данных и, если она вам нужна опять, вы должны опять запросить базу данных. Однако не обязательно следовать именно таким путем. Устанавливая параметры объекта Statement, вы можете управлять теми ResultSet'ами, которые он вырабатывает. Например:
...
Class.forName(driverName);
db = DriverManager.getConnection(connectURL);
Statement statement =
db.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE);
String orderElName = xmlfileEl.getElementsByTagName("order").item(0)
.getFirstChild().getNodeValue();
...
Statement теперь будет вырабатывать ResultSet'ы, которые могут быть изменены и которые будут подхватывать изменения, сделанные другими пользователями базы данных. Вы также можете двигаться по этому ResultSet вперед и назад.
ResultSet. Варианты такие:
TYPE_FORWARD_ONLY: Тип по умолчанию. Позволяет только двигаться вперед и на него не влияют изменения, которые делают в базе данных другие пользователи.
TYPE_SCROLL_INSENSITIVE: Позволяет двигаться вперед и назад и даже на заданную позицию, например, перейти на четвертую запись в списке или вернуться на две записи назад от текущей позиции. На него не влияют изменения, которые делают в базе данных другие пользователи.
TYPE_SCROLL_SENSITIVE: Похож на TYPE_SCROLL_INSENSITIVE, позволяет позиционироваться на записи. На этот тип влияют изменения, которые делают в базе данных другие пользователи. Если пользователь удаляет запись после того, когда запрос выполнился, эта запись будет удалена из ResultSet. Аналогично и изменения в значениях данных будут отражаться в ResultSet.
ResultSet быть изменен. Варианты такие:
CONCUR_READ_ONLY: Значение по умолчанию, задает, что ResultSet не может быть изменен.
CONCUR_UPDATABLE: Задает, что ResultSet может быть изменен.
ResultSet, то данные в базе данных еще не обязательно отразили эти изменения.
Изменение
ResultSetResultSet многом похоже на создание и изменение ResultSet в предыдущем примере:
...
import java.sql.ResultSet;
public class SaveOrders extends Object {
...
int results = statement.executeUpdate(sqlTxt);
}
//Выборка данного товара из таблицы productOrders
String updateSqlTxt = "select * from productOrders where productid = '"+
thisProductid +"'";
ResultSet updateRs = statement.executeQuery(updateSqlTxt);
//Получение нового количества и изменение ResultSet
int oldQty = updateRs.getInt("quantity");
updateRs.updateInt("quantity", getSum(oldQty, qtyStr));
double oldRev = updateRs.getDouble("revenue");
updateRs.updateInt("revenue", getRev(oldRev, qtyStr, priceStr));
//Отсылка изменений в базу данных
updateRs.updateRow();
}
}
} catch (ClassNotFoundException e) {
...
ResultSet, как обычно, но вместо чтения столбцов при помощи таких методов, как getString и getInt, измените поля ResultSet при помощи таких методов, как updateString и updateInt (в зависимости от типа в базе данных). Первым параметром является имя поля (хотя вы можете использовать также и индекс поля), а вторым - устанавливаемое значение. (getSum() и getRev() просто выполняют преобразования типа и математические вычисления.)
updateRow(). Они, однако, воздействуют на значения, хранимые в ResultSet. (Вы можете также отменить изменения прежде, чем они будут посланы в базу данных, при помощи cancelRowUpdates().)
Отображение изменений
ResultSet является легкость, с которой он может быть преобразован для использования переменных из файла отображения:
...
import java.sql.ResultSet;
public class SaveOrders extends Object {
...
int results = statement.executeUpdate(sqlTxt);
//Выборка данного товара из таблицы productOrders
String updateSqlTxt = "select * from " + products_table
+ " where " + products_productid
+ " = '" + thisProductid + "'";
ResultSet updateRs = statement.executeQuery(updateSqlTxt);
//Получение нового количества и изменение ResultSet
int oldQty = updateRs.getInt(products_quantity);
updateRs.updateInt(products_quantity, getSum(oldQty, qtyStr));
double oldRev = updateRs.getDouble(products_total);
updateRs.updateDouble(products_total, getSum(oldRev, qtyStr, priceStr));
//Отсылка изменений в базу данных
updateRs.updateRow();
}
}
} catch (ClassNotFoundException e) {
...
updateXXX().
Остается, однако, серьезная проблема: что если запись вообще не существует?
Вставка новой записи
ResultSet, называемой строкой вставки. Строка вставки является специальной строкой, созданной как временное пространство для сохранения изменений - аналогично изменениям при модификации записи - пока эти изменения не зафиксированы в базе данных.
...
String updateSqlTxt = "select * from " + products_table
+ " where " + products_productid
+ " = '" + thisProductid + "'";
ResultSet updateRs = statement.executeQuery(updateSqlTxt);
if (updateRs.next()) {
//Получение нового количества и модификация ResultSet
int oldQty = updateRs.getInt(products_quantity);
updateRs.updateInt(products_quantity, getSum(oldQty, qtyStr));
double oldRev = updateRs.getDouble(products_total);
updateRs.updateDouble(products_total, getRev(oldRev, qtyStr, priceStr));
//Отсылка изменений в базу данных
updateRs.updateRow();
} else {
//Создание новой строки вставки
updateRs.moveToInsertRow();
//Добавление данных в строку вставки
updateRs.updateInt(products_productid, getSum(0, thisProductid));
updateRs.updateInt(products_quantity, getSum(0, qtyStr));
updateRs.updateDouble(products_total, getRev(0, qtyStr, priceStr));
//Отсылка новой строки в базу данных
updateRs.insertRow();
}
...
Раздел 8. Итоги по вставке данных при помощи JDBC/XML
Резюме
ResultSet'ы, которые позволяют вам непосредственно программно изменять данные на основе предопределенных отображений.
Ресурсы
| Каталог | Индекс раздела |