Rambler's Top100IT • archiv

rus / eng | Логин | Комментарий к колонке | Печать | Почта | Клуб




Колонки


Использование технологий Java в Oracle 8i

 
(Николай Зенькевич)

Хранимые процедуры Java в Oracle 8i.

Введение.

Компания Oracle давно и активно внедряет в свои технологии язык программирования Java. Революционным новаторством в этой области стал выпуск новой версии сервера базы данных Oracle 8i и Oracle Application Server. До 8i язык Java если и присутствовал в продуктах Oracle , то был как бы дополнительной возможностью к основному языку PL\SQL. Но начиная с этой версии он встал наравне, а во многом даже и впереди PL\SQL. Требуется учесть наличие гораздо более широких возможностей Java по сравнению с PL\SQL (многопоточность, динамическую загрузку классов, сетевые возможноcти и др.), а также большее число библиотек и разработчиков Java.

Язык Java был выбран не случайно. Во-первых, язык Java кроссплатформенен (существуют JDK для всех платформ поддерживаемых Oracle). Во-вторых, Java, также как и Oracle, обладает высокой степенью безопасности (Java 2 Security), что позволяет безопасно выполнять код Java внутри базы данных.

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

Следует отметить, что за богатство новых возможностей естественно приходится платить – возросшие требования к конфигурации сервера или же снижение скорости.

JServer.

Как уже отмечалось, для поддержки Java на сервера базы данных в его состав был включен JServer– сервер Java, который отвечает за загрузку java-классов в базу данных, компиляцию java кода, безопасность выполнения приложений. На рисунке изображена схема JServer:

JServer включает в свой состав виртуальную машину Java Aurora, именно она и выполняет Java код. JServer также включает в свой состав все классы JRE, причем для повышения производительности многие из них представляют собой native код. Также для повышения производительности Aurora использует JIT (just-in-time) предкомпилятор, который компилирует байт код java в native код и только затем он выполняется. Это повышает производительность в 2-10 раз, но сильно загружает систему. Также возможно подключение внешних акселераторов Java.

К особенностям Java программирования под JServer следует отнести невозможность подключения пользовательского native кода из соображений безопасности, невозможность работы с графическим выводом (можно создавать графические объекты, но нельзя их визуализировать). Для соединения с базой данных, в которой выполняется Java код, хотя и используется JDBC, но JDBC-соединение уже не сессия в базе данных, а простой внутренний канал сессии, в которой выполняется код. Такое соединение открывается практически мгновенно и работает очень быстро, кроме этого исчезла необходимость отслеживать закрытие, а также коммит и роллбэк для каждого соединения – это можно проделать в любом соединении для всей сессии. Для соединений с другой базой данных нельзя применять OCI драйвер (так как нельзя использовать пользовательский native код). Однако включена более быстрая поддержка Thin драйвера.

К проблемным моментам следует отнести нечеткое соблюдение приоритетов потоков.  Эта проблема возникла из-за большой загрузки сервера JIT предкомпилятором.

К главным особенностям выполнения Java на JServer следует отнести принцип работы JVM Aurora. JServer имеет свою JVM Aurora, однако для каждой сессии, при первом обращении к Java коду, запускается своя отдельная JVM Aurora с правами, которыми обладает пользователь открывший соединение. Каждая JVM имеет свою память и загруженные классы.  Таким образом, классы загружаться  в память для каждой сессии, однако, это не сказывается на скорости, так как все классы находятся в базе данных.

Можно условно считать, что переменная CLASSPATH установлена на корень хранилища Java объектов некоторых схем базы данных. В базе данных могут находиться не только  исходный код Java и классы, но и другие ресурсы, например сериализированные объекты или файла свойств.

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

Данная сложность работы JVM хотя и требует значительного объема памяти сервера и других ресурсов, но при этом значительно повышает безопасность выполнения Java  приложений внутри базы данных, а также упрощает сборку мусора (очистку памяти от ненужных объектов).

При окончании Java сессии соответствующая JVM уничтожается. Окончание Java сессии происходит при окончании сессии в базе данных, при тайм-ауте или при вызове:

oracle.aurora.mts.session.Session.THIS_SESSION().endSession()

пользовательским приложением.

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

Ограничение возможностей java приложений, безопасность.

Как отмечалось Java имеет гораздо больше возможностей чем PL\SQL. Следовательно, возникает необходимость ограничения этих возможностей некоторым пользователям. Каждой схеме базы данных могут быть присвоены персональные разрешения на выполнение определенных действий в Java приложениях. Для запуска Java схеме не требуется особых прав.

Для проверки прав Java используется принцип «что не разрешено,то запрещено». Для раздачи прав на выполнения какой-либо операции используется два метода: выдача Java-роли схеме или прямая выдача конкретного права одного из типов разрешений.

  • Список основных Java-ролей:
  • JAVA_ADMIN – никакие права не проверяются – все разрешено. Может раздавать права другим пользователям.
  • JAVAUSERPRIV – малые права, включая чтение свойств JVM.
  • JAVASYSPRIV – большие права, включая изменения защищенных пакетов.
  • Вот список типов возможных разрешений:
  • java.util.PropertyPermission
  • java.io.SerializablePermission
  • java.io.FilePermission
  • java.net.NetPermission
  • java.net.SocketPermission
  • java.lang.RuntimePermission
  • java.lang.reflect.ReflectPermission
  • java.security.SecurityPermission
  • oracle.aurora.rdbms.security.PolicyTablePermission
  • oracle.aurora.security.JServerPermission

Таким образом, можно выдать права на чтение/запись в конкретный файл или каталог, открытие сокета, сериализацию объекта,  изменение свойств JVM, чтение свойств класса, раздачу Java прав и др.

Раздача и отбор прав Java прав может производиться двумя способами, через SQL и Java.

Для раздачи Java прав через SQL существует специальный пакет dbms_java. Он содержит несколько десятков процедур. Вот две основные из них:

restrict_permission( grantee varchar2, permission_type varchar2,permission_name varchar2,permission_action varchar2)
grant_permission( grantee varchar2, permission_type varchar2,permission_name varchar2,permission_action varchar2)

Grantee – схема, которой выдается разрешение. Permission_type – тип разрешения. Permission_name – имя разрешения. Permission_action – разрешаемые действия.

Пример выдачи права схеме WWW на запись и чтение в каталоге D:\www, кроме файла password.txt.

call dbms_java.grant_permission('WWW','java.io.FilePermission','D:\www','read,write');
call dbms_java.restrict_permission(’WWW’,’java.io.FilePermission’,'D:\www \password.txt',’read,write’);
commit;

Требуется помнить, что после сеанса раздачи и отбора прав, необходимо выполнять коммит, так как все изменения происходят в таблице SYS.JAVA$POLICY$ (там можно посмотреть розданные права, однако лучше напрямую не производить изменений).

Для раздачи прав через Java существует класс с аналогичными статическими методами oracle.aurora.rdbms.security.PolicyTableManager.

Существует возможность создавать свои типы разрешений и сами разрешения.

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

Создание хранимой Java процедуры обязательно происходит по следующей схеме.

  • Разработка кода может происходить в любой удобной для вас среде. Загрузка кода может производить несколькими способами:
  • Через ручное создание Java Source оператором SQL.
  • Через утилиту loadjava (с предварительной компиляцией (загружается .class) или без (загружается .java)).

Публикация представляет собой создание простой (PL\SQL) хранимой процедуры, которая ссылается на статический метод загруженного класса.

Процесс разработки рассматривать не будем. Остановимся лишь на результате: результатом разработки должен стать исходный код Java класса (или нескольких классов), который должен содержать хотя бы один статический метод для дальнейшей публикации. Для создания хранимой процедуры желательно чтобы статический метод возвращал void, однако, это не обязательно – он может возвращать любой объект, но он будет теряться. Для создания функции необходимо, чтобы метод возвращал объект Java-типа, имеющем аналог в SQL. Таблица соответствия SQL типов – Java типам.

SQL JAVA
CHAR, NCHAR,
LONG, VARCHAR2,
NVARCHAR2
oracle.sql.CHAR
java.lang.String
java.sql.Date
java.sql.Time
java.sql.Timestamp
java.lang.Byte
java.lang.Short
java.lang.Integer
java.lang.Long
java.lang.Float
java.lang.Double
java.math.BigDecimal
byte, short, int, long, float, double
DATE oracle.sql.DATE
java.sql.Date
java.sql.Time
java.sql.Timestamp
java.lang.String
NUMBER oracle.sql.NUMBER
java.lang.Byte
java.lang.Short
java.lang.Integer
java.lang.Long
java.lang.Float
java.lang.Double
java.math.BigDecimal
byte, short, int, long, float, double
RAW, LONG RAW oracle.sql.RAW
byte[]
ROWID oracle.sql.CHAR
oracle.sql.ROWID
java.lang.String
BFILE oracle.sql.BFILE
BLOB oracle.sql.BLOB
oracle.jdbc2.Blob
CLOB, NCLOB oracle.sql.CLOB
oracle.jdbc2.Clob
OBJECT oracle.sql.STRUCT
oracle.SqljData
oracle.jdbc2.Struct
REF oracle.sql.REF
oracle.jdbc2.Ref
TABLE, VARRAY oracle.sql.ARRAY
oracle.jdbc2.Array

Исключения, вызываемые Java кодом и необрабатываемые им, успешно обрабатываются следующим способом:

begin
--Вызов java процедуры
test_proc();
EXCEPTION
when others then
--Ваш обработчик исключения
  null;
end;

Как уже сообщалось, загрузка может производиться двумя основными способами. Загрузка командой SQL CREATE JAVA может осуществляться из SQL*Plus или другой SQL оболочки. Для выполнения этой команды надо иметь системную привилегию CREATE PROCEDURE и CREATE TABLE. Существует три типа команды CREATE JAVA (CLASS, SOURCE, RESOURCE) для создания соответственно класса из поля типа LOB или JAVA SOURCE,  исходного кода Java и Java ресурса (картинки, файла свойств, файла сериализированного объекта) из поля типа LOB или текста самой команды.

Для создания кода хранимой процедуры можно использовать CREATE JAVA SOURCE - для загрузки исходного кода и дальней его компиляции JServerом или CREATE JAVA CLASS для загрузки скомпилированного класса из поля BFILE, что более сложно, так сначала класс все равно нужно загрузить через loadjava.

Синтаксис CREATE JAVA SOURCE проще объяснить на простом примере, тем более что многие опций не  применяются очень редко. Опции позволяют автоматически скомпилировать код, разрешить его использование в других схемах или же не создавать source, если его не удалось скомпилировать, установить CLASSPATH на другие схемы, а также определить от чего имени будет выполняться код: от имени и с правами вызвавшего или же создавшего.

create or replace java source named
"test_java" as  public class test_me{
  public static String getStr(){
            return "Hello from JServer !";
        }
  }

Эта команда просто создаст Java Source в текущей схеме и будет иметь в виду, что класс test_me храниться в нем, что не позволит создать класс с таким же именем в другом Java Source.

Для загрузки кода через утилиту loadjava требуется те же права, как для CREATE JAVA, а также возможность записи в таблицу JAVA$CLASS$MD5$TABLE, которая находится в схеме пользователя и хранит информацию о классах. Эта информация используется для отслеживания изменений в классах, чтобы во время перекомпилировать классы или заново не загружать старую версию при повторной загрузке.

Загрузка классов loadjava может осуществляться через BLOB поля таблиц, однако в этом нет никаких преимуществ, кроме более удобного отслеживания хранилища классов, ручные изменения которых могут привести к невосстанавливаемым потерям. Для установки ссылок на такие поля используется таблица CREATE$JAVA$LOB$TABLE.

Утилита loadjava имеет такие же опции, что и CREATE JAVA, плюс дополнительные. Такие как, строка соединения с базой данных, используемый при этом драйвер, загружать все указанные файлы или только обновленные, создавать ли public синонимы для загружаемых объектов и др. Сама утилита входит в состав клиента Oracle 8i.

Пример использования утилиты loadjava:

loadjava -user scott/tiger@FR10
-verbose test_me.class

Данная команда, выполненная в окне сеанса DOS на рабочей станции, соединится с базой данных, TNS имя которой FR10, с именем SCOTT и паролем TIGER и загрузит в схему SCOTT класс test_me, хранящийся в текущей директории, отображая лог процесса на экране (опция verbose).

Следует отметить, что возможно создание и  компиляция Java объектов в других схемах. Таким образом, можно использовать библиотечные классы, загруженные в какую-либо схему. При этом для их использования надо будет либо создать синонимы, либо разрешить их при компиляции (установить CLASSPATH на нужную схему).

Удаление объектов Java из базы данных происходит при помощи команды DROP JAVA или утилиты dropjava.

За этапом загрузки следует этап публикации. Как уже отмечалось, публикация представляет собой создание простой (PL\SQL) хранимой процедуры, которая ссылается на статический метод загруженного класса. Эта процедура может представлять собой самостоятельную хранимую процедуру или функции, процедуру или функцию в пакете или метод объекта, для создания которых, естественно, требуются соответствующие привилегии.

Синтаксис команд создания Java процедур такой же, как и при создании обычной хранимой процедуры, отличается только описание тела процедуры. Что проще пояснить на примере создания хранимой функции на основе созданного source:

create or replace function getHello return VARCHAR2
      as language java name 'test_me.getStr()
return java.lang.String';

Следует отметить, что компиляция класса test_me будет произведена автоматически при первом обращении к нему, в данном случае вызове функции getHello. Если пересоздать java source testjava и вызвать функцию getHello, то возникнет ошибка – java сессия будет прервана и запущена новая. Пример обращения к хранимой Java процедуре:

select getHello from dual;

Также следует подметить, что имя java классов используется полное: java.lang.String, хотя компилятор понял бы и сокращенное: String, но Oracle настойчиво рекомендует использовать полные имена.

Приведем пример создания хранимой процедуры с передаваемыми параметрами и возвращаемыми параметрами:

create or replace java source named "test_stored_proc" as 
import java.sql.*;
public class store_proc{
  private static String query = "";
  public static void selectData(String query,String[] answer){
  try{
  store_proc.query = query;
  Connection conn =
DriverManager.getConnection("jdbc:default:connection:");
  Statement stmt = conn.createStatement();
  ResultSet rset = stmt.executeQuery(query);
  if (rset.next()){
    answer[0] = rset.getString(1);
   }
else {
    answer[0] = "empty";
  }   
  stmt = conn.createStatement();
  stmt.executeUpdate("insert into test values('"+answer[0]+"')");
  }
  catch(Exception e){
   System.out.println("Error: "+e.toString());
  } 
}

public static void selectData(){
store_proc.selectData(store_proc.query,new String[1]);
}

}

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

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

Для работы с таблицами базы данных используется соединение по умолчанию «jdbc:default:connection:», которое, как уже говорилось, представляет внутренний канал базы данных.

Скрипт опубликования процедур:

create or replace procedure selDataQuery(query varchar2,
answer IN OUT varchar2)
as language java name 'store_proc.selectData(java.lang.String,
java.lang.String[])';

create or replace procedure selData
  as language java name 'store_proc.selectData()';

Скрипт для проверки работоспособности процедур:

delete from test;
declare
  str varchar2(200);
begin
  selDataQuery('select to_char(sysdate) from dual',str);
  dbms_output.put_line(str);
  selData();
end;
/

select * from test;

Следует обратить внимание на то, что для создания OUT параметра в Java методе пришлось передавать параметр как массив – это стандартный метод создания возвращаемых параметров в Java.

Также следует обратить внимание на обработку исключительных ситуаций (try{} – catch(Exception e){}) – сообщение об ошибке будет записано в поток System.out, который по умолчанию направлен в trace файл базы данных.

Использование SQLJ для ускорения разработки хранимых Java процедур.

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

Эта возможность получила название «Embedded SQL in Java» или SQLJ. Первое название полностью определяет сущность SQLJ: он позволяет по определенным правилам внедрить SQL код внутрь кода Java, затем транслировать его в чистый код Java(один или несколько классов) и файлы сериализованных объектов и только после этого скомпилировать их до байт-кода. Схематично это можно изобразить так:

JServer автоматически распознает SQLJ Java source и выполняет SQLJ трансляцию. Также это можно проделать и на рабочей станции (JDeveloper выполняет это автоматически при обычной компиляции).

Приведем пример использования SQLJ на сервере (клиентская использование SQLJ требует соединения с базой данных перед выполнением кода SQLJ). Следует отметить, что SQLJ может применяться для работы не только с базой данных Oracle, а также может использоваться для создания  SQLJ - JSP.

СозданиеSQLJ Java source:

create or replace java source named "test_sqlj_source" as
  public class test_sqlj{
  #sql public static iterator objectIter(String,String);
  public static String giveAll()
throws java.sql.SQLException{
   objectIter oi=null;
   String answer = "";
   String object_name = "";
   String object_type = "";
   #sql oi = {
select object_name,object_type from all_objects
where owner=user and
object_type like 'JAVA%' and object_name like
'%sqlj%' };
    while(true){
    #sql{ FETCH :oi INTO :object_name,:object_type };
    if (oi.endFetch()) break;
     answer+= object_name+" - "+object_type+"; ";


     }
   return answer;   
   }
  }

Публикация через функцию ничем не отличается от обычной:

create or replace function giveList return varchar2
  as language java name
'test_sqlj.giveAll() return java.lang.String';

Теперь сделав выборку:

select giveList from dual;

получим список имен и типов всех Java объектов задействованных в работе данного примера SQLJ.

Это был простой пример использования SQLJ. На самом деле SQLJ содержит большое число возможностей.

  • Кроме ускорения разработки Java приложений для работы с базой данных (в том числе удобство отладки) SQLJ имеет еще ряд преимуществ:
  • Проверка корректности SQL операторов в процессе трансляции.
  • Работа со сложными типами данных, которые не поддерживаются JDBC.
  • Компиляция SQL операторов.

PL\SQL против JAVA.

После всего сказанного встает вопрос: «Так что же лучше использовать, для написания хранимых процедур базы данных Oracle 8i PL\SQL или Java ?» Однозначного вопроса не дает даже сама корпорация Oracle. Но правда, дает совет: если есть необходимость создать сложный объект делающий сложные математические вычисления, работающий с сетевыми ресурсами, XML, файловой системой, LOB полями или же представляющий собой какой-либо Web объект, то лучше использовать Java и не мудрить с PL\SQL. Однако использовать Java для написания простого инсерта по срабатыванию триггера не стоит.

Список использованной литературы:

  • Oracle 8i. Product Documentation Library.
  • Б. Эккель. Философия Java. М.: Питер – 2001г.
  • М. Даконта, А. Саганич. XML и Java 2. М.: Питер – 2001г.

Об авторе
Зенькевич Николай Александрович
Год рождения: 1979
Место жительства: Республика Беларусь г.Минск
Место учебы: 5 курс факультета Радоифизики и Электроники Белорусского Государственного Университета
Место работы : НИООО "Глобо-Центр"
E-Mail : znick@tut.by
ICQ # : 106386973




Справка | Условия Copyright © 1999 — 2008, IT • archiv.
В начало | Логин | Комментарий к колонке | Поиск | Почта