Лучшее из двух миров: вызов Java из PL/SQL
Лучшее из двух миров: вызов Java из PL/SQL
1 июня 2000 г.
Стивен Ферстайн
С появлением Oracle8i разработчики могут в дополнение к PL/SQL воспользоваться преимуществами стандартного для Интернет языка программирования Java. В этой статье рассказывается с чего начать.
(Oracle Magazine, no.2,2000)
В Oracle8i для построения приложений, которые хранятся и запускаются на сервере базы данных, разработчики имеют возможность выбрать два языка программирования: PL/SQL и широко распространенный Java. Приложения PL/SQL и Java взаимодействуют напрямую внутри базы данных, логика Java-приложений может расширять существующие PL/SQL-программы, а хранимые Java-программы могут вызывать PL/SQL внутри программного обеспечения сервера (см. рисунок 1).
Эта статья является первой из серии, рассматривающей методику, связанную с использованием Java из окружения, написанного на PL/SQL. Эта первая часть подробно описывает, как конструировать простые Java-классы для доступа к базовой функциональности Java, загружать Java-классы в программы базы данных Oracle, управлять этими новыми объектами базы данных, вызывая хранимые Java-процедуры, и публиковать их для использования в PL/SQL. (Смотри “Основной Глоссарий Java” для определения некоторых терминов.)
Первым пунктом повестки дня является установка Java Development Kit (JDK) версии 1.1.5 или более поздней. JDK поставляется с Oracle8i, ее также можно скачать с web-сайта Sun Microsystems Java. Убедитесь в том, что переменная окружения CLASSPATH установлена так, чтобы компилятор Java мог определить местонахождение создаваемого вами класса, также как и классов, поставляемых Oracle.
Первоначальные сведения можно из публикуемой в этом номере заметки “Азы PL/SQL и Java”.
Доступ к Java из PL/SQL
Доступ к хранимым Java-процедурам из базы данных Oracle включает в себя шесть основных шагов.
Шаг 1: Определите, какие функциональные возможности Java вы хотите использовать в вашем приложении. Посмотрите, содержат ли существующие библиотеки Java-классов, какие-либо предопределенные Java классы, включающие методы с такими функциональными возможностями.
Шаг 2: Создайте специальный Java-класс, который включает методы, основанные на этих функциональных возможностях.
Шаг 3: Откомпилируйте этот Java-класс, проверьте его и загрузите в базу данных.
Шаг 4: Постройте PL/SQL программу-упаковщик, которая будет вызывать загруженную вами хранимую Java-процедуру.
Шаг 5: Дайте привилегии, необходимые для PL/SQL программы-упаковщика и хранимых Java-процедур, на которые она ссылается.
Шаг 6: Вызовите PL/SQL программу.
Для того, чтобы продемонстрировать эти действия и представить технологию, которая вам потребуется для выполнения работы, я воспользуюсь очень простым примером. Он заключается в следующем: я хочу иметь возможность удалять файлы операционной системы из PL/SQL-программы. До появления Oracle8 версии 8.1 и поддержки Java возможны были только следующие варианты:
В Oracle7 Release 7.3 можно было посылать сообщение в канал базы данных, затем программа C-listener перехватывала сообщение ("Delete file X") и выполняла всю работу.
В Oracle8 Release 8.0 можно было создать библиотеку, указывающую на динамически подключаемую библиотеку C (DLL) или разделяемую библиотеку. Затем из PL/SQL можно было вызвать программу этой библиотеки для удаления файла.
Технология канала удобна, но это искусственный прием. Внешнепроцедурный подход, хотя и является лучшим решением, но это не такой уж прямой способ, особенно, если вы не знаете языка С. С другой стороны, Java содержит предопределенные классы, которые предлагают мощные, простые в использовании интерфейсы программирования приложений (APIs) для широкого спектра функциональных возможностей, включая файловый ввод/вывод.
Шаг1. Поиск необходимых предопределенных функциональных возможностей Java.
Java включает сотни предопределенных классов, Java API, которые скомпонованы в пакеты (каждый находится в одноименной директории) родственных классов. Библиотеки Java-классов содержат методы, которые программисты могут расширять и включать в свои программы. Например, пакет java.applet включает класс Applet, который программисты могут расширить, чтобы создать свои апплеты. Держа в руках “Руководство по Основным Java классам” Марка Гранда и Джонатана Кнудсена (Java Fundamental Classes Reference, Mark Grand, Jonathan Knudsen), я искал в указателе раздел File. На глаза попалась статья "File class", и я поспешил к странице 161. (Примечание: в качестве руководства можно также использовать документацию по JDK). Там я нашел сведения о классе java.io.File, из которых следовало, что класс “предоставляет набор методов для получения информации о файлах и директориях”. К счастью, этот класс позволяет не только получить информацию, он содержит также методы для удаления и переименования файлов, создания директорий и тому подобное. Я нашел то, что искал. Вот фрагмент API, предлагаемый классом File:
public class java.io.File {
public boolean delete();
public boolean mkdir ();
}
Другими словами, я могу вызвать булевый Java-метод для удаления требуемого файла. Если метод удаляет файл, то он возвращает TRUE, иначе он возвращает FALSE.
Хранимые процедуры Java
Рисунок 1: Хранимые процедуры являются Java-методами, опубликованными в SQL и хранимыми в базе данных Oracle для общего пользования. Для публикации Java-методов, нужно написать спецификации вызовов (сокращенно call specs), которые отображают имя, типы параметров и возвращаемый тип в их SQL-дубликаты. Клиентские приложения могут вызывать хранимые Java-процедуры также, как они вызывают хранимые процедуры PL/SQL.
ШАГ 2. Построение пользовательского Java-класса
Вы можете удивиться, почему необходимо строить другой Java-класс на базе класса File. Почему нельзя просто вызвать File.delete напрямую из программы-упаковщика? На это существуют две причины:
Java-метод, почти во всех случаях (кроме статических методов и методов класса), выполняется из специального объекта, создаваемого из класса. Вы не можете создать (instantiate) Java-объект из PL/SQL и затем вызвать метод для этого объекта.
Типы данных в Java и PL/SQL не совпадают друг с другом напрямую. Например, вы не можете передать булевый тип данных Java напрямую в булевый тип данных PL/SQL.
Следовательно, вам необходимо построить новый класс, который будет:
Создавать объект из класса File
Вызывать метод delete для этого объекта
Возвращать значение, которое сможет интерпретировать PL/SQL
Ниже приводится простой класс, который я создал, чтобы использовать преимущества метода File.delete:
import java.io.File;
public class JDelete {
public static int delete (String fileName) {
File myFile = new File (fileName);
boolean retval = myFile.delete();
if (retval) return 1; else return 0;
}//method delete
}//class Jdelete
Метод JDelete.delete просто создает пустой объект File для заданного имени файла, так что я могу вызвать метод delete для этого файла. Объявляя метод статическим, я делаю его доступным, избегая необходимости создавать объект: статические методы связываются с классами, а не с объектами этих классов.
Этот класс подчеркивает некоторые важные отличия между Java и PL/SQL:
Java не имеет операторов BEGIN и END для блоков, циклов и операторов условия. Вместо этого, нужно использовать открывающуюся скобку ({) для того, чтобы начать блок связанного кода и закрывающуюся скобку (}), чтобы закрыть его.
Java чувствительна к регистру, что означает, что if вовсе не то же самое, что IF или If или iF.
Оператор присваивания Java – это просто знак “равно” (=), в отличие от сложного символа (:=) в PL/SQL.
При вызове метода, не имеющего аргумента (такого как метод delete в этом примере), необходимо, тем не менее, указывать открывающиеся и закрывающиеся скобки; в противном случае компилятор Java будет пытаться интерпретировать метод как член класса или структуру данных.
ШАГ 3. Компиляция, тестирование и загрузка класса в базу данных Oracle
Для компиляции написанного мной класса, мне нужно перейти в директорию, в которой расположен этот класс, и выполнить из командной строки команду javac, используя имя класса в качестве аргумента:
D:Java> javac JDelete.java
Теперь имеет смысл проверить метод перед загрузкой его в базу данных. Всегда лучше создать и сразу проверить. Java предоставляет простой способ, чтобы сделать это: метод main. Если вы предусмотрите в вашем классе метод void, вызывающий main, и дадите ему корректный список аргументов, вы сможете затем вызвать класс, и его код выполнится. Добавьте метод main к JDelete, как показано ниже:
public class JDelete {
public static int delete
public static void main (String args[]) {
System.out.println (delete(args[0]));
}//method main
}//class JDelete
Иными словами, метод вызывает delete для первого значения, переданного классу, и затем отображает возвращаемое значение. Теперь нужно перекомпилировать класс и запустить его (этот пример запущен из окна DOS):
D:Java>javac JDelete.java
D:Java>java JDelete c:tempte_employ.pks
1
D:Java>java JDelete c:tempte_employ.pks
0
Обратите внимание, что при первом вызове JDelete, была отображена 1 (TRUE), потому что мой метод удалил файл. При повторном запуске этой команды, была отображен 0, потому что метод не смог удалить файл, который уже не существует.
Теперь, когда я убедился, что метод delete работает, я использую команду loadjava для его загрузки. loadjava является утилитой Oracle, запускаемой из командной строки операционной системы, которая загружает элементы кода Java ( классы, .jar файлы и тому подобные) в базу данных Oracle. Я загрузил класс в схему SCOTT базы данных Oracle:
D:Java>loadjava -user scott/tiger -oci8 -resolve JDelete.class
Для того, чтобы проверить, что класс действительно загрузился, я запрашиваю содержимое представления словаря данных USER_OBJECTS (см. Листинг 1).
На этом заканчиваются проблемы, связанные с характерными для Java шагами. Настало время вернуться в знакомый мир PL/SQL.
ШАГ 4. Построение PL/SQL спецификации вызова
Я хочу предоставить всем возможность присоединяться к моему экземпляру базы данных, чтобы удалять файлы из PL/SQL. Для достижения этого, я создаю спецификацию вызова (известную как call spec), внешне похожую на функцию PL/SQL, но на самом деле это всего лишь оболочка для вызова основного Java-кода:
CREATE OR REPLACE FUNCTION fDelete
(file IN VARCHAR2)
RETURN NUMBER
AS LANGUAGE JAVA
NAME 'JDelete.delete (java.lang.String)
return int';
/
Реализация функции fDelete состоит из строк, описывающих вызов Java-метода. Список параметров должен соответствовать параметрам метода, но вместо каждого параметра нужно указывать полное точное название типа данных. (Java не использует глобальные переменные; каждое поле и метод привязаны к классу, а каждый класс является частью пакета. Такая иерархия обеспечивает механизм полного составного имени и предотвращает возникновение противоречий в пространстве имен.) В данном случае это означает, что я не могу просто набрать String, но вместо этого должен добавить полное имя пакета, содержащего класс String. В предложении RETURN просто указано int для целых. int это один из элементарных типов данных Java, а не класс, следовательно, это его полная спецификация.
ШАГ 5. Создание привилегий базы данных Oracle
Oracle8i предлагает две новых роли для поддержки безопасности Java. Многие операции, ориентированные на Java, не нуждаются в этих ролях, но если нужно взаимодействовать с операционной системой, например, обращаться к файлам операционной системы или изменять их, необходимо иметь роль Javasyspriv или Javauservpriv. Предоставление этой роли ничем не отличается от предоставления любой другой роли базы данных. Например, для того, чтобы SCOTT мог выполнять любую связанную с Java операцию, нужно выполнить следующую команду из-под учетной записи SYSDBA:
GRANT JAVASYSPRIV TO SCOTT;
Если вы хотите наложить некоторые ограничения на то, что пользователь может делать с помощью Java, то вместо этого нужно выполнить такую команду:
GRANT JAVAUSERPRIV TO SCOTT;
Для создания или удаления файла с использованием Java, необходимо иметь роль JAVASYSPRIV; для чтения или записи файла достаточно иметь роль JAVAUSERPRIV.
При инициализации JServer виртуальной Java-машины, устанавливается экземпляр Java-класса Java Security Manager (Менеджер Безопасности Java) java.lang .SecurityManager. Каждый пользователь базы данных имеет динамический идентификатор (ID), который соответствует владельцу сессии, когда пользователь обращается к Java методам из PL/SQL. Если пользователь, не имеющий достаточных привилегий, попытается выполнить операцию, то виртуальная Java-машина возбудит исключение java.lang.SecurityException (аналогично концепции возбуждения исключений в PL/SQL). В SQL*Plus вы увидите следующее:
ORA-29532: Java call terminated by uncaught Java exception: java.lang.SecurityException
При запуске Java-методов внутри базы данных, могут возникать различные проблемы, связанные с безопасностью, особенно, когда программа взаимодействует с файловой системой на стороне сервера или другими ресурсами операционной системы. Сервер базы данных Oracle следует одному из двух правил для проверки безопасности перед выполнением операций ввода/вывода:
Если динамическому идентификатору предоставлена привилегия JAVASYSPRIV, то Security Manager разрешает выполнение операции;
Если динамическому идентификатору предоставлена привилегия JAVAUSERPRIV, то Security Manager следует тем же правилам, которые используются PL/SQL-пакетом UTL_FILE для определения, является ли операция разрешенной, которые гласят, что файл должен находиться в директории, указанной в параметре UTL_FILE_DIR в файле инициализации базы данных.
ШАГ 6. Вызов программы PL/SQL
Теперь компоненты Java находятся на своих местах, необходимые привилегии предоставлены, я компилирую PL/SQL функцию и затем выполняю волшебный, ранее трудный трюк:
SQL> @fdelete.sf
Function created.
Input truncated to 12 characters
SQL> exec DBMS_OUTPUT.PUT_LINE (
fdelete('c:tempte_employee.pkb'))
1
SQL> exec DBMS_OUTPUT.PUT_LINE (
fdelete('c:tempte_employee.pkb'))
0
Вы можете также построить утилиты на базе этой функции. Например, вы можете создать процедуру, которая удаляет все файлы в строках вложенной таблицы. Или еще лучше, вы можете написать процедуру, которая принимает на вход имя директории и фильтр (например, “все файлы похожие на *.tmp”) и удаляет все файлы из директории, которые проходят фильтр.
Взгляд в будущее
Если вы никогда прежде не использовали Java, я надеюсь, что теперь вы скажете себе: “Эй, это легче, чем я предполагал!” В Java имеется гораздо больше возможностей, чем я затронул в этой статье, но пример JDelete должен дать вам чувство уверенности в том, что использование Java возможно. Встраивание Java в приложения PL/SQL может, конечно, быть гораздо более сложным, в зависимости от того, что именно вы собираетесь делать. Я рассмотрю некоторые из этих проблем в следующей статье этой серии.
Стивен Ферстайн создает PL/SQL утилиты разработчика для RevealNet, Inc. (www.revealnet.com), а также консультирует и обучает через PL/Solutions (www.plsolutions.com). Он является автором книги “Oracle PL/SQL Programming Guide to Oracle8i Features” и других книг издательства O'Reilly & Associates.
Основной Глоссарий Java
Класс (class) Строительный блок Java-приложений, класс является группой элементов данных (членов), с которыми связаны программы (методы), выполняющие операции над этими данными. Пакет PL/SQL в чем-то похож на класс, за исключением того, что вы не можете создавать отдельный объект из пакета, поэтому он больше похож на статический Java-класс.
Типы данных (datatypes) Все в Java определяется через класс, кроме элементарных типов данных. Так, String является классом, поэтому когда вы объявляете переменную типа String, вы создаете объект, основанный на этом классе. Элементарные типы данных Java включают четыре типа целых, а также boolean, char, double, и float.
Реализация (instantiation) Для создания частного экземпляра из более общей структуры, нужно объявить объект как экземпляр или реализацию класса. В языке PL/SQL вы можете объявить тип запись(TYPE) и затем объявить экземпляр записи, основанный на этом типе.
Член (member) Класс может содержать любое количество переменных, методов и других классов (классов таких как в Java 1.1), которые все вместе называются членами класса. Класс может быть определен внутри своих членов, которые содержат информацию о классе в целом (статические члены) или о реализациях объектов этого класса. Члены класса похожи на переменные уровня пакета в PL/SQL. Однако, каждый раз когда вы создаете объект из класса, вы получаете новый набор членов. Пакет имеет только одну реализацию своих данных во время сессии базы данных Oracle.
Метод (method) Метод – это именованная группа операторов языка программирования Java, которые связаны с определенным классом и которые могут быть вызваны для объектов, являющихся реализациями класса. В Java вы можете определить статические методы (также называемые методами класса), которые могут быть доступны напрямую из класса без создания объекта. Метод main также всегда объявляется как статический.
Объект (object) Известный также как экземпляр, объект – это элемент исполняемого кода, который особым образом реализует основную структуру класса. Класс является общим шаблоном типов (sorts), предоставляющим структуру и правила для этой структуры. В общем смысле объект – это элемент данных, которым вы манипулируете в вашем приложении.
Пакет (package) Пакет – это элемент языка Java, используемый для группировки родственных классов под общим именем. Все пакеты в Java API сгруппированы в один пакет, называемый java. Внутри пакета java находятся другие пакеты, сгруппированные по функциональным возможностям, например, пакет java.util содержит основные классы утилит.
Ресурсы Java
Программированию на Java посвящены сотни книг. Вот некоторые из моих фаворитов:
“Изучение Java”, Патрик Нимейер, Джошуа Пек (Exploring Java, Patrick Niemeyer, Joshua Peck; ISBN 1565922719, O'Reilly & Associates, 1997). Добротный вводный курс по синтаксису и концепциям.
“Руководство по основным классам Java”, Марк Гранд и Джонатан Кнудсен (Java Fundamental Classes Reference, Mark Grand, Jonathan Knudsen; ISBN 1565922417, O'Reilly & Associates, 1997). Полное руководство пользователя по основным классам Java 1.1.
“Язык программирования Java”, второе издание, Кен Арнольд, Джеймс Гослинг (The Java Programming Language, Ken Arnold, James Gosling ISBN 0201310066, Addison-Wesley, 1997). Джеймс Гослинг является создателем Java, поэтому естественно предполагать, что книга окажется полезной. Так и есть. Написанная простым понятным языком она предоставляет серьезные основные знания о языке
“Размышления в Java”, Брюс Экел (Thinking in Java, Bruce Eckel ISBN 0136597238, Prentice Hall, 1998). Легко читаемый и творческий подход к объяснению объектно-ориентированных концепций.
Всю документацию по платформе Java можно скачать с сайта Sun Microsystems: http://java.sun.com/docs/
Листинг 1
Для проверки, существует ли класс и его методы в базе данных
запросим содержимое представления словаря данных USER_OBJECTS,
используя следующий запрос:
SELECT object_name, object_type, status, timestamp
FROM user_objects
WHERE (object_name NOT LIKE 'SYS_%'
AND object_name NOT LIKE 'CREATE$%'
AND object_name NOT LIKE 'JAVA$%'
AND object_name NOT LIKE 'LOADLOB%')
AND object_type LIKE 'JAVA %'
ORDER BY object_type, object_name;
Вот результат выполнения запроса:
Object Name Object Type Status Timestamp
-----------------------------------------------------------------------
Hello JAVA CLASS VALID 1999-05-19:16:42
JDelete JAVA CLASS VALID 1999-06-07:13:20
JFile2 JAVA CLASS VALID 1999-05-26:17:07
|