Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Java Новый топик    Ответить
 FAQ: Java & Oracle, передача-прием массивов.  [new]
Denis Popov
Member

Откуда: Санкт-Петербург
Сообщений: 7841
Взято с Metalink.

При работе через THIN-драйвер отсутствует возможность передачи -приема pl/sql таблиц Oracle. Но можно воспользоваться оракловыми объектными типами.

Код, который необходимо выполнить на сттороне Oracle:

create or replace type rectype as object (
    cnumber   number (9)
  , cvarchar  varchar2 (32 char)
  , cdate     date
)
/
create or replace type rectab as table of rectype;
/
create or replace package iostructarray as
  procedure testproc (
      iorec in out rectab
    , orec out rectab
  );
end;
/
create or replace package body iostructarray as
  procedure testproc (
      iorec in out rectab
    , orec out rectab
  ) is
  begin
    -- Копирование inout параметра в out и изменение inout параметра.
    orec := iorec;
    for i in 1 .. iorec.count loop
      iorec (i).cnumber := -orec (i).cnumber;
      iorec (i).cvarchar := 'New-' || orec (i).cvarchar;
      iorec (i).cdate := orec (i).cdate + 1 + 12 / 24 + 34 / (24 * 60) + 56 / (24 * 60 * 60);
    end loop;
  end;
end;
/

Демонстрация работы с массивами:

import oracle.jdbc.driver.OracleCallableStatement;
import oracle.jdbc.driver.OracleTypes;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;

public class CallableStructArray {

  private static final String username = "hall";
  private static final String password = "h";
  private static final String URL = "jdbc:oracle:thin:@192.168.170.90:1521:CVPROC";

  public static void main(String[] args)
    throws SQLException {
    DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
    // Коннект к Oracle.
    Connection con = DriverManager.getConnection(URL, username, password);
    // Определяем два дескриптора, для ARRAY TYPE и для OBJECT TYPE.
    StructDescriptor structDesc = StructDescriptor.createDescriptor("RECTYPE", con);
    ArrayDescriptor arrayDesc = ArrayDescriptor.createDescriptor("RECTAB", con);

    // Инициализация коллекции для объектов STRUCT.
    ArrayList pArrayList = new ArrayList();

    // Объявление массива для значений.
    // Размерность его равна количеству атрибутов в объектном типе RECTYPE,
    // присвоение значений будет идти в порядке объявления в объектном типе.
    Object[] pRecord = new Object[3];
    // Определение значений и создание объекта STRUCT.
    pRecord[0] = new Integer(1);
    pRecord[1] = "Первый";
    pRecord[2] = new Timestamp(Calendar.getInstance().getTimeInMillis());
    pArrayList.add(new STRUCT(structDesc, con, pRecord));

    pRecord[0] = new Integer(2);
    pRecord[1] = "Второй";
    pRecord[2] = new Timestamp(Calendar.getInstance().getTimeInMillis());
    pArrayList.add(new STRUCT(structDesc, con, pRecord));

    // Создание объекта ARRAY.
    ARRAY pARRAY = new ARRAY(arrayDesc, con, pArrayList.toArray());
    // Объявление callable statement.
    // Он должен быть объявлен как OracleCallableStatement.
    OracleCallableStatement cst = (OracleCallableStatement) con.prepareCall(
      "{call ioStructArray.testproc(iorec=>?, orec=>?)}"
    );
    // Определение первого параметра процедуры
    cst.setARRAY(1, pARRAY);
    // Первый параметр имеет тип inout, поэтому дополнительно он регистрируется как out.
    // Note the reuse of the TYPE.
    cst.registerOutParameter(1, OracleTypes.ARRAY, "RECTAB");
    // Регистрация второго out-параметра процедуры
    cst.registerOutParameter(2, OracleTypes.ARRAY, "RECTAB");
    // Выполнение процедуры
    cst.execute();

    // Ассоциация возвращаемого массива с объектом ARRAY.
    pARRAY = cst.getARRAY(1);
    // Получение данных первого параметра.
    Object[] pObjects = (Object[]) pARRAY.getArray();
    System.out.println("Первый объект:");
    for (int i = 0; i < pObjects.length; i++) {
      pRecord = ((STRUCT) pObjects[i]).getAttributes();
      System.out.println(pRecord[0] + " | " + pRecord[1] + " | " + pRecord[2]);
    }

    // Получение данных второго параметра.
    pARRAY = cst.getARRAY(2);
    pObjects = (Object[]) pARRAY.getArray();
    System.out.println("Второй объект:");
    for (int i = 0; i < pObjects.length; i++) {
      pRecord = ((STRUCT) pObjects[i]).getAttributes();
      System.out.println(pRecord[0] + " | " + pRecord[1] + " | " + pRecord[2]);
    }
  }
}
19 янв 05, 14:37    [1257393]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
Timm
Member

Откуда: Moscow, Ё-burg
Сообщений: 3729
Можно дополнить и описать использование Custom Object Classes for Oracle Objects (завтра может напишу). Но есть глюк, от которого избавиться не удалось.
19 янв 05, 16:17    [1257920]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
Timm
Member

Откуда: Moscow, Ё-burg
Сообщений: 3729
Пусть имеются вышеописанные типы rectype и rectab.
SQL> desc rectab
 rectab TABLE OF RECTYPE
 Name                                                              Null?    Type
 ----------------------------------------------------------------- -------- -----------------
 CNUMBER                                                                    NUMBER(9)
 CVARCHAR                                                                   VARCHAR2(32 CHAR)
 CDATE                                                                      DATE
Для работы с этим типом как с обычным java-классом можно использовать:
1) стандартный интерфейс SQLData;
2) оракловые интерфейсы ORAData и ORADataFactory.
Опишу второй способ.
1) Для генерации java-классов, которые будут представлять оракловые типы, удобно воспользоваться утилитой JPublisher ([ORACLE_HOME]/bin/jbub.exe). Необходимо прописать следующие пути: [ORACLE_HOME]/jdbc/lib/classes12.zip; [ORACLE_HOME]/sqlj/lib/runtime12.zip and [ORACLE_HOME]/sqlj/translator.zip; [ORACLE_HOME]/jdbc/lib/nls_charset12.zip
2) Создаем файл jprop.txt:
jpub.driver=oracle.jdbc.driver.OracleDriver
jpub.numbertypes=oracle
jpub.url=jdbc:oracle:thin:@localhost:1521:info
jpub.builtintypes=oracle
jpub.user=tim/t
jpub.sql=rectab

3) В командной строке:
c:\Work\code>jpub -props=jprop.txt
TIM.RECTAB
TIM.RECTYPE
В результате получаем два файла с необходимыми классами: rectab.java и Rectype.java (эстеты могут переименовать классы в RecTab и RecType соответственно:))
4)
import oracle.jdbc.driver.OracleCallableStatement;
import oracle.jdbc.driver.OracleTypes;
import oracle.sql.*;

import java.sql.*;
import java.io.*;
import java.util.Calendar;

public class CallableStructArray {

	private static final String username = "tim";
	private static final String password = "t";
	private static final String URL = "jdbc:oracle:thin:@localhost:1521:info";

	public static void main(String[] args) throws SQLException {
		try {
			System.setOut(new PrintStream(System.out, true, "cp866"));
		} catch (Exception e) {
		} //try
		DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
		Connection conn = DriverManager.getConnection(URL, username, password);

		OracleCallableStatement ocstmt = (OracleCallableStatement)conn.prepareCall(
			"BEGIN ioStructArray.testproc(?, ?); END;");

		//get character set
		int oracleId = CharacterSet.CL8MSWIN1251_CHARSET;
		CharacterSet myCharSet = CharacterSet.make(oracleId);
		
		//construct array of rectype
		Rectype[] r = new Rectype[] {
			new Rectype(new NUMBER(1), new CHAR("Первый", myCharSet),
				new DATE(new Timestamp(Calendar.getInstance().getTimeInMillis()))),
			new Rectype(new NUMBER(2), new CHAR("Второй", myCharSet),
				new DATE(new Timestamp(Calendar.getInstance().getTimeInMillis())))};
		//construct table
		rectab tab = new rectab(r);
		
		ocstmt.setORAData(1, tab);
		ocstmt.registerOutParameter(1, OracleTypes.ARRAY, "RECTAB");
		ocstmt.registerOutParameter(2, OracleTypes.ARRAY, "RECTAB");

		ocstmt.execute();

		tab = (rectab)ocstmt.getORAData(1, rectab.getORADataFactory());
		r = tab.getArray();
		for (int i = 0; i<r.length; i++) {
			System.out.println(r[i].getCnumber().intValue() + "|" +	r[i].getCvarchar() + "|"
				+ r[i].getCdate().toText("YYYY.MM.DD HH24:MI:SS", "AMERICAN_AMERICA.CL8MSWIN1251"));
		} //for

		tab = (rectab)ocstmt.getORAData(2, rectab.getORADataFactory());
		r = tab.getArray();
		for (int i = 0; i<r.length; i++) {
			System.out.println(r[i].getCnumber().intValue() + "|" + r[i].getCvarchar() + "|"
				+ r[i].getCdate().toText("YYYY.MM.DD HH24:MI:SS",  "AMERICAN_AMERICA.CL8MSWIN1251"));
		} //for

	}
};


К сообщению приложен файл (code.rar - 2Kb) cкачать
20 янв 05, 11:02    [1259665]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
Denis Popov
Member

Откуда: Санкт-Петербург
Сообщений: 7841
2Timm: я свою часть предложил в разделе FAQ, предложи свою:)
24 янв 05, 13:00    [1268415]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
А.Грасоff™
Member [заблокирован]

Откуда: ∞
Сообщений: 9778
Denis Popov
2Timm: я свою часть предложил в разделе FAQ, предложи свою:)
на всякий: фак опубликован, но, думаю, что не стоит
удалять одноименные топики (как этот, например) из форума -
яндекс и гугл пусть посетителей увеличивают :)
24 янв 05, 13:10    [1268456]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
DrewT
Member

Откуда:
Сообщений: 1
Timm
Можно дополнить и описать использование Custom Object Classes for Oracle Objects (завтра может напишу). Но есть глюк, от которого избавиться не удалось.


От этого глюка избавиться можно, если поставить orai18n.jar и classes12.jar из 10g .
Кроме того, проблемы с выводом русских букв не останется если использовать BLOB, как это показано здесь.
6 фев 05, 15:40    [1303067]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
йцукен
Guest
А подходят ли драйвера от 10g к базам на oracle 8i и 9i. У меня при их использовании вываливается ошибка

ORA-12705: invalid or unknown NLS parameter value specified

хотя установлено

set NLS_LANG=AMERICAN_AMERICA.CL8MSWIN1251
30 май 05, 13:42    [1581281]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
Denis Popov
Member

Откуда: Санкт-Петербург
Сообщений: 7841
йцукен
А подходят ли драйвера от 10g к базам на oracle 8i и 9i


У меня после наката пача Oracle 9.2.0.6 перестал работать возврат курсоров из хранимых процедур при использовании JDBC от 10g.
30 май 05, 13:53    [1581324]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
йцукен
Guest
Denis Popov
[У меня после наката пача Oracle 9.2.0.6 перестал работать возврат курсоров из хранимых процедур при использовании JDBC от 10g.


А как ты запустил клиента?
Дело в том, что он у меня вообще не поднимается с этими драйверами.
30 май 05, 14:02    [1581372]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
Denis Popov
Member

Откуда: Санкт-Петербург
Сообщений: 7841
йцукен
А как ты запустил клиента?
Дело в том, что он у меня вообще не поднимается с этими драйверами.


Запустил и все:) Ни с какой ошибкой при "запуске" (т.е. при создании соединения?) не столкнулся. М.б. из-за того, что у нас все базы созданы в юникоде, а это порой избавляет от многих проблем.
30 май 05, 14:45    [1581547]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
йцукен
Guest
Denis Popov
[Запустил и все:) Ни с какой ошибкой при "запуске" (т.е. при создании соединения?) не столкнулся. М.б. из-за того, что у нас все базы созданы в юникоде, а это порой избавляет от многих проблем.


Ну тогда конечно, у меня база в 1251
30 май 05, 14:51    [1581575]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
verter
Member

Откуда: Санкт-Петербург
Сообщений: 879
Сделал всё по написанному выше:

в java на строчке

pArrayList.add(new STRUCT(structDesc, con, pRecord));

вываливается ошибка:

java.sql.SQLException: Non supported character set: oracle-character-set-171
at oracle.gss.util.NLSError.throwSQLException(NLSError.java:46)
at oracle.sql.CharacterSetUnknown.failCharsetUnknown(CharacterSetFactoryThin.java:171)
at oracle.sql.CharacterSetUnknown.convert(CharacterSetFactoryThin.java:135)
at oracle.sql.CHAR.<init>(CHAR.java:159)
at oracle.sql.CHAR.<init>(CHAR.java:183)
at oracle.jdbc.oracore.OracleTypeCHAR.toDatum(OracleTypeCHAR.java:161)
at oracle.sql.StructDescriptor.toOracleArray(StructDescriptor.java:830)
at oracle.sql.StructDescriptor.toArray(StructDescriptor.java:1735)
at oracle.sql.STRUCT.<init>(STRUCT.java:145)


БД 8.1.7
NLS_NAME = AMERICAN_AMERICA.CL8MSWIN1251

Причём задаю для структуры не русские буквы, английские слова пишу
13 дек 05, 13:13    [2166529]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
Denis Popov
Member

Откуда: Санкт-Петербург
Сообщений: 7841
nls_charset12.zip в CLASSPATH есть?
13 дек 05, 14:25    [2166987]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
verter
Member

Откуда: Санкт-Петербург
Сообщений: 879
Да есть. Я вообще в сервлете всё это делаю, а сервлет запускается на Tomcat'е. Эту библиотеку nls_charset.zip я положил вместе с остальными в Tomcat\common\lib\
13 дек 05, 15:58    [2167548]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
verter
Member

Откуда: Санкт-Петербург
Сообщений: 879
причём, я посмотрел, что в этой библиотеке nls_charset12.zip есть класс

oracle\sql\converter\CharacterConverter00ab.class

00ab = 171

наверное который работает с этим чарсетом.

после компиляции все библиотеки правильно лежат для Томкэта:

в

%Tomcat%\webapps\Имя_приложения\WEB-INF\lib\

но всё-равно ошибка вылетает.
13 дек 05, 16:08    [2167608]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
Denis Popov
Member

Откуда: Санкт-Петербург
Сообщений: 7841
Локально тоже не работает? Что возвращает запрос?
select * from sys.props$ where name like 'NLS%CHARACTERSET';
И еще: какой именно используется JDBC-драйвер к Ораклу, имя файла?
13 дек 05, 16:29    [2167749]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
Denis Popov
Member

Откуда: Санкт-Петербург
Сообщений: 7841
Вот еще, вдруг чего подскажет:
http://forums.oracle.com/forums/search.jspa?threadID=&q=%22oracle-character-set-171%22&objID=&dateRange=all&userID=&numResults=30
13 дек 05, 16:42    [2167835]     Ответить | Цитировать Сообщить модератору
 Re: FAQ: Java & Oracle, передача-прием массивов.  [new]
verter
Member

Откуда: Санкт-Петербург
Сообщений: 879
Денис, спасибо за помощь!

решил проблему:

оказывается надо было использовать более свежую nls_charset12.zip, вернее nls_charset12.jar.

Я брал эту библиотеку для 8-ой версии оракла - не работало, взял для 9-ки - всё стало работать.
13 дек 05, 17:25    [2168079]     Ответить | Цитировать Сообщить модератору
Все форумы / Java Ответить