Необходимо выполнить хранимую процедуру на JAVA (на PL/SQL SAX-парсера
нет). Результатом ее работы будут три коллекции объектов. Чтобы не усложнять,
в качестве примера приведу упрощенный вариант на примере одной коллекции,
который ясно демонстрирует возникшую проблему. Элементом коллекции является
объект с двумя полями - числовым и строкой. Вызов идет из PL/SQL. Но между
вызовом и возвратом данных проходит 5-10 секунд (для коллекции размером 50000
элементов).
1.
2.
3.
4.
5.
6.
7.
8.
-- Создаю тестовый вариант объекта и коллекции
create or replace type testObj as object
(
id number( 16 ),
value varchar2( 30 )
);
create or replace type testObjs as table of TestObj;
С использованием JPublisher генерирую классы-оболочки для объекта и
коллекции.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
// для объекта testobj
package pack_new;
import java.sql.SQLException;
import java.sql.Connection;
import oracle.jdbc.OracleTypes;
import oracle.sql.ORAData;
import oracle.sql.ORADataFactory;
import oracle.sql.Datum;
import oracle.sql.STRUCT;
import oracle.jpub.runtime.MutableStruct;
public class testobj implements ORAData, ORADataFactory
{
public static final String _SQL_NAME = "TESTOBJ";
public static final int _SQL_TYPECODE = OracleTypes.STRUCT;
protected MutableStruct _struct;
private static int [] _sqlType = { 2 , 12 };
private static ORADataFactory[] _factory = new ORADataFactory[ 2 ];
protected static final testobj _testobjFactory = new testobj(false);
public static ORADataFactory getORADataFactory()
{ return _testobjFactory; }
/* constructor */
protected testobj( boolean init)
{ if (init) _struct = new MutableStruct( new Object[ 2 ], _sqlType, _factory); }
public testobj()
{ this (true); }
public testobj(oracle.sql.NUMBER id, oracle.sql. CHAR value) throws SQLException
{ this (true);
setId(id);
setValue(value);
}
/* ORAData interface */
public Datum toDatum(Connection c) throws SQLException
{
return _struct.toDatum(c, _SQL_NAME);
}
/* ORADataFactory interface */
public ORAData create(Datum d, int sqlType) throws SQLException
{ return create( null , d, sqlType); }
protected ORAData create(testobj o, Datum d, int sqlType) throws SQLException
{
if (d == null ) return null ;
if (o == null ) o = new testobj(false);
o._struct = new MutableStruct((STRUCT) d, _sqlType, _factory);
return o;
}
/* accessor methods */
public oracle.sql.NUMBER getId() throws SQLException
{ return (oracle.sql.NUMBER) _struct.getOracleAttribute( 0 ); }
public void setId(oracle.sql.NUMBER id) throws SQLException
{ _struct.setOracleAttribute( 0 , id); }
public oracle.sql. CHAR getValue() throws SQLException
{ return (oracle.sql. CHAR ) _struct.getOracleAttribute( 1 ); }
public void setValue(oracle.sql. CHAR value) throws SQLException
{ _struct.setOracleAttribute( 1 , value); }
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
// для коллекции testobjs
package pack_new;
import java.sql.SQLException;
import java.sql.Connection;
import oracle.jdbc.OracleTypes;
import oracle.sql.ORAData;
import oracle.sql.ORADataFactory;
import oracle.sql.Datum;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
import oracle.jpub.runtime.MutableArray;
public class testobjs implements ORAData, ORADataFactory
{
public static final String _SQL_NAME = "TESTOBJS";
public static final int _SQL_TYPECODE = OracleTypes.ARRAY;
MutableArray _array;
private static final testobjs _testobjsFactory = new testobjs();
public static ORADataFactory getORADataFactory()
{ return _testobjsFactory; }
/* constructors */
public testobjs()
{
this ((testobj[]) null );
}
public testobjs(testobj[] a)
{
_array = new MutableArray( 2002 , a, testobj.getORADataFactory());
}
/* ORAData interface */
public Datum toDatum(Connection c) throws SQLException
{
return _array.toDatum(c, _SQL_NAME);
}
/* ORADataFactory interface */
public ORAData create(Datum d, int sqlType) throws SQLException
{
if (d == null ) return null ;
testobjs a = new testobjs();
a._array = new MutableArray( 2002 , (ARRAY) d, testobj.getORADataFactory());
return a;
}
public int length() throws SQLException
{
return _array.length();
}
public int getBaseType() throws SQLException
{
return _array.getBaseType();
}
public String getBaseTypeName() throws SQLException
{
return _array.getBaseTypeName();
}
public ArrayDescriptor getDescriptor() throws SQLException
{
return _array.getDescriptor();
}
/* array accessor methods */
public testobj[] getArray() throws SQLException
{
return (testobj[]) _array.getObjectArray(
new testobj[_array.length()]);
}
public void setArray(testobj[] a) throws SQLException
{
_array.setObjectArray(a);
}
public testobj[] getArray( long index, int count) throws SQLException
{
return (testobj[]) _array.getObjectArray(index,
new testobj[_array.sliceLength(index, count)]);
}
public void setArray(testobj[] a, long index) throws SQLException
{
_array.setObjectArray(a, index);
}
public testobj getElement( long index) throws SQLException
{
return (testobj) _array.getObjectElement(index);
}
public void setElement(testobj a, long index) throws SQLException
{
_array.setObjectElement(a, index);
}
}
Теперь пишу основной код хранимой процедуры для возврата коллекции при
вызове из PL/SQL
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
// основной код
package pack_new;
import oracle.sql.CharacterSet;
import java.sql.SQLException;
public class stored
{
public static void call(testobjs[] objs)
{
// засекаем время исполнения
long tm = System.currentTimeMillis();
// подготавливаем массив объектов
testobj[] obj = new testobj[ 50000 ];
CharacterSet chs = CharacterSet.make(CharacterSet.AL16UTF16_CHARSET);
try
{
// заполняем коллекцию
for ( int i = 0 ; i < 50000 ; i++)
{
oracle.sql.NUMBER id = new oracle.sql.NUMBER(i);
oracle.sql. CHAR value = new oracle.sql. CHAR ("text_" + i, chs);
obj[i] = new testobj(id, value);
}
// если ссылка пришла нулевая/ненулевая
if (objs[ 0 ] == null )
objs[ 0 ] = new testobjs(obj);
else
objs[ 0 ].setArray(obj);
}
catch (SQLException e)
{
e.printStackTrace();
}
// время выполнения в USER TRACE
System.out.println(( double ) (System.currentTimeMillis() - tm)/ 1000 );
}
}
Компилирую и загружаю пакет loadjava-утилитой и создаю оболочку вызова
1.
2.
3.
-- создаю класс-оболочку для вызова
create or replace procedure TestJava(p_objs out nocopy testobjs)
as language java name 'pack_new.stored.call(pack_new.testobjs[])';
Тестирую вызов
1.
2.
3.
4.
5.
6.
-- тест JAVA
declare
p_objs TestObjs;
begin
TestJava(p_objs);
end;
Общее время выполнения варьируется от 5 до 10 секунд (ну пусть даже 5).
Формирую коллекцию в PL/SQL
1.
2.
3.
4.
5.
6.
7.
8.
9.
-- тест PL/SQL
declare
p_objs TestObjs := TestObjs();
begin
for i in 1 .. 50000 loop
p_objs.extend;
p_objs(i) := TestObj(i, 'Text' || i);
end loop;
end;
Общее время выполнения 0.188 секунды.
Смотрю USER TRACE FILE в Oracle. Время формирования заняло 1.5 секунды
(что тоже не мало, хотелось бы 0.188). Остальное время (5 - 1.5 = 3.5)
заняли непонятные преобразования при возврате данных. Хотя в документации
JDBC for Oracle утверждается, что самую лучшую производительность дают
интерфейсы ORAData, ORADataFactory и использование типов oracle.sql.XXXXX,
которые не требуют внутренних преобразований. Я не знаю JAVA (это мой
первый опыт тесной работы с ней), может кто подскажет в чем проблема и
предложит решение. Производительность мне нужна близкая к той, что показывает
код на PL/SQL (~ 0.188).