2007-03-22

Oracle java tárolt eljárások, múltidézés: jdk1.3

Oracle-ben lehetséges az adatbázisszerverre feltöltött java programokat tárolt eljárásként futtatni. Ezzel a feature-rel, valamint az Oracle 10g és 9.2 ehhez kapcsolódó diszkrepanciáival találkoztam. A google-n "Oracle loadjava" kulcsszavakkal kitűnő cikkeket lehet keresni, amelyek elmagyarázzák a dolog lényegét, úgyhogy ezt nem részletezem, csak dióhéjban a lényeg:

Ha valami ok folytán olyan logika végrehajtására van szükség adatbázisban lévő adatokkal, amit tárolt eljárással nem lehet megoldani (pl. bonyolultabb számítások), a külső számítás használata pedig előnytelen, a java tárolt eljárás hívása egy jó módszer lehet. A felhasznált java osztályokat vagy jar fájlokat először be kell tölteni az adatbázisba az adatbázisszerver bin könyvtárában lévő loadjava parancssori eszközzel vagy máshogy (google). A java osztályokon belül a szokásos módon JDBC-n keresztül érhető el az adatbázis és a teljes standard java API használható, plusz azok a könyvtárak amiket feltöltünk. A java osztályok metódusai wrapper függvényeken keresztül érhetőek el, amiket ugyanúgy lehet hívni mint a tárolt eljárásokat, akár triggerből is. A wrapper függvény tartalmazza a hívandó java osztályt és a metódust a teljes szignatúrával. A metódusnak akár Clob-ok is átadhatóak paraméterként.

És most pár probléma / érdekesség amivel találkoztam:

  • Az Oracle 10g Express Edition nem támogatja ezt a feature-t, csak a Standard Edition vagy fölfelé.
  • Az Oracle 10g java1.4-et használ, az Oracle 9.2 pedig java1.3-at. Erre a fejlesztés során gondolni kell. Nem néztem utána hogy ezeket le lehet-e cserélni, de az Oracle elég érdekesen áll a jdk-khoz. Ha például installálok egy adatbázisszervert, a path-ot átírja a saját jdk-jára ami felülvágja az eddig használt (újabb) jdk-mat, ha esetleg volt olyan.
  • A loadjava parancssori eszköz az adatbázisszerverhez tartozik, tehát az adatbázisszervert tartalmazó gépen kell futtatni. (Jelenlegi tudásom szerint.)
  • Oracle 9.2-nél a loadjava -nál nem működik a -thin opció, bár ez a működést nem befolyásolja, csak a driver kiválasztásánál van szerepe.
  • Ha a loadjava-hoz megadjuk a -resolve opciót, a betöltés után végigellenőrzi a betöltött osztályokat. Ha nem adjuk meg ezt az opciót, az ellenőrzés az első futtatás során fog megtörténni. Amelyik osztályokkal gond van, vagy nem történt meg rájuk az ellenőrzés, azok invalidok: select dbms_java.longname(object_name),uo.* from user_objects uo where object_type='JAVA CLASS' and trunc(created) = trunc(sysdate) and status = 'INVALID'
  • Bizonyos osztályhibákat úgy kezel az Oracle, hogy lecseréli a bájtkódot egy exception dobásra. Ilyenkor egy warningot ad a konzolra (ORA-29552: verification warning), az osztályt viszont érvényesnek titulálja. Pl. ha olyan metódust akarunk hívni egy másik osztályból, ami nem létezik, akkor pontosan ez történik. A futáskor fog egy RuntimeException dobódni.
  • Itt van még egy átfogó írás a témában.
  • Az adatbázisszerveren belül tárolt eljárásokként a java kód párszor lassabban fut, mint normál módon, jre-vel futtatva. Ez nálam 4-5-szörös lassulást jelentett.
Ha nem gond a vendor lock-in, akkor hasznos dolog ez. Mivel 9.2-es Oracle-re kellett fejleszteni, jobbnak láttam felrakni egy ősrégi 1.3-as JDK-t, ami viszont a következő problémákat okozta az Eclipse-en belül, ha az egész projektre beállítottam az 1.3-as jre-t:
  • Nem tudta futtatni az Ant-ot, mert az már újabb jre-t igényel. Az ant-futtatáshoz explicite meg kellett adni egy újabb jre-t.
  • Ugyanígy a JUnit tesztesetekkel, meg kellett adni explicite egy újabb jre-t. Ez mégegy érv amellett, hogy a JUnit teszteket érdemes különálló projektbe rakni. Korántsem biztos, hogy a tesztesetek ugyanabban a környezetben futnak, mint a tesztelendő projekt.
  • A build.xml -ben a javac targetnél nem fogadta el a target="1.2", source="1.3" opciókat, hanem az executable megadásával tudtam megmondani, hogy melyik jdk-ból használja a javac-ot.
  • Találkoztam olyan kódrészekkel, amelyek 1.4-es és 1.3-as java-ban is értelmesen néznek ki, viszont az 1.4-es és 1.3-as fordító mást fordít belőle. Pl.:
    StringBuffer sb1 = new StringBuffer();
    StringBuffer sb2 = new StringBuffer();
    sb1.append(sb2);
    Java 1.4-ben van StringBuffer.append(StringBuffer) metódus, tehát egy 1.4-es fordító ezt fogja fordítani. Az 1.3-as java fordító viszont a StringBuffer.append(Object) metódust fogja alkalmazni. Hiába azonos a forráskód, 1.4-essel fordítva nem fog futni 1.3-as java-ban, mert nem fogja találni a StringBuffer.append(StringBuffer) metódust.
  • Azzal is szembesültem, hogy az 1.4-es java-ba már rengeteg dolgot beletettek az 1.3-hoz képest, pl. az XML-lel kapcsolatos csomagokat. Sokat kellett találgatni, hogy mi is nem volt még akkor benne? Szerencsére a jdom -nál amit használtam jól le volt írva, hogy a jdk1.3-ast és 1.4-et használóknak milyen könyvtárakat kell még feltölteniük.
A másik tanulság hogy ha lehet kerülni kell már ezeknek a régiségeknek a használatát.