Oracle 10G программирование: Oracle-MS SQL Server интеграция

 

Борис Макушкин

Alba Spectrum Technologies

(095) 918-3314, (095) 918-3111, help@albaspectrum.com  

Два мира, две идеологии, два непримиримых соперника – как еще можно точнее описать два столпа RDBMS мира – Oracle Database и MS SQL Server. Горы TPC тестов, доказывающих превосходство поочередно то одной, то другой стороны. Легионы приверженцев одного и другого продукта, яростно доказывающие достоинства и не принимающие никаких аргументов, свидетельствующих о недостатках своего любимца. Инь и янь рынка SQL серверов… Возможно ли ужиться вместе двум столько разным сущностям? Еще как! И задача нашего цикла статей дать Вам некоторые подсказки по обеспечению прозрачного переноса данных между этими двумя вселенными.

Целью сегодняшней статьи является демонстрация возможностей Oracle по использованию первичных и вторичных ключей в стиле MS SQL (Guid datatype). Отличительной особенностью Oracle является свободное использование встроенной java машины – с ее помощью можно писать хранимые процедуры сколь угодно высокой сложности. Этим мы и воспользуемся в нашей работе. Итак приступим:

  1. Цель генерации GUID значений – получить уникальный ключ для однозначной идентификации сущности. Oracle предлагает для этого пользоваться механизмом sequence, но для обеспечения переносимости приложения между Oracle и MS SQL Server лучше выбрать механизм, используемый в MS SQL Server (по крайней мере, я предпочитаю такой подход).
  2. Для реализации GUID генератора мы воспользуемся уникальными значениями времени, IP-адреса и нескольких случайно выбранных случайных значений из таблицы псевдослучайных чисел. В общем случае этого достаточно, но в production systems рекомендуется использовать MAC адрес используемой сетевой карты – его можно получить с помощью C/C++ библиотеки, к которой потом осуществляется доступ через JNI интерфейс. Итак, рассмотрим реализацию GUID генератора:

 

package com.albaspectrum.util;

 

import java.security.*;

import java.util.*;

import java.io.*;

import java.net.*;

import java.rmi.server.*;

 

public class Guid {

    private String internalGuid = null;

 

    public String newGuid() throws Exception {

        setGuid(generateGuid());

 

        return getGuid();

    }

 

    public String getGuid() {

        return internalGuid;

    }

 

    public void setGuid(String assignedGuid) {

        internalGuid = assignedGuid;

    }

 

    public String generateGuid() throws Exception {

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        PrintStream printStream = new PrintStream(outputStream);

 

        MessageDigest messageDigest = MessageDigest.getInstance("MD5");

        messageDigest.update(getSourceMessage());

        byte[] byteDigest = messageDigest.digest();

       

        if (byteDigest != null) {

            for(int i = 0; i < byteDigest.length; i++)

                hexDigit(printStream, byteDigest[i]);

        } else {

            throw new Exception("Got broken GUID");

        }

 

        return (outputStream.toString());

    }

 

    private static void hexDigit(PrintStream p, byte x) {

        char c;

 

        c = (char) ((x >> 4) & 0xf);

        if (c > 9) {

            c = (char) ((c - 10) + 'a');

        } else {

            c = (char) (c + '0');

        }

        p.write(c);

 

        c = (char) (x & 0xf);

        if (c > 9) {

            c = (char)((c - 10) + 'a');

        } else {

            c = (char)(c + '0');

        }

        p.write(c);

    }

 

    protected byte[] getSourceMessage() throws Exception {

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        PrintStream printStream = new PrintStream(outputStream);

       

        printStream.print(getSourceString());

       

        return outputStream.toByteArray();

    }

 

    protected String getSourceString() throws Exception {

        StringBuffer resultString = new StringBuffer(InetAddress.getLocalHost().toString());

 

        resultString.append("; ").append(new Date().toString());

        resultString.append("; ").append(new Integer(new Random().nextInt()).toString());

        resultString.append("; ").append(new Integer(new Random().nextInt()).toString());

        resultString.append("; ").append(new Integer(new Random().nextInt()).toString());

        resultString.append("; ").append(new Integer(new Random().nextInt()).toString());

          resultString.append("; ").append(new UID().toString());

 

        return resultString.toString();

    }

 

    public static String getMsStyleGuid(String guid) {

          String formattedGuid = "{" + guid.substring(0, 8) + "-" + guid.substring(8, 12) + "-" +

                 guid.substring(12, 16) + "-" + guid.substring(16, 20) + "-" + guid.substring(20, 32) + "}";

 

          return formattedGuid;

    }

 

    public static String getGuidForOracle() throws Exception {

          Guid guid = new Guid();

          String rawGuid = guid.newGuid();

 

          return getMsStyleGuid(rawGuid);

    }

}

 

  1. В данном классе нас интересует последний метод – getGuidForOracle(). Именно он выдает привычную для нас форму представления GUID значения.
  2. Соберем наш класс при помощи javac компилятора, дабы убедиться в отсутствии ошибок. Для тестирования можно воспользоваться небольшой программой:

package com.albaspectrum.util;

 

public class TestGuid {

   public static void main(String args[]) throws Exception {

          Guid guid = new Guid();

          String rawGuid = guid.newGuid();

 

          System.out.println("Generated GUID: " + rawGuid);

          System.out.println("Generated MS-style GUID: " + Guid.getMsStyleGuid(rawGuid));

   }

}

 

  1. Далее, перенесем файл Guid.java (в котором содержится наш генератор) на хост, где работает Oracle Database. Зарегистрируемся под пользователем oracle и загрузим наш класс в Oracle Java Machine:

oracle@vega:~/product/10gR1/bin> loadjava -u system/manager -v -resolve /tmp/Guid.java

arguments: '-u' 'system/manager' '-v' '-resolve' '/tmp/Guid.java'

creating : source com/albaspectrum/util/Guid

loading  : source com/albaspectrum/util/Guid

created  : CREATE$JAVA$LOB$TABLE

resolving: source com/albaspectrum/util/Guid

oracle@vega:~/product/10gR1/bin>

 

  1. Проверим, все ли в порядке с нашим классом – для этого в iSQL дадим команду:

SELECT * FROM user_objects WHERE object_type LIKE ‘JAVA%’

 

Обратим особое внимание на поля OBJECT_NAME и STATUS

  1. Далее нам необходимо создать PL/SQL функцию для доступа к методу getGuidForOracle:

create or replace function GET_GUID

RETURN VARCHAR2

AS LANGUAGE JAVA

NAME 'com.albaspectrum.util.Guid.getGuidForOracle() return String';

 

  1. Теперь проверим работу нашей функции:

SET SERVEROUTPUT ON

VARIABLE guid VARCHAR2(38);

CALL GET_GUID() INTO :guid;

PRINT guid

 

  1. Теперь мы можем установить значение по умолчанию для необходимых полей в наших Oracle таблицах как значение функции GET_GUID(). Это все!

 

Борис Макушкинведущий девелопер в Alba Spectrum Technologiesкомпании партнере Oracle и Microsoft Business Solutions в городах Москва, Петербург, Екатеринбург, Казань, Ростов и обслуживающей всю Российскую Федерацию (www.albaspectrum.com), Борис является Microsoft CRM SDK, C#, VB.Net, SQL, Oracle, Unix девелопером.