java alapú sql api felületek hatékonyság elemzésemidra.uni-miskolc.hu › document › 21383...

67
Miskolci Egyetem Gépészmérnöki és Informatikai Kar Általános Informatika Tanszék Java alapú SQL API felületek hatékonyság elemzése Szakdolgozat Dobos Tamás H4BW07 3580 Tiszaújváros, Munkácsy Mihály út 5. 1/1.

Upload: others

Post on 31-Jan-2021

3 views

Category:

Documents


0 download

TRANSCRIPT

  • Miskolci Egyetem

    Gépészmérnöki és Informatikai Kar

    Általános Informatika Tanszék

    Java alapú SQL API felületek hatékonyság elemzése

    Szakdolgozat

    Dobos Tamás

    H4BW07

    3580 Tiszaújváros, Munkácsy Mihály út 5. 1/1.

  • Java alapú SQL API felületek hatékonyság elemzése

    EREDETISÉGI NYILATKOZAT

    Alulírott Dobos Tamás (Neptun-kód: H4BW07) a Miskolci Egyetem

    Gépészmérnöki és Informatikai Karának végzős mérnökinformatikus szakos

    hallgatója ezennel büntetőjogi és fegyelmi felelősségem tudatában nyilatkozom és

    aláírásommal igazolom, hogy

    Java alapú SQL API felületek hatékonyság elemzése című

    szakdolgozatom saját, önálló munkám; az abban hivatkozott szakirodalom

    felhasználása a forráskezelés szabályai szerint történt.

    Tudomásul veszem, hogy szakdolgozat esetén plágiumnak számít:

    - szószerinti idézet közlése idézőjel és hivatkozás megjelölése nélkül;

    - tartalmi idézet hivatkozás megjelölése nélkül;

    - más publikált gondolatainak saját gondolatként való feltüntetése.

    Alulírott kijelentem, hogy a plágium fogalmát megismertem, és tudomásul veszem,

    hogy plágium esetén szakdolgozatom visszautasításra kerül.

    Miskolc, 2015. május 4.

    ...................................

    Dobos Tamás hallgató

  • Java alapú SQL API felületek hatékonyság elemzése

    KÖSZÖNETNYILVÁNÍTÁS

    Ezúton szeretném megköszönni a Miskolci Egyetem tanárainak, akik

    tudásukat átadva lehetővé tették számomra, hogy eljuthassak szakdolgozatom

    megírásához.

    Köszönet illeti mindazokat, akik segítségemre voltak szakdolgozatom

    elkészítésében és tanácsaikkal előrébb vitték munkámat.

    Külön köszönettel tartozom konzulensemnek, dr. habil Kovács László

    egyetemi docens Úrnak, akinek szakmai tanácsai nélkül nem jöhetett volna létre

    ezen dolgozat.

    Természetesen hálával tartozom Édesapámnak és Édesanyámnak is, akik

    egyetemi éveim során mindenben támogattak és szakdolgozatom készítése alatt

    is folyamatosan bátorítottak.

    Miskolc, 2015. május 4.

    ...................................

    Dobos Tamás hallgató

  • Tartalomjegyzék

    1. Bevezető ............................................................................................................ 1

    2. Java környezet ................................................................................................... 3

    2.1 JDBC API áttekintés ..................................................................................... 3

    2.2 SQLJ API áttekintés ...................................................................................... 7

    2.3 JPA API áttekintés ...................................................................................... 10

    2.4 JINQ API áttekintés..................................................................................... 14

    3. Benchmarking, benchmark tesztek .................................................................. 19

    3.1 Benchmark tesztek az adatbázis-kezelésben ............................................. 19

    4. Tesztrendszer megtervezése ........................................................................... 23

    4.1 Tesztelés célja ............................................................................................ 23

    4.2 Mérhető vagy vizsgálható tulajdonságok .................................................... 23

    4.2.1 Tesztelési környezet ............................................................................. 24

    4.3 Tesztelés módszere .................................................................................... 26

    5. Implementáció .................................................................................................. 28

    5.1 Általában az adatbázis programozásról ...................................................... 28

    5.2 Tesztadatbázis létrehozása ........................................................................ 28

    5.3 Az alkalmazói program implementálása ..................................................... 29

    5.3.1 JDBC API implementáció ..................................................................... 36

    5.3.2 SQLJ API implementáció ..................................................................... 38

    5.3.3 JPA API implementáció ........................................................................ 40

    5.3.4 JINQ API implementáció ...................................................................... 43

    6. Tesztelés eredményei ...................................................................................... 45

    6.1 Insert művelet eredményei .......................................................................... 45

    6.2 Update művelet eredményei ....................................................................... 47

    6.3 Select művelet eredményei ......................................................................... 50

    6.4 Kapcsolódás művelet eredményei .............................................................. 53

    6.5 Funkciók feltárása és egyéb döntési szempontok ...................................... 54

    7.Összegzés ......................................................................................................... 57

    8. Summary .......................................................................................................... 59

    9. Irodalomjegyzék ............................................................................................... 61

    10. Melléklet ......................................................................................................... 63

  • Java alapú SQL API felületek hatékonyság elemzése

    1

    1. Bevezető

    Napjainkban bármilyen szoftvert vagy alkalmazást veszünk alapul, szinte

    bizonyos, hogy a háttérben kapcsolatban áll valamilyen adatbázissal. Gondoljunk

    csak bele egy webshopba, ahol az eladni kívánt termék jellemzőit, árát, a raktáron

    lévő mennyiséget vagy bármilyen termékhez kötődő adatot tárolnunk kell.

    Szintén hatalmas méretű adatbázis állhat egy könyvtári nyilvántartó

    rendszer mögött. Itt tárolni kell a kölcsönözhető tartalmak alapvető adatait.

    Például: cím, szerző, kiadás éve, ISBN stb. Továbbá a beiratkozott olvasókról is

    tárolni kell néhány alapvető adatot, mint a név, születési dátum, lakcím stb. Ahhoz

    azonban, hogy a nyilvántartó rendszer hasznosan tudjon működni, további

    információkat kell tárolni a kölcsönzésekről is. Egy könyvtárban nem árt tudni,

    hogy melyik könyvet mikor és melyik olvasó kölcsönözte ki.

    A fent leírt példákat tekintve látható, hogy az adatbázisok és a különböző

    adatok kezelése szerves részét képezik egy-egy szoftvernek vagy alkalmazásnak.

    Informatikusként az ember ismeri a legelterjedtebb adatkezelő nyelvet (SQL),

    melynek segítségével viszonylag könnyedén kezelheti az adatbázist, valamint az

    adatokat. Egy SQL parancsfelület nagyon hasznos a különböző SQL parancsok

    kiadására, valamint az eredmények megtekintésére. Általánosságban azonban

    elmondható, hogy egy webshop adminisztrátora vagy a könyvtáros nem feltétlen

    "beszél" adatkezelő nyelvet. Továbbá az SQL parancsfelület nem túl

    felhasználóbarát az adatmegjelenítés szempontjából. Nem alkalmas akkor sem,

    ha több adatot akarunk bevinni egyszerre vagy a bevitelre kerülő értékeket egy

    listából választjuk ki.

    Erre a problémára nyújthat megoldást, ha egy felhasználóbarát,

    rugalmasan testre szabható alkalmazói programot készítünk. Ezen az alkalmazói

    programon keresztül már a webshop adminisztrátor és a könyvtáros is képes

    különösebb informatikai előismeret nélkül kezelni az adatbázist és a benne lévő

    adatokat.

    Egy alkalmazói programnak valamilyen módon el kell érnie a külső

    szerveren levő adatbázist. Fejlesztőként már az is okozhat fejtörést, hogy

    egyáltalán milyen adatbázis-kezelőt válasszunk. A piacon ugyanis számos

    lehetőség kínálkozik. Ha letettük voksunkat egy adatbázis-kezelő mellett, akkor

  • Java alapú SQL API felületek hatékonyság elemzése

    2

    további gondolkodásra adhat okot az, hogy alkalmazói programunk milyen módon

    fog kommunikálni az adatbázissal. A paletta nagyon széles, sok SQL API létezik

    erre a célra.

    A szakirodalomban nem igazán kapunk átfogó képet és egyfajta

    összevetést a különböző SQL API felületeket tekintve. Mindegyiknek létezik külön-

    külön specifikációja, azonban kezdő fejlesztők számára hasznos lehet, ha

    olvashatnak valamilyen összegzést.

    Ezt az „űrt” szeretném pótolni, ezért dolgozatom első felében - a teljesség

    igénye nélkül - áttekintenék néhány SQL API felületet. Ezután bemutatom a

    tesztrendszer tervezésének folyamatát és annak implementációját. Végezetül

    ismertetem az elvégzett teszteket és azok eredményeit, valamint értékelem a

    tapasztaltakat.

  • Java alapú SQL API felületek hatékonyság elemzése

    3

    2. Java környezet

    A programozás kezdetén az úgynevezett strukturális programozás volt az

    irányadó. Mára az objektum orientált szemlélet van előtérben. Természetesen

    több objektum orientált programnyelv létezik. Például a C++, C#, Visual Basic,

    Java stb. Mivel egy szakdolgozat keretein belül nem célszerű minden egyes OOP

    nyelv és a hozzá tartozó SQL API felületek bemutatás, ezért dolgozatomban egy

    programnyelvre szűkítem a halmazt. Választásom a Java nyelvre esett.

    A Java egy általános célú, objektum orientált programnyelv. [1] Elsődleges

    célja az volt, hogy legyen egy olyan programozási nyelv, amely hálózaton

    keresztül is biztonságosan használható és lehetőleg platform független.

    A Java azonban több, mint egy programozási nyelv. A Java egy tisztán

    szoftver megvalósítású platformként is tekinthető, melynek két része van [2]:

    Java programozási interfész (Java API)

    Java Virtual Machine (JVM)

    Tehát azok az eszközök, amelyek képesek futtatni egy JVM környezetet,

    képesek lesznek Java nyelven írt programok futtatására is. Ebből következik, hogy

    Java kliensek elég széles skálája létezik. További előnye a Java nyelvnek, hogy a

    fejlesztéshez szükséges környezet ingyenesen elérhető. Szintén előny, hogy a

    nyelvet folyamatosan fejlesztik.

    Ezek alapján úgy gondoltam, hogy a Java széles körben használt és ismert

    nyelv és platform. Ezért esett erre a választásom.

    2.1 JDBC API áttekintés

    A Java Database Connectivity egy ipari szabvány adatbázis-független

    kapcsolatok kialakítására a Java nyelven írt program és a különböző adatbázisok

    között. [3] Segítségével három dolog valósítható meg:

    Létrehozza a kapcsolatot egy adatbázis vagy hozzáférhető táblázatos

    adatforrás között

    Alkalmas SQL parancsok küldésére

    Feldolgozza az eredmény halmazt

  • Java alapú SQL API felületek hatékonyság elemzése

    4

    Tulajdonképpen Java nyelven íródott osztályokat és interfészeket

    tartalmazó csomagok halmaza, melyek lehetővé teszik adatbázis alapú

    alkalmazások fejlesztését. Előnye abban mutatkozik meg, hogy platform független,

    hiszen bárhol alkalmazható, ahol a JVM (Java Virtual Machine) futtatható.

    Másik nagy előnye, hogy bármilyen adatbázis-kezelő rendszerrel

    használható. Ehhez szükség van az adott adatbázis-kezelővel kompatibilis JDBC

    meghajtó programra (driver). A driver tehát rendszerfüggő. Ezt az adatbázist

    gyártó cég honlapjáról kell letölteni.

    A JDBC API két fő interfész halmazt tartalmaz: az egyik a JDBC API az

    alkalmazás fejlesztők számára, a másik pedig egy alacsonyabb szintű JDBC

    driver API a driver fejlesztők részére. Maguk a JDBC driverek pedig négy

    csoportba sorolhatóak, melyet az 1. ábra szemléltet.

    1. ábra: a JDBC API struktúrája és driver típusai.

    Az ábra bal szélső részén található az úgynevezett Direct-to Database Pure

    Java Driver. Az ilyen típusú driverek átalakítják a JDBC hívásokat arra a hálózati

    protokollra, amelyet az adatbázis-kezelő rendszerek közvetlenül használnak.

  • Java alapú SQL API felületek hatékonyság elemzése

    5

    Megengedik a kliens programból történő közvetlen hívásokat, ezzel praktikus

    hozzáférést biztosítva.

    Ha tovább haladunk az ábrán, akkor a következő driver típus a Pure Java

    Driver for Database Middleware. Ezek a típusú driverek lefordítják a JDBC

    hívásokat egy middleware szállító protokollra, amely aztán lefordítható adatbázis-

    kezelő protokollra. A middleware összeköttetést biztosít a különböző

    adatbázisokhoz.

    Az ábrán a harmadik driver a JDBC-ODBC bridge. Ez a kombináció a JDBC

    hozzáférést egy ODBC driveren keresztül biztosítja. A JDBC-ODBC bridge

    alkalmas kísérleti használatra vagy olyan esetekben, amikor nincs más elérhető

    driver.

    A jobb szélső driver a Native API, mely részben Java technológiát használ.

    Az ilyen fajta driverek átalakítják a JDBC hívásokat az adatbázis-kezelő

    rendszeren történő kliens hívásokra.

    Tehát látható, hogy a JDBC használatához minden esetben szükséges

    valamilyen driver regisztrálása a Java alkalmazásunkban.

    Miután letöltöttük a megfelelő meghajtót, regisztrálnunk kell azt a Driver

    Managerben. Ennek feltétele, hogy a drivert - ami lényegében egy .jar

    kiterjesztésű fájl - elérhetővé tegyük a Java keresési útvonalán szereplő

    könyvtárban. Ha a driver elérhető a keresési útvonalon, akkor regisztrálható a

    Driver Managerben. Ezután létrehozhatjuk az adatbázis kapcsolatot, ha meghívjuk

    a DriverManager.getConnection(); metódust, melynek átadjuk az alábbi

    paramétereket:

    url (host, port és dbname)

    username

    password

    Amint a kapcsolat sikeresen létrejött, a programozónak lehetősége van

    SQL parancsokat küldeni az adatbázisnak. A JDBC API nem korlátozza az

    elküldhető SQL utasítások halmazát (akár nem SQL parancsokat is

    használhatunk). [4] Azt viszont a fejlesztőnek kell biztosítania, hogy az adatbázis

    fel tudja dolgozni a kapott utasítást. Erre egy példa: tárolt eljárást JDBC-n

    keresztül akkor hívjunk meg, ha biztosak vagyunk benne, hogy a használt

    adatbázis támogatja a tárolt eljárásokat, különben kivétel fog keletkezni.

  • Java alapú SQL API felületek hatékonyság elemzése

    6

    Az API három interfészt biztosít az SQL kérések küldéséhez:

    Statement: paraméter nélküli SQL utasítások hívására szolgál. A

    kapcsolat objektum createStatement() metódusával hozható létre.

    PreparedStatement: A Statement leszármazottja, a kapcsolat

    objektum prepareStatement() metódusával hozható létre. Akkor

    használjuk, ha egy utasítást többször is végre akarunk hajtani. A

    parancs string paraméterezhető is, ahol a setYYY() (YYY a paraméter

    típusa) metódus használható a paraméter konkrét értékének a

    megadására.

    CallableStatement: A PreparedStatement leszármazottja, a kapcsolat

    objektum prepareCall() metódusával hozható létre. Tárolt eljárások

    hívására használjuk. Lehetnek kimeneti és bemeneti paraméterei is.

    Az API három végrehajtási módot biztosít a programozó számára. Ezek a

    következők:

    executeUpdate(): adatdefiníciós és adatkezelő utasítások

    végrehajtására használjuk (INSERT, UPDATE, DELETE, CREATE,

    DROP).

    executeQuery(): eredményhalmazt visszaadó parancsok

    végrehajtásakor használjuk (SELECT).

    execute(): az előző két típus bármelyikének végrehajtására alkalmas.

    Láthatjuk, hogy a JDBC egy jól használható, stabil SQL API. Talán nem

    véletlen, hogy a fejlesztők körében az egyik legelterjedtebb módszer az

    adatkezelés megvalósítására.

  • Java alapú SQL API felületek hatékonyság elemzése

    7

    2.2 SQLJ API áttekintés

    Az SQLJ egy másik lehetséges szabvány Java környezetben arra, hogy a

    programozó kezelhessen egy adatbázist. [5] Három részt tartalmaz:

    SQLJ 0. rész: beágyazott, statikus SQL utasítások a Java kódban

    (SQL/Object Language Bindings).

    SQLJ 1. rész: Az adatbázis szerveren tárolt eljárások meghívása Java

    metódusokon keresztül (SQL Routines using in the Java languages).

    SQLJ 2. rész: Java osztályok a felhasználó által definiált SQL

    típusokra.

    A legszélesebb körben az SQLJ 0. része terjedt el, melyet az ISO

    szabványosított. A továbbiakban SQLJ alatt az SQLJ 0. részét értem.

    A Java fordító nem ismeri a beágyazott SQLJ utasításokat. [6] Ehelyett egy

    speciális .sqlj kiterjesztést használ, mely jelzi, hogy a forrás fájl SQLJ fájl. Egy

    előfeldolgozási lépésben az SQLJ fordító lefordítja az SQLJ forrás fájlt egy

    közbenső Java forrás fájlra, amelyben az SQLJ parancsokat futás időben hívja

    meg. A köztes Java forrás fájl már lefordítható a Java fordítóval. Ennek menetét

    szemlélteti a 2. ábra.

    2. ábra: az SQLJ fordítási struktúrája.

    A fejlesztéshez szükség van a korábban már bemutatott JDBC meghajtóra.

    Ügyelnünk kell azonban arra, hogy nem minden adatbázis-kezelő támogatja az

    SQLJ-t, mely a JDBC-hez képest mindenképpen hátrány. Oracle vagy DB2

    adatbázis-kezelő jó választás, míg az ismertebbek közül a MySQL nem támogatja

    az SQLJ-t. A megfelelő JDBC meghajtó kiválasztása és regisztrálása után

    nekiláthatunk a tényleges kapcsolat felépítésnek.

    Létre kell hoznunk egy ConnectionContext objektumot. Minden beágyazott

    SQL utasítás az SQLJ programban egy ConnectionContext-ben fut.

  • Java alapú SQL API felületek hatékonyság elemzése

    8

    Ez lehetővé teszi, hogy többszörös kapcsolatot alakítsunk ki egy

    adatbázissal, vagy több adatbázishoz kapcsolódjunk egy időben. Ennek egyik

    módja, hogy DefaultContext() objektumot hozunk létre. Ezt a getConnection()

    metódus meghívásával tehetjük meg, melynek az alábbi paraméterei vannak:

    url

    username

    password

    autocommit: értéke true vagy false lehet, attól függően, hogy

    automatikus commit-ot szeretnénk-e vagy a programozónak saját

    magának kell gondoskodnia a commit-ról.

    Ha ez sikeres volt, akkor a setDefaultContext() metódussal beállítjuk a

    kapott objektumot. Ezután már írhatunk SQLJ utasításokat.

    Lehetőség van többszörös adatbázis kapcsolat létrehozására is. [7] Ilyenkor

    az alapértelmezett kapcsolat mellett egy nem alapértelmezett kapcsolat is van.

    Hogy melyik kapcsolatot akarjuk használni, azt explicite jelezhetjük, ha

    hivatkozunk a nevére. Ha nincs névhivatkozás, akkor automatikusan az

    alapértelmezett kapcsolaton keresztül hajtódik végre az utasítás.

    A továbbiakban szeretném bemutatni a beágyazott SQLJ utasítások

    alapvető szintaktikáját. Az utasításnak minden esetben #sqlj-vel kell kezdődnie,

    majd { } zárójelek között kell megadni bármilyen érvényes SQL utasítást. A végén

    pontosvesszővel kell lezárni.

    Léteznek különböző foglalt kulcsszavak, melyek a következők: iterator,

    context és with. Ügyelnünk kell arra is, hogy a parancsok case sensitive-ek.

    Alkalmazhatunk úgynevezett host változókat is a Java környezet és az SQL

    kérések közötti kommunikációra. A host változó tulajdonképpen vagy egy Java

    lokális változó, vagy egy osztály változó. Használata esetében kettősponttal (:) kell

    kezdődnie a kifejezésnek. Ezt opcionálisan megelőzheti az IN, OUT vagy az

    INOUT kifejezések egyike. Fontos, hogy a host változók case sensitive-ek.

    SQLJ esetében szintén fontos nyelvi elemek az úgynevezett iterátorok.

    Olyan lekérdezések esetében, melyeknél az eredményhalmaz több rekordból áll,

    az iterátorok használatával lehetséges az eredmények feldolgozása.

  • Java alapú SQL API felületek hatékonyság elemzése

    9

    A fentebb részletezett két módszer együttesen is használható ugyanazon

    alkalmazásban, attól függően, hogy éppen dinamikus (JDBC) vagy statikus

    (SQLJ) parancsokat szeretnénk használni.

  • Java alapú SQL API felületek hatékonyság elemzése

    10

    2.3 JPA API áttekintés

    A JPA (Java Persistence API) egy objektum-relációs leképzést biztosít Java

    környezetben, hogy alkalmazásokban egyszerűbb módon lehessen az

    objektumokat a relációs adatbázisban kezelni [8]. A Java perzisztencia kezelő

    keretrendszer három fő területet foglal magában:

    A Java Persistence API

    Objektum-relációs metaadatok

    Lekérdező nyelv (JPQL - Java Persistence Query Language)

    Ahhoz, hogy a lekérdező nyelvet alkalmazhassuk, először fontos

    megismerkednünk az API struktúrájával. Az alkalmazásunkban létre kell hoznunk

    egy EntityManagerFactory objektumot, melynek segítségével inicializálhatjuk a

    kapcsolatot az adatbázissal. Ezután szükség van egy EntityManager objektumra.

    Minden EntityManager példány egy perzisztencia kontextussal társul. A

    perzisztencia kontextus meghatározza azt a hatókört, amely alatt az egyes Entity

    példányok létrehozhatók, állandósíthatók és törölhetők.

    Tehát az EntityManager példányon keresztül tranzakciót tudunk nyitni,

    melyen keresztül kezelhetjük az adatokat. Továbbá lekérdezéseket is

    végrehajthatunk (4. ábra).

    4. ábra: a JPA API struktúrája. [9]

  • Java alapú SQL API felületek hatékonyság elemzése

    11

    Fontos szót ejteni az objektum-relációs leképzésről is, mely nem más, mint

    egy programozási technika. Lehetővé teszi az adatok konverzióját valamely

    objektum orientált programozási nyelv és valamely nem kompatibilis típusos

    rendszer között. A JPA-ban a leképzés az Entity objektumok segítségével valósul

    meg.

    Az Entity tulajdonképpen egy fő táblát és esetleg néhány melléktáblát

    reprezentál az adatbázisból. Minden egyes Entity példány a tábla egy sorának,

    azaz egy rekordnak felel meg. Az Entity példány tulajdonságai pedig a tábla

    oszlopaival hozhatók párhuzamba.

    Ebből adódóan az elsődleges programozási egység egy Entity

    megvalósítására az un. Entity osztály. Egy Entity osztálynak a következő

    követelményeknek kell megfelelnie:

    Az osztályt a @Entity annotációval kell megjelölni.

    Az osztálynak implementálnia kell egy public vagy protected paraméter

    nélküli konstruktort. Ezen felül természetesen az osztály

    implementálhat egyéb konstruktorokat.

    Az osztály nem deklarálhatja a final módosítót.

    Ha egy Entity példányt érték szerint külső objektumként adunk át,

    akkor az osztálynak implementálnia kell a Serializable interfészt.

    A perzisztens példányváltozók deklarációjánál csak a protected és a

    private módosító használható. Továbbá csak az Entity osztály

    metódusain keresztül érhető el közvetlenül.

    Az Entity-k kezelésére alkalmas az EntityManager, mely számos

    alapműveletet kínál a fejlesztő számára. Ezek közül a legfontosabbak: [10]

    persist(): egy új entitás példány perzisztencia kontextushoz való

    hozzáadását teszi lehetővé;

    merge(): beleolvasztja az adott entitás objektum állapotát az aktuális

    perzisztencia kontextusba;

    remove(): eltávolítja a paraméterként megadott entitás objektumot;

    find(): segítségével entitás objektumokat kereshetünk a perzisztencia

    kontextusban (többféle paraméter szignatúra is lehetséges);

  • Java alapú SQL API felületek hatékonyság elemzése

    12

    flush(): szinkronizálja a perzisztencia kontextust a mögöttes

    adatbázissal;

    lock(): a megadott zárolási módon zárolja a perzisztencia

    kontextusban levő entitás objektumot (többféle paraméter szignatúra is

    lehetséges);

    refresh(): frissíti a paraméterül kapott entitás objektum állapotát az

    adatbázisból (többféle paraméter szignatúra is lehetséges);

    clear(): törli a perzisztencia kontextust;

    createQuery(): lekérdező utasítás végrehajtására használhatjuk

    (többféle paraméter szignatúra is lehetséges);

    createNativeQuery(): natív SQL utasítások végrehajtására alkalmas

    (többféle paraméter szignatúra is lehetséges).

    Most, hogy már képet kaptunk az Entity fogalomról, foglalkozhatunk a

    lekérdező nyelvvel (JPQL), melyet a JPA biztosít. A JPQL - azaz a Java

    Persistence Query Language - meghatározza egy Entity lekérdezéseit és

    perzisztenciáját. A JPQL lehetővé teszi, hogy olyan hordozható lekérdezéseket

    írjunk, amelyek a mögöttes adattárolástól függetlenül működnek. Szintaktikailag

    hasonló az SQL nyelvhez. Tehát a JPQL nem közvetlenül az adattáblákat kezeli,

    hanem az entitás objektumokat.

    A JPQL egészen az alapokon át az összetettebb, finomabb lekérdezések

    megvalósítására is lehetőséget ad. Számos olyan hasonló eleme van, melyek már

    jól ismertek az SQL nyelvből.

    A lekérdezés a select kulcsszóval kezdődik. Ez meghatározza a lekérdezés

    által visszaadott elemek típusát vagy értékét. Az SQL-hez és a JINQ-hoz

    hasonlóan itt is van lehetőségünk aggregációs kifejezések megadására. Ezek a

    következők: sum, count, min, max, avg. Hiányzik azonban a JINQ-ban megismert

    többszörös aggregációs kifejezés.

  • Java alapú SQL API felületek hatékonyság elemzése

    13

    A lekérdezés tartományának kijelölésére a from kulcsszó szolgál. Ahhoz,

    hogy ebből a tartományból szűrni tudjuk az adatokat a where kulcsszó után kell

    megadnunk a megfelelő feltételeket. A feltételek megadására rendkívül sok

    lehetőség van:

    Between: meghatározza, hogy egy aritmetikai kifejezés a megadott két

    érték közé esik-e.

    In: meghatározza, hogy a vizsgált elem eleme-e a megadott

    halmaznak. Alkalmas stringek és numerikus értékek vizsgálatára.

    Like: meghatározza, hogy a helyettesítő karakterek illeszkednek-e a

    stringre. A vizsgált kifejezésnek stringnek vagy numerikus adatnak kell

    lennie. Ha az érték NULL, akkor a Like kifejezés értéke ismeretlen.

    Null: azt vizsgálja, hogy egy egyértékű kifejezésnek vagy egy

    bemeneti paraméternek van-e NULL értéke.

    Empty: azt vizsgálja, hogy egy gyűjtemény üres-e, azaz van-e eleme

    vagy sem.

    JPQL is támogatja a csoportképzést, melyet a group by utasítással

    adhatunk meg. A csoportokban további szűrést végezhetünk a having feltétel

    megadásával. Ha a lekérdezés során valamilyen rendezésre van szükségünk,

    akkor az order by opciót érdemes használnunk. Az order by esetén két további

    kulcsszót kell megismerni:

    asc: növekvő sorrendbe rendezi az eredményhalmaz elemeit a

    megadott tulajdonság alapján. Ez az alapértelmezett.

    desc: csökkenő sorrendbe rendezi az eredményhalmaz elemeit a

    megadott tulajdonság alapján.

    Az SQL-ben megszokott allekérdezések is megvalósíthatók. Továbbá

    lehetséges az adatok módosítása, valamint törlése. Előbbire az update, utóbbira a

    delete utasítás használható. Ezen utasítások meghatározzák a módosítandó vagy

    kitörlendő Entity típusát. A where feltétel megadásával pedig meghatározható

    azon elemek köre, amelyeket módosítani vagy törölni szeretnénk.

    Összességében elmondható a JPA, illetve a JPQL használatáról, hogy

    szintaktikája nagyon hasonló az SQL nyelvhez. Az SQL által megismert és

    használt elemek túlnyomó többsége itt is megtalálható és alkalmazható.

  • Java alapú SQL API felületek hatékonyság elemzése

    14

    2.4 JINQ API áttekintés

    A .NET világban már évek óta létezik az úgynevezett LINQ (Language

    Integrated Query), amely lehetővé teszi, hogy a különböző adatforrásokat

    könnyedén, egységesített módon kezelhessük. [11] Napjainkban számos

    adatforrás áll rendelkezésünkre, melyek kezeléséhez különböző eszközök

    használatát és új nyelveket kell megtanulnunk (SQL, XQuery stb.). A LINQ egyik

    nagy előnye, hogy segítségével ugyanolyan módon kezelhetjük az adatelemek

    halmazát, függetlenül attól, hogy az adatbázisban, memóriában vagy egy XML-

    ben tárolódik. Másik előnye, hogy erősen típusos nyelv, vagyis a legtöbb hiba még

    fordítási időben felderíthető és kijavítható.

    Jelenleg a LINQ-nak három fő iránya van, melyek a következők:

    LINQ to XML: XML dokumentumok kezelését és lekérdezését

    biztosítja.

    LINQ to Object: memóriában lévő gyűjtemények, listák, tömbök

    feldolgozására szolgál.

    LINQ to SQL: relációs adatbázisok menedzselését teszi lehetővé az

    alkalmazói programok számára.

    A fentiekből egyértelműen látszik a LINQ előnye. A Java világban sokáig

    nem volt hozzá hasonló alternatíva, azonban a Java 8-as verziójától kezdve

    megjelent a JINQ.

    Alapvetően a JINQ erősen épít a korábban bemutatott JPA-ra. [12] Éppen

    ezért a struktúrája is nagyon hasonló hozzá. A JINQ jelenleg csak annyival tud

    többet a JPA-nál, hogy használatával adatbázis lekérdezéseket írhatunk egyszerű

    Java szintaktikát alkalmazva. Ugyanaz a kód használható az adatok szűrésére,

    mint normál Java adatok esetén. Tehát az adatmanipulációs utasítások esetén a

    JPA struktúrája érvényes. A leképzés itt is az Entity objektumokon keresztül

    történik, melyeket az EntityManager segítségével kezelhetünk. A struktúra csak a

    lekérdezéseknél bővül egy JinqJPAStreamProvider objektummal. Tulajdonképpen

    ezen keresztül alkalmazhatóak a JINQ nyújtotta szolgáltatások. Ezt szemlélteti az

    5. ábra.

  • Java alapú SQL API felületek hatékonyság elemzése

    15

    5. ábra: a JINQ API struktúrája.

    Adatbázis tesztelésnél a programozó először megírja a lekérdezést, majd

    elindítja az adatbázist és lefuttatja a lekérdezést. Ez körülményes és lassú

    folyamat. A JINQ további előnye, hogy a Java fordító már fordítási időben elkapja

    a hibás lekérdezéseket, ezzel gyorsítva a fejlesztés menetét. Továbbá, mivel a

    lekérdezések Java szintaktikával készültek, így az SQL injection támadások

    lehetetlenek, tehát nő a biztonság.

    Fontos tisztázni, hogy mi az, ami lefordítható. A JINQ normál Java kódot

    használ, de nem minden Java kód fordítható le JINQ adatbázis lekérdezésekre. A

    JINQ automatikusan lefordítja a Java kódot adatbázis lekérdezésekre, ezért olyan

    műveleteket kell használnunk, amelyek elérhetőek az adatkezelő nyelvben is. Ha

    olyan műveleteket használunk, melyek nem lefordíthatóak, akkor a JINQ

    egyszerűen futtatja a kódot. A programozó fog eredményhalmazt visszakapni,

    azonban a kód nem használja ki a lehetséges teljesítmény előnyeit.

  • Java alapú SQL API felületek hatékonyság elemzése

    16

    Ahhoz, hogy a fordítás zökkenőmentes legyen, az alábbi megszorításokat

    kell betartani:

    A kódunk nem tartalmazhat ciklusokat.

    A kódunkban meghívhatunk más metódusokat, de csak azokat, melyek

    egy szűkített lista elemei, ismert mellékhatásokkal.

    A kódunkban olvashatunk és módosíthatunk lokális változókat (mivel

    ezek a változtatások elvesznek, miután a metódus lefutott).

    A kódunkban olvashatunk nem lokális változókat, de nem

    módosíthatjuk azokat.

    Egy alkalmazásban szükség lehet a lekérdezések dinamikus

    összeállítására. Erre a JINQ kétféle megoldást kínál. Az egyik a paraméterezés. A

    paraméterek lehetővé teszik, hogy egy lekérdezésbe különböző értékeket

    helyettesítsünk be. Ezzel különböző lekérdezések jöhetnek létre. A paraméter

    változónak lokálisnak kell lennie, mert a JINQ a Java szerializációt használja. Ha

    nem lokális változót használunk paraméterként, az problémákhoz vezethet.

    A másik dinamikus lehetőség a futásidejű lekérdezés összetétel. Mivel a

    JINQ lekérdezés Java kódot használ, nem lehet kivitelezni, hogy futás időben

    módosítsuk azt. Arra viszont van lehetőség, hogy a lekérdezés különböző részeit

    futás időben rakjuk össze.

    A JINQ továbbá képes arra, hogy lefordítson különféle Java metódusokat

    ekvivalens JPQL függvényekre. A teljesség igénye nélkül néhány ilyen metódus:

    Math.sqrt(); String.toUpperCase(); String.trim(); String.length(); stb.

    Mielőtt megvizsgálnám az alapvető JINQ lekérdezéseket, célszerű

    betekinteni a lambda kifejezések világába, mivel a JINQ is használ ilyeneket. A

    lambda kifejezések tulajdonképpen névtelen metódusok, amelyeket ott írunk, ahol

    ténylegesen használjuk is. [13] Lambda kifejezések esetén nem egy olyan

    objektumot adunk át, ami megvalósítja a kívánt interfészt, hanem magát a

    viselkedést, azaz magát az implementációt. A lambda kifejezések szintaktikája

    három részből tevődik össze [14]:

    argumentum lista;

    nyíl jelkép (arrow token);

  • Java alapú SQL API felületek hatékonyság elemzése

    17

    body rész: lehet egyszerű kifejezés vagy utasítás blokk is. Kifejezés

    formula esetén a kifejezés egyszerűen kiértékelődik és visszatér az

    eredménnyel. Blokk formula esetén a body rész egy metódus

    törzséhez hasonlóan működik.

    Egy alapvető JINQ lekérdezésben a where() metódus szűri az adatbázis

    adatokat. Minden egyes elemre megvizsgálja a feltételként megadott kifejezést.

    Ha igaz, akkor visszaadja az elemet, egyébként kihagyja a szűrésből. Továbbá

    lehetőség van az or és az and operátorok használatával összetett szűrési

    feltételek megadására is.

    Előfordulhatnak olyan esetek, amikor nincs szükség a teljes rekord

    megjelenítésére, csak bizonyos mezőit szeretnénk lekérni. Erre alkalmas a

    select() metódus. A metódusnak van egy olyan funkciója is, mely képes átalakítani

    az adatokat, például számításokat végez el velük. Lehetőség van arra is, hogy a

    select() metóduson belül elágazásokat használjunk, összetettebb lekérdezéseket

    megvalósítva ezzel.

    A tipikus és alap JINQ lekérdezéseken kívül sokkal összetettebb és

    finomabb lekérdezések írására is alkalmas az API. Az alábbiakban

    összefoglalnám a különböző aggregációs lehetőségeket:

    sum(): több fajtája van, aszerint, hogy milyen típusú értékeket

    szeretnénk összegezni. Például int-sumInteger(); long-sumLong();

    double-sumDouble(); stb. A sum() metódus bemenetként egy

    függvényt kap. Ez a függvény alkalmazható a stream minden elemére,

    és azokkal az értékekkel tér vissza, amelyeket össze kell adni.

    count(): a stream-ben lévő tételek megszámlálására használható.

    Visszatérési értéke egy Long típusú érték.

    min() és max(): a stream-ben a minimum vagy maximum érték

    meghatározására használható. Bementként egy függvényt kap. Ez a

    függvény alkalmazható a stream minden elemére és azzal az értékkel

    tér vissza, amit minimumként vagy maximumként megtalált. Az értékek

    szám típusúak vagy más ismert összehasonlítható típus (például

    dátum) lehetnek.

    avg(): az átlagos érték meghatározására használható.

  • Java alapú SQL API felületek hatékonyság elemzése

    18

    aggregate(): többszörös aggregációra használható akkor, ha

    ugyanazon a stream-en kell egyidejűleg több különböző aggregációt

    elvégezni.

    A lekérdezéseknél előfordulhat, hogy az eredményhalmazt nem egy

    táblából várjuk. Természetesen a JINQ-ban is van lehetőség az SQL nyelvben

    már jól ismert joinra. Szintén kivitelezhető a csoportképzés (group()). A kapott

    eredményeket rendezhetjük is (sort()).

    Összességében látható tehát, hogy a JINQ még egy viszonylag új SQL API,

    hiszen a Java 8-as verziójától kezdődően használható (a lambda kifejezések

    miatt), melyet 2014-ben adtak ki. Ennek ellenére rendkívül gazdag és széleskörű

    funkciókkal rendelkezik, megkönnyítve ezzel a Java fejlesztők mindennapjait.

  • Java alapú SQL API felületek hatékonyság elemzése

    19

    3. Benchmarking, benchmark tesztek

    Egy ember bármilyen fontosabb döntés előtt áll, igyekszik körüljárni az adott

    témakört, alternatívákat, megoldási lehetőségeket. Számos döntést támogató

    algoritmus, technika létezik az élet több területén. Ilyenek például a SWOT

    analízis, ABC elemzés vagy a Benchmarking.

    Az informatikában állandó kérdés és tényező a teljesítmény. Szinte

    valamennyi esetben befolyásolja a fejlesztő vagy a felhasználó döntését. Ugyanis

    minden felhasználó azt szeretné, hogy az adott szoftver vagy hardver gyors,

    megbízható, magas rendelkezésre állású legyen, azaz jó teljesítményt nyújtson.

    A benchmark programok tehát a szoftver- és hardvereszközök, valamint

    komplett rendszerek effektív teljesítményének megbecsülésére alkalmas

    programok. Ezek a programok megmérik, hogy egy előre definiált műveletsor

    végrehajtásához mennyi időre van szükség a tesztelt környezetben. A mért

    eredmények alapján pedig következtetnek a vizsgált rendszer effektív

    teljesítményére.

    Az, hogy a becsült teszteredmények mennyire relevánsak a tényleges

    felhasználás mellett, attól függ, hogy a benchmark teszt alatt elvégzett műveletek

    jellemzői mennyire állnak közel a valós életbeli felhasználási körülményekhez.

    Fontos tehát az ilyen teszteknél, hogy a valós élethez minél közelebbi feltételeket

    teremtsünk elő. [15]

    3.1 Benchmark tesztek az adatbázis-kezelésben

    Adatbázisok és adatbázis kezelés világában is léteznek benchmark tesztek.

    Alapvetően két fő irány tesztelése lehetséges: az egyik az adatbázis-kezelő

    rendszerek összevetése, a másik pedig a különböző API-k komparációja.

    Az adatbázis-kezelő rendszerek esetén többféle elemzés és

    összehasonlítás létezik. Az interneten többek között találhatóak összehasonlító

    táblázatok arról, hogy az RDBMS:

    mely operációs rendszert támogatja;

    milyen alapvető funkciókkal rendelkezik;

    milyen adat méretre vonatkozó korlátozásai vannak (adatbázis mérete,

    táblák mérete, sorok mérete stb.);

  • Java alapú SQL API felületek hatékonyság elemzése

    20

    támogatja-e a view kezelést;

    milyen adattípusokat és egyéb objektumokat támogat (string, date,

    triggerek, eljárások, függvények stb.);

    milyen módon korlátozza a hozzáférést és milyen biztonsági

    funkciókkal rendelkezik.

    A fentiek elsősorban az adatbázis-kezelő rendszerek funkcionalitására

    összpontosító elemzések. Természetesen léteznek teljesítményre vonatkozó

    benchmark tesztek is.

    A TPC (Transaction Processing Performance Council) tranzakció

    feldolgozás és adatbázis benchmark teszteket határoz meg. [16] Továbbá

    megbízható adatokat és eredményeket szállít az ipar számára. Az alábbiakban - a

    teljesség igénye nélkül - szeretnék röviden néhány TPC benchmarkot ismertetni.

    A TPC-C egy teljes számítógépes környezetet szimulál, ahol a felhasználók

    tranzakciókat hajtanak végre az adatbázison. A teszt egy order-entry környezet

    főbb tevékenységei (tranzakciók) köré épül. Ezek a tranzakciók tartalmazzák a

    kifizetések rögzítését, ellenőrzik a rendelések állapotát és figyelemmel kísérik a

    felhalmozódott készletet a raktárban. A TPC-C öt különböző típusú és

    bonyolultságú konkurens tranzakciót foglal magában.

    TPC-E: egy OLTP (On-Line Transaction Processing) munkaterhelést

    tesztel. Egy brókercéget modellező adatbázist használ, ahol a felhasználók

    tranzakciókat generálnak a kereskedéshez és a piackutatáshoz. A TPC-E metrika

    tps (transaction per second) értéket ad vissza, mely megmutatja, hogy a szerver

    adott idő alatt mennyi tranzakciót képes kezelni.

    TPC-DS egy döntést támogató benchmark, amely számos, általánosan

    alkalmazható szempontból modellez egy döntéstámogató rendszert, beleértve a

    lekérdezéseket és az adatkarbantartást. Ez a benchmark a döntéstámogató

    rendszereket illusztrálja, melyekre jellemző, hogy:

    nagy mennyiségű adatokat elemeznek;

    való üzleti kérdésekre adnak választ;

    magas CPU és I/O terhelésnek vannak kitéve;

    a végrehajtott lekérdezések széles üzemeltetési követelménnyel és

    bonyolultsággal bírnak.

  • Java alapú SQL API felületek hatékonyság elemzése

    21

    TPC-H szintén egy döntést támogató benchmark. Az általa jelzett

    teljesítménymérési mutató a Query-per-Hour Performance metrika. Ez

    meghatározza, hogy az adott méretű adatbázison, melyen a lekérdezéseket

    végrehajtjuk, mekkora a lekérdezés végrehajtási sebessége, ha a lekérdezések

    egyetlen szálon vagy konkurens módon hajtódnak végre.

    TPC-VMS célja egy virtuális környezet reprezentálása, ahol 3 adatbázis fut

    egy szerveren. Teszteléskor választanak egy benchmarkot a fentebb bemutatottak

    közül, majd futtatják mind a három adatbázison. A három adatbázisnak minden

    paraméterben egyeznie kell. A 3 példányon lefuttatott benchmark eredményeinek

    minimum értéke adja meg az elsődleges teljesítménymutatót.

    TPCx-HS a Big Data technológia tesztelésére alkalmas. A benchmark a hét

    minden napján 24 órában folyamatosan rendelkezésre álló rendszert modellez.

    Lényegében arra használható, hogy felmérjék a széles körű rendszer-topológiákat

    és végrehajtható módszereket technikailag szigorú és közvetlenül

    összehasonlítható, gyártótól független módon.

    A TPC teszteken kívül léteznek más teljesítmény tesztek is. A MySQL a

    saját adatbázis-kezelőihez kínál különböző benchmark tool-okat. Például a DBT2,

    ami egy nyílt forráskódú benchmark. Lényegében egy OLTP alkalmazást szimulál

    egy nagy raktárkészlettel rendelkező cég számára. Öt különböző tranzakciót

    tartalmaz, melyek között van olvasási és írási is. Tesztelhető vele egy MySQL

    Server példány, vagy végezhetőek vele nagy, elosztott tesztek számos MySQL

    Cluster node-al és MySQL Server példánnyal. Ehhez a DBT2 szkripteket biztosít,

    amelyek automatizálják a benchmark folyamatot.

    MySQL esetén másik alternatíva a FlexAsynch, amely speciálisan a MySQL

    Cluster-ek skálázhatóságának tesztelésére lett kifejlesztve. Továbbá létezik a

    SysBench is, amely egy népszerű nyílt forráskódú benchmark nyílt forráskódú

    adatbázis-kezelők tesztelésére. Segítségével egyetlen MySQL Server példány

    tesztelhető InnoDB vagy MyISAM futási módban. [17]

    NoSQL adatbázis-kezelők esetében is léteznek benchmark tesztek. Például

    az YCSB (Yahoo Cloud Serving Benchmark), melyet a Yahoo kutatási részlegén

    dolgozók fejlesztettek ki 2010-ben. Az eredeti célja az volt, hogy megkönnyítse az

    új generációs felhő adattároló rendszerek teljesítmény összehasonlítását, különös

    tekintettel a tranzakció feldolgozásból adódó terhelésre. Manapság azonban

    gyakran használják NoSQL adatbázis-kezelők relatív teljesítményének

  • Java alapú SQL API felületek hatékonyság elemzése

    22

    összehasonlítására. Az interneten konkrét eredményeket, diagramokat lehet

    találni NoSQL adatbázisokon elvégzett benchmark tesztekről. [18]

    Látható tehát, hogy az adatbázis-kezelő rendszerek esetében számos adat

    áll a fejlesztő rendelkezésére, melyek segítségével ki tudja választani a számára

    megfelelőt.

    Az SQL API felületeket tekintve viszont ez nem mondható el

    maradéktalanul. Információk után kutatva arra jutottam, hogy ezen a területen az a

    jellemzőbb, hogy egy adott SQL API-n belüli különböző lehetőségeket vizsgálják.

    Erre példa a JDBC driverek összevetése vagy az egyes ORM (Object-relational

    mapping) programkönyvtárak elemzése.

    Tehát a különböző SQL API-k esetében nem igazán található

    összehasonlítás és teszt. A továbbiakban a korábban általánosságban ismertetett

    Java alapú SQL API felületeket szeretném tesztelni.

  • Java alapú SQL API felületek hatékonyság elemzése

    23

    4. Tesztrendszer megtervezése

    Bármilyen tevékenység előtt fontos meghatározni és kitűzni különböző

    célokat. Természetesen ez a teszteléseknél sem lehet másképpen. Ha nem

    definiálunk célokat, akkor tulajdonképpen nem határozzuk meg, hogy a tesztek

    végén mit szeretnénk elérni. Ez mindenképpen hiba volna.

    4.1 Tesztelés célja

    A teszt fő célja a Java gazdanyelven rendelkezésre álló SQL API modulok

    teljesítmény- és funkcióvizsgálata. Másodlagos cél, a teszt végén a kapott

    eredmények összehasonlítása és ezek alapján valamiféle döntéstámogatás.

    Másképpen kifejezve, segítségnyújtás azok számára, akik SQL API felület

    választása előtt állnak a saját alkalmazói programjuk fejlesztésénél.

    Ki kell emelni, hogy ezen teszt nem lesz teljes körű. Egy-egy API

    önmagában is széleskörű szolgáltatásokkal bír, melyeket teljes mértékben

    körüljárni, kipróbálni nagy feladat lenne. Egy szakdolgozat keretén belül erre nincs

    lehetőség.

    Emiatt azt a módszert választottam, hogy néhány lényeges elemre

    helyezem a hangsúlyt, melyeket megvizsgálok minden egyes API esetében. Ezzel

    szeretnék egy viszonylag átfogó képet adni, ami talán megkönnyítheti a döntést

    egy fejlesztő számára.

    4.2 Mérhető vagy vizsgálható tulajdonságok

    A célok meghatározása után, érdemes konkretizálni azt a néhány elemet,

    melyet a teszt során kiemelek.

    Véleményem szerint minden egyes SQL API felület esetében két fő

    irányban érdemes vizsgálódni. Az egyik lehetőség a hatékonyság, és azon belül a

    teljesítmény mérése és összevetése. A másik követhető út pedig a funkcionalitás

    elemzése lehet.

    Teljesítmény kifejezésére többféle mértékegység létezik (pl. Watt). Az

    informatikában releváns teljesítménymutató az idő. Érdemes tehát vizsgálni, hogy

    milyen gyorsan, azaz mennyi idő alatt hajtódnak végre az előre definiált

    műveletsorok.

  • Java alapú SQL API felületek hatékonyság elemzése

    24

    Az SQL API felületekre konkretizálva, egy lehetséges szempont az, hogy

    mennyi idő alatt jön létre sikeres kapcsolat az adatbázis és az alkalmazói program

    között attól függően, hogy melyik API segítségével kapcsolódunk.

    Továbbiakban mérhetjük az adatmanipulációs műveletek végrehajtási idejét

    is a méret függvényében. Tehát attól függően, hogy melyik SQL API-val

    dolgozunk, eltérés adódhat a beszúrás és módosítás műveletek sebességében.

    Ez a sebesség függhet attól is, hogy mennyi adatot kell egyszerre beszúrni vagy

    módosítani. Módosítás esetén további befolyásoló tényező lehet a módosítás

    mértéke is. Ezalatt azt értem, hogy - egy rekordra nézve - hány mezőt kell

    módosítanunk.

    Szintén mérhető az adott API-k esetében az adatlekérdező utasítások

    végrehajtási ideje. Eltérő eredményeket kaphatunk, ha egy teljes táblát,

    összekapcsolt táblákat (join művelet) kérdezünk le vagy valamilyen feltétel alapján

    szűrjük az adatokat. Természetesen ez is függhet a mérettől. Ugyanis nem

    mindegy, hogy a lekérdezéskor hány rekordon kell végigfutni, illetve hány rekord

    lesz az eredményhalmazban.

    Az eddigiekben a teljesítmény szempontjából vizsgálható paramétereket

    vettem sorra. Korábban említettem, hogy a másik követhető út a funkcionalitás.

    Ide tartozhat az adott SQL API-val megvalósítható műveletek köre. Például van-e

    lehetőségünk az adott API-val tárolt eljárásokat, függvényeket meghívni.

    Lehet vizsgálni az adott API használatának egyszerűségét is. Ez egy

    nagyon szubjektív szempont, ugyanis nem minden embernek jelenti ugyanazt az

    egyszerűség. Jelen esetben az egyszerűség "mérésére" feltérképezhetjük a

    működéshez szükséges osztályok és metódusok számát, valamint összevethetjük

    a kód hosszát is.

    4.2.1 Tesztelési környezet

    Egy jégkocka olvadási ideje szobahőmérsékleten valószínűleg lassabb

    lesz, mint egy 90 °C-ra felfűtött szaunában. Éppen ezért minden teszt vagy

    kísérlet előtt fontos tisztázni a tesztelési körülményeket.

  • Java alapú SQL API felületek hatékonyság elemzése

    25

    A teszt szempontjából nem közömbös, hogy milyen operációs rendszeren,

    milyen CPU teljesítmény stb. mellett fut. Éppen ezért, meghatározó tényező lehet

    annak a hardver- és szoftverkörnyezetnek a jellemzői, melyen a tesztelés zajlik.

    A vizsgálataim során alkalmazott hardver specifikációja:

    CPU: Intel Core i7-4790 processzor (4 mag; 8 szál; 3,6 GHz)

    Alaplap: Gigabyte H97M-HD3

    Memória kapacitás: 8 GB

    Szoftver oldali specifikáció:

    Operációs rendszer: Windows 7 Professional, 64 bit

    Fejlesztő környezet:

    o Eclipse Luna

    o IBM Data Studio 4.1.1

    o Oracle Database 11g Express Edition

    Választásom azért esett az Eclipse-re, mert ezt a fejlesztő környezetet

    használtam már, ezt ismerem a legjobban. Természetesen az Eclipse-nek több

    verziója is van (Juno, Kepler, Luna), de mivel a JINQ API csak a Java 8-as

    verziójával kompatibilis, ezért olyan Eclipse-re volt szükségem, amely 1.8-as

    fordítóval rendelkezik. Jelenleg erre - bármiféle egyéb trükközés nélkül - csak a

    Luna verzió képes.

    Az IBM Data Studio használatát az SQLJ felület tesztelése indokolja.

    Alapvetően az Eclipse fejlesztő környezet nem ismeri fel a .sqlj kiterjesztésű

    fájlokat, mert nem rendelkezik integrált SQLJ fordítóval. Első körben próbáltam

    megoldásokat keresni arra, hogy az Eclipse-ben tudjak SQLJ-t használni. Nem

    találtam semmilyen alkalmas plugint ehhez. Csupán egy Maven pluginra

    bukkantam, de ennek használatát bonyolultnak tartottam. Ekkor akadtam rá az

    IBM Data Studiora, amely lényegében egy Eclipse környezet, kiegészítve néhány

    plusz funkcióval az adatbázisok és adatkezelés terén. Ez a megoldás

    egyszerűbbnek tűnt, ezért választottam az IBM Data Studio használatát.

    Jogosan merülhet fel a kérdés, hogy ha az IBM Data Studio is Eclipse

    alapú, akkor miért van szükség külön Eclipse környezetre is és miért nem elég

    csak az IBM Data Studio. Erre a magyarázat szintén a JINQ, ugyanis az IBM Data

  • Java alapú SQL API felületek hatékonyság elemzése

    26

    Studioba integrált Eclipse csak 1.7-es fordítóval rendelkezik, ami a JINQ

    kipróbáláshoz nem elegendő.

    Szintén fontos tényező az adatbázis-kezelő rendszer kiválasztása. A

    jelenlegi esetben viszonylag egyszerű dolgom volt, ugyanis az SQLJ felületet csak

    az Oracle adatbázis-kezelő rendszer támogatja. A gyártó honlapján elérhető

    egyetlen ingyenes verzió az Oracle Database 11g Express Edition volt, így más

    lehetséges alternatíva nem jöhetett szóba.

    4.3 Tesztelés módszere

    Először egy tesztadatbázist kell létrehozni, majd egy alkalmazói programot.

    Az alkalmazói programnak képesnek kell lennie a különféle műveletek

    kiválasztására és azok végrehajtására. Ezek után az alkalmazói programot

    összekapcsoljuk a teszt adatbázissal a különböző SQL API felületeket használva.

    Ha ez sikerült, akkor valamilyen módon meg kell tudnunk mérni a műveletsorok

    végrehajtási idejét.

    Az idő mérését szintén az alkalmazói programban valósítom meg, minden

    esetben a rendszeridőt alapul véve. Mielőtt elindul a műveletsor végrehajtása

    lekérem a rendszeridőt, majd miután teljesült a műveletsor, ismét lekérem a

    rendszeridőt. A két idő különbsége fogja megadni a műveletsor végrehajtási idejét.

    A mért értékek minden esetben másodperc mértékegységben lesznek megadva.

    A mérés hibájának kiküszöbölése - pontosság javítása - érdekében minden

    mérést 10 egymást követő alkalommal fogok megismételni. A mért értékeket

    táblázatban rögzítem, majd különféle statisztikai mutatókat számolok belőlük.

    Ezen mutatók legyenek a következők:

    mért értékek számtani középértéke (átlaga);

    mért értékek szórása;

    mért értékek terjedelme.

  • Java alapú SQL API felületek hatékonyság elemzése

    27

    Számtani vagy aritmetikai középértéken n darab szám átlagát, vagyis a

    számok összegének n-ed részét értjük. Kiszámítása:

    A szórás az egyes értékek számtani átlagtól vett eltéréseinek négyzetes

    átlaga, vagyis megmutatja, hogy az ismert értékek mennyivel térnek el átlagosan

    az átlagtól. Kiszámítása:

    A legnagyobb illetve a legkisebb mért érték különbségét terjedelemnek

    nevezzük. Kiszámítása:

    A mérés és a statisztikai mutatók kiszámítása után, összegzésként készítek

    néhány szemléltető diagramot. Ezt követően értékelem a kapott eredményeket.

  • Java alapú SQL API felületek hatékonyság elemzése

    28

    5. Implementáció

    Ebben a fejezetben a tényleges megvalósításról, az alkalmazói program

    kialakításáról és az egyes SQL API felületekkel történő működtetéséről lesz szó.

    5.1 Általában az adatbázis programozásról

    Az alkalmazói programon keresztül történő adatkezelés lépései (6. ábra):

    1. Kapcsolódás az adatbázishoz: kapcsolódás paramétereinek

    megadása.

    2. Lekérdezés: magába foglalja az SQL kérés összeállítását és

    elküldését.

    3. Eredmények feldolgozása: ebben a lépésben végezzük el az

    eredményhalmaz rekordonkénti bejárását, valamint az értékeket

    programváltozókhoz rendeljük.

    4. Hibakezelés: feldolgozzuk az esetleges alkalmazás vagy adatbázis

    hibákat, illetve elkapjuk a keletkezett kivételeket.

    5. Kapcsolat bontása, erőforrások felszabadítása: lezárjuk az

    eredményhalmazt, az SQL kérést és a kapcsolatot.

    6. ábra: alkalmazói program kapcsolódása az adatbázishoz SQL API-val.

    5.2 Tesztadatbázis létrehozása

    A teljesítménymérés szempontjából lényegtelen az adatbázis szerkezete és

    a benne lévő adatok jelentéstartalma. Az egyetlen kritérium a join művelet

    teszteléséből fakad, azaz az adatbázisban legalább két táblának kell lennie.

  • Java alapú SQL API felületek hatékonyság elemzése

    29

    Azért, hogy mégse legyen teljesen "értelmetlen" az adatbázis, egy egyszerű

    sémát találtam ki, melynek relációs modelljét a 7. ábra szemlélteti.

    7. ábra: a tesztadatbázis relációs modellje.

    Fontos az ábrán jelzett mezők típusa, hiszen a különböző műveletek

    tesztelésénél ügyelnünk kell arra, hogy a véletlenszerűen generált adatok típusa

    és a mezők típusa megegyezzen. Például a Person tábla Name mezőjénél a

    generált adatoknak szöveges típusúaknak, míg az Age mező esetében egész

    számoknak kell lenniük, különben hiba keletkezik.

    Az ábrán az is látható, hogy a táblák között 1:N kapcsolat áll fent, amely

    lehetővé teszi majd a későbbiekben a join művelet tesztelését. Az 1:N kapcsolat

    azt jelenti, hogy a Person tábla Workspace mezője csak olyan értéket vehet fel,

    amely már létezik a Workspace tábla Name oszlopában, egyéb esetben hibát

    kapunk. Ha az adatokat véletlenszerűen generáljuk, akkor előfordulhat olyan eset,

    amikor a két érték nem egyezik meg. Annak érdekében, hogy az adategyezés

    mindig teljesüljön, a Workspace táblát inicializáltam néhány rekorddal. A Person

    táblába történő beszúrás előtt, lekérdezem a Workspace tábla Name oszlopát,

    majd az eredményhalmazból véletlenszerűen kiválasztok egyet. A kiválasztott

    adat lesz a beszúrandó rekord Workspace mezőjének értéke.

    Tehát a teszt elején az induló adatbázis két táblával rendelkezik. A

    Workspace táblában található néhány rekord, míg a Person tábla teljesen üres.

    5.3 Az alkalmazói program implementálása

    Mivel szerettem volna, hogy a négy API használata élesen elkülönüljön

    egymástól, ezért mindegyik API-hoz külön hoztam létre egy-egy alkalmazói

    programot. Ezt a döntést a kód átláthatóságával, valamint a JINQ és az SQLJ által

    igényelt körülményekkel indokolnám (vö.: 4.1 Tesztelési környezet).

  • Java alapú SQL API felületek hatékonyság elemzése

    30

    Ahhoz azonban, hogy a teszt szempontjából lényeges időt ne befolyásoljam

    (vagy annak mértékét minimálisra csökkentsem) az utasítások számával, a

    program API-tól független részeit ugyanúgy implementáltam.

    Ilyen független rész maga a program felépítése, beleértve a felhasználói

    felületet és a vezérlő szerkezeteket is. Igyekeztem egy letisztult, bárki számára

    könnyen kezelhető GUI-t összerakni. Ehhez a SWING widget toolkitet hívtam

    segítségül. A sikeres használathoz az Eclipse-hez telepíteni kellett egy

    Windowbuilder plugint. Ezután már könnyedén alkalmazhattam a SWING nyújtotta

    grafikus komponenseket.

    Az alkalmazás felhasználói felülete lényegében három osztályból áll,

    melyeket a frames csomag tartalmaz:

    FrmMain: az alkalmazás főablaka;

    FrmTable: a lekérdezés eredményeit megjelenítő ablak;

    FrmStatistic: a mért eredményeket és a statisztikai mutatókat

    megjelenítő ablak;

    FrmLogin: JDBC és SQLJ API esetében a kapcsolódáshoz szükséges

    felhasználónév és jelszó megadására szolgáló ablak.

    8. ábra: az alkalmazás főablakának (FrmMain) képe.

    A 8. ábra az alkalmazás fő ablakának felépítését (FrmMain) szemlélteti,

    amely három menüpontot tartalmaz a beszúrás, módosítás és lekérdezés

    műveletek kiválasztásához. Ha kiválasztjuk a tesztelni kívánt műveletet, akkor

    rádió gombok segítségével jelölhetjük ki, hogy hány rekordot szeretnénk beszúrni,

  • Java alapú SQL API felületek hatékonyság elemzése

    31

    melyik táblát szeretnénk lekérdezni vagy mely mezőket szeretnénk módosítani.

    Mindhárom műveletnél megtalálható egy gomb is a felületen, melyre rákattintva

    elindíthatjuk a kiválasztott műveletsor végrehajtását.

    További közös GUI elem a lekérdezések eredményeit megjelenítő táblázat

    ablak (FrmTable). Az ablakban a táblázat fejléce aszerint változik, hogy a

    lekérdezés műveletet az adatbázis mely tábláján vagy tábláin hajtottuk végre.

    Tehát, ha a lekérdezés a Person táblára vonatkozott, akkor a táblázat fejléce a

    Person tábla oszlopainak nevét fogja tartalmazni. Ugyanez mondható el a

    Workspace tábla esetében is. Join művelet végrehajtása során a tábla fejléce a

    Person és Workspace tábla oszlop neveinek együtteséből fog állni. Ezt

    szemléltetem a 9. ábra segítségével.

    9. ábra: a Workspace tábla lekérdezésének eredményét megjelenítő ablak (FrmTable).

    Az ablak fejlécében látható felirat megmutatja, hogy melyik SQL API-t

    használtuk a lekérdezéshez, valamint jelzi az eredményhalmaz számosságát is.

    A harmadik közös GUI elem (FrmStatistic) a mért adatok táblázatban való

    megjelenítését, valamint az ezen adatokból számolt statisztikai mutatókat jeleníti

    meg. Ahhoz, hogy a mutatók megjelenjenek, a felhasználónak meg kell nyomnia a

    "Mutatók kiszámítása" feliratú gombot. Az ablak fejlécében szintén olvasható,

    hogy melyik API statisztikáját látjuk. Ezt szemlélteti a 10. ábra.

  • Java alapú SQL API felületek hatékonyság elemzése

    32

    10. ábra: a statisztikai eredményeket megjelenítő ablak (FrmStatistic).

    A korábban említett frames csomagon kívül, minden alkalmazásban van

    egy technológia_api csomag is, például: jdbc_api, jpa_api. Ebben a csomagban

    minden esetben megtalálható az alábbi két osztály:

    StatisticMaker osztály;

    DataManager osztály.

    A StatisticMaker osztály minden SQL API esetében azonos kivitelezésű és

    ugyanazokat a metódusokat tartalmazza. A metódusok ugyanúgy vannak

    megvalósítva minden API esetében, hiszen az osztály feladata független attól,

    hogy melyik SQL API felületet használjuk. Az osztály metódusai a következők:

    getAvarage(): paraméterként megkapja a mért értékek listáját (egész

    számokra tipizált List objektumot). A paraméter alapján, egy ciklus

    segítségével összegzi a lista elemeit, majd elosztja az elemszámmal.

    Tehát ez a metódus a mért értékek átlagával tér vissza.

    getDeviation(): paraméterként ugyanazon mért értékek listáját kapja

    meg, mint a getAvarage(). A metódus törzsén belül meghívódik a

    getAvarage() metódus, mely visszatér az átlaggal. Az átlag alapján

    pedig egy ciklus segítségével kiszámoljuk a szórást. Tehát ez a

    metódus lényegében a mért értékek szórásával tér vissza.

    getRange(): paraméterként szintén a mért értékek listáját kapja meg.

    Ezután ciklusok segítségével megkeresi a maximum és minimum

  • Java alapú SQL API felületek hatékonyság elemzése

    33

    értékét, majd veszi ezek különbségét. Tehát a metódus lényegében a

    mért értékek terjedelmével tér vissza.

    A DataManager osztály már nem független az egyes SQL API felületektől,

    hiszen ebben az osztályban valósítom meg az egyes műveleteket. Ennek ellenére

    az egyes SQL API felületekre nézve, az osztály felépítése ugyanolyan.

    Ugyanazon metódusokat tartalmazza, az eltérés a metódusok törzsében van,

    kivétel az adatok generálásért felelős metódus. Tehát az osztály az alábbiakat

    tartalmazza:

    Kapcsolódást tesztelő metódus:

    o Connect(): paraméterként egy egész számot kap. Ezután

    annyiszor kapcsolódik az adatbázishoz, amennyi a

    paraméter értéke. Visszatérési értéke logikai típusú.

    Amennyiben a kapcsolódás sikeres volt, akkor true

    konstanssal tér vissza, egyéb esetben false konstanst ad

    vissza.

    Adatok generálásáért felelős metódus:

    o stringGenerator(): paraméterként egy egész számot kap.

    Ezután egy előre definiált karakterláncból annyi

    véletlenszerű elemet választ ki, mint a paraméterként kapott

    szám. Lényegében az adatbázis szöveges típusú mezőinek

    értékét állítja elő és azzal tér vissza.

    o Szám típusú adatok előállítása: erre a Java nyelvbe

    beépített Math osztály random() metódusát használom. Ez a

    metódus 0-1 közötti véletlen valós számot állít elő. Ezen

    értéket szorzom 100-al, ekkor 0-100 közötti valós számot

    kapok, majd az értéket egész számmá konvertálom.

  • Java alapú SQL API felületek hatékonyság elemzése

    34

    Adatmanipulációért és adatlekérdezéséért felelős metódusok

    általánosan:

    o getWorkpace(): nincs paramétere. Lekérdezi a Workspace

    tábla Name oszlopát, majd visszatér az eredménnyel. A

    metódusra azért van szükség, hogy mindig megfelelően

    történjen az adategyezés a táblák közötti 1:N kapcsolat miatt

    (vö.: 5.1 Tesztadatbázis létrehozása).

    o initTable(): nincs paramétere. A Person táblába történő

    beszúrás előtt inicializálja a táblát. Lényegében az adatok

    törlésével üres táblát biztosít a beszúráshoz. Ha az

    inicializálás (törlés) sikeres volt, akkor true értékkel tér

    vissza. Ha az inicializálás valami miatt nem történt meg,

    akkor false értéket ad vissza.

    o Insert(): paraméterként egy egész számot kap. Ezután a

    Person táblába annyi rekordot szúr be, amennyi a paraméter

    értéke. Ha a művelet sikeres volt, akkor true értékkel,

    egyébként false-al tér vissza.

    o Update(): paraméterként egy szöveget kap. Ez a szöveges

    paraméter megmutatja, hogy a Person tábla mely oszlopát

    vagy oszlopait szeretnénk módosítani. Ha a módosítás

    sikeres volt, akkor true-val tér vissza, egyébként pedig false

    értékkel.

    o Select(): paraméterként szöveget kap. Ez a szöveges

    paraméter megmutatja, hogy a lekérdezés mely táblára

    vonatkozik, illetve jelzi, hogy teljes táblás, egykulcsos (where

    feltétel) vagy join típusú lekérdezésről van-e szó. Ha a

    lekérdezés sikeres volt, akkor a metódus visszatér az

    eredményhalmazzal, ha viszont sikertelen volt, akkor null

    értéket ad vissza.

    Szintén független az SQL API felületektől a műveletek végrehajtási idejének

    mérése. Erre nem hoztam létre külön osztályt, hanem mindig a rendszeridőt alapul

    véve határozom meg egy műveletsor végrehajtási idejét.

  • Java alapú SQL API felületek hatékonyság elemzése

    35

    boolean flag = dm.initTable();

    if(flag == true){

    long start = System.currentTimeMillis();

    long passed;

    if(rdbtnTenThousand.isSelected()){

    boolean bool = dm.Insert(10000);

    if(bool == true){

    long end = System.currentTimeMillis();

    passed = (end-start)/1000;

    }

    }

    }

    11. ábra: az időmérést szemléltető kódrészlet.

    A 11. ábrán lévő kódot megnézve látható, hogy először törlöm a táblát

    (dm.initTable() metódus). Ha ez sikeres volt, akkor lekérem a rendszeridőt és

    eltárolom egy változóba (System.currentTimeMillis() értékének start változóba

    történő tárolása). Mivel csak a tábla törlése után kérem le a rendszeridőt, ezért a

    törlés ideje nem lesz benne a végrehajtási időben. Annak értéke ténylegesen csak

    a beszúrás művelet idejét fogja tükrözni. Ezután megvizsgálom, hogy hány

    rekordot kell beszúrni a táblába, majd meghívom a beszúrásra alkalmas Insert()

    metódust. Amint ez a metódus sikeresen végrehajtódik, akkor ismételten lekérem

    a rendszeridőt és tárolom egy változóba (System.currentTimeMillis() értékének

    end változóba történő tárolása).

    Ezen a ponton érdemes kitekintést ejtenünk a rendszeridőt lekérdező

    metódusról:

    currentTimeMillis(): a metódus visszaadja a hívás pillanatában, 1970.

    január 1. óta eltelt időt milliszekundumban mérve. A visszatérési érték

    típusa egy long típusú szám.

    Visszakanyarodva, jelenleg megvan a két időpillanat milliszekundumban

    mérve. Az eltelt idő - azaz a művelet végrehajtási ideje - a későbbi és a korábbi

    idő különbsége lesz. Mivel az eredményt másodpercben szeretnénk megkapni,

    ezért gondoskodnunk kell a megfelelő átváltásról. Egy milliszekundum egy

    másodpercnek az 1/1000-ed része, tehát a milliszekundum értéket osztani kell

    1000-el, ekkor másodpercben kapjuk meg az értéket.

  • Java alapú SQL API felületek hatékonyság elemzése

    36

    public boolean Connect(String host, String port, String db, String username,

    String password){

    this.host = host;

    this.port = port;

    this.db = db;

    this.username = username;

    this.password = password;

    try{

    DriverManager.registerDriver(new oracle.jdbc.OracleDriver());

    String connstr = "jdbc:oracle:thin:@" + this.host + ":" +

    this.port + ":" + this.db;

    conn = DriverManager.getConnection(connstr, username, password);

    return true;

    }

    catch(SQLException ex){

    return false;

    }

    }

    A következőkben szeretném röviden végigvenni minden egyes SQL API

    felület esetében a konkrét implementáció menetét. Továbbá rátérnék - a korábban

    általánosságban - említett adatmanipulációért és lekérdezésért felelős metódusok

    SQL API specifikus részletezésére.

    5.3.1 JDBC API implementáció

    Az Eclipse Luna megfelelő beállítása után, letöltöttem az Oracle 11g

    Express Edition adatbázis-kezelő rendszerrel kompatibilis drivert az Oracle

    honlapjáról. Ezután létrehoztam egy normál Java projektet az Eclipse-ben, majd a

    számítógépen a projekt mappájába bemásoltam a letöltött drivert (ojdbc6.jar).

    Ezzel a drivert elérhetővé tettem a Java keresési útvonalán.

    Következő lépésben létrehoztam egy Connect osztályt, amely az

    adatbázishoz történő kapcsolatért felelős. Itt regisztráltam a drivert, melyet az

    alábbiakban egy rövid példakóddal szemléltetnék (12. ábra).

    12. ábra: JDBC kapcsolat felépítését szemléltető kódrészlet.

    A Connect() metódus paraméterként megkapja a kapcsolat létrehozásához

    szükséges adatokat. Ezután a try blokkban regisztráljuk a drivert. Majd a

    getConnection() metódussal kapcsolatot kérünk a Driver Manager-től.

    Amennyiben ez sikeres volt, akkor true értékkel térünk vissza. Ha a kapcsolat

    felépítése sikertelen volt, akkor a catch ágban false értékkel térünk vissza.

  • Java alapú SQL API felületek hatékonyság elemzése

    37

    public boolean Insert(int record_number){

    String[] workspaces = getWorkspace();

    try{

    PreparedStatement pstmt = conn.getConn().prepareStatement("insert

    into person values(?, ?, ?, ?)");

    for(int i=0; i

  • Java alapú SQL API felületek hatékonyság elemzése

    38

    Az Update() metódus a paramétertől függően dönti el, hogy melyik mezőt

    vagy mezőket módosítja. Itt is egy PreparedStatement utasítást használok a

    módosítás végrehajtásához. Ez a metódus is logikai értékkel tér vissza.

    A Select() metódus ugyancsak a kapott paraméter alapján dönti el, hogy

    melyik táblán vagy milyen módon szeretnénk végrehajtani a lekérdezést. Ha ez

    sikerül, akkor egyszerű Statement utasítást használok a lekérdezés

    végrehajtásához. Az eredményt egy ResultSet típusú objektumban tárolom el és

    ez lesz a metódus visszatérési értéke is.

    Összességében a JDBC API használatához egyedül a fent említett .jar fájl

    volt szükséges.

    5.3.2 SQLJ API implementáció

    Az IBM Data Studio letöltése és telepítése után - a JDBC analógiájára -

    csináltam egy normál Java projektet, majd hozzáadtam a projekthez a JDBC

    drivert és a fent leírt Connect() osztály segítségével csatlakoztam az

    adatbázishoz.

    Ezután az sqlj_api csomagban létrehoztam egy DataManager.sqlj fájlt.

    Fontosnak tartom kiemelni a fájl kiterjesztését. Ugyanis a fejlesztés során végig a

    DataManager.sqlj fájlt szerkesztettem, ebbe írtam a Java kódot és az SQLJ API

    segítségével az egyes műveleteket. A fejlesztő környezet pedig ezen fájl alapján

    automatikusan generált egy DataManager.java kiterjesztésű fájlt.

    A DataManager.sqlj fájlban elsőként létrehoztam egy DefaultContext

    objektumot. A Connect() metódus implementálása egy az egyben megegyezik a

    JDBC API-nál leírtakkal.

    A getWorkspace() metódus itt is egy String tömbbel tér vissza. Először

    lekérdezem, hogy hány rekord van a Workspace táblában. Ezt egy normál SQL

    utasítás segítségével lehet megtenni. Mindössze arra kell figyelni, hogy #sqlj-vel

    kezdődjön, majd pedig meg kell adni a kontextust. Ezután lekérdeztem a Name

    oszlop értékeit. Ahhoz, hogy az eredményhalmaz elemeit el tudjam tárolni a String

    tömbbe, egy iterátorra volt szükségem. Az iterátort még a metódus előtt

    definiáltam.

  • Java alapú SQL API felületek hatékonyság elemzése

    39

    #sql public iterator PersonIt(int id, String name, int age, String workspace);

    #sql public iterator WorkspaceIt(String name, String city, int numofemp,

    String address);

    #sql public iterator JoinIt(int id, String name, int age, String workspace,

    String city, int numofemp, String address);

    public ResultSet Select(String table){

    try{

    ResultSet rs;

    if(table.equals("person")){

    PersonIt pit = null;

    #sql[ctx] pit = {select * from person};

    rs = pit.getResultSet();

    }

    else if(table.equals("workspace")){

    WorkspaceIt wit = null;

    #sql[ctx] wit = {select * from workspace};

    rs = wit.getResultSet();

    }

    else if(table.equals("join")){

    JoinIt jit = null;

    #sql[ctx] jit = {select * from person inner join workspace

    on person.workspace = workspace.name};

    rs = jit.getResultSet();

    }

    else{

    PersonIt pit = null;

    #sql[ctx] pit = {select * from person where age >50};

    rs = pit.getResultSet();

    }

    return rs;

    }catch(SQLException ex){

    return null;

    }

    }

    Az initTable() metóduson belül egy egyszerű SQL utasítást használtam,

    szintén ügyelve arra, hogy #sqlj-vel és a kontextus megadásával kezdődjön. A

    visszatérési érték logikai típusú.

    Az Insert() metódus szinte ugyanúgy működik, mint a JDBC esetében. Az

    Update() metódus esetében is ugyanez mondható el.

    A Select() metódus ResultSet objektummal tér vissza. Ahhoz azonban,

    hogy ez megvalósulhasson plusz lépéseket kellett beiktatni a JDBC-hez képest.

    Elsőként iterátorok definiálására volt szükség. Ezt követően az iterátorkból sikerült

    ResultSet típusú objektumban eltárolni az eredményhalmazt. Ezt az alábbi

    példakóddal szemléltetem (14. ábra).

    14. ábra: a Select() metódus megvalósítása SQLJ-vel.

    Összességében elmondható, hogy az SQLJ nagyban hasonlít a JDBC

    használatához. Talán az iterátorok jelentik az egyetlen különbséget.

  • Java alapú SQL API felületek hatékonyság elemzése

    40

    5.3.3 JPA API implementáció

    Az Eclipse-ben egy JPA projektet hoztam létre, melyhez az eddigieknél

    több előkészületre volt szükség. Le kellett töltenem egy objektum-relációs

    leképzést biztosító programkönyvtárat. Az ingyenesen elérhető alternatívák közül

    az Eclipselink-et választottam. A projekten belül létrehoztam egy User Library-t

    eclipselink néven, melybe bemásoltam az alábbi két .jar fájlt: eclipselink.jar és

    javax.persistence_2.1.0.v201304241213.jar. Továbbá itt is szükség volt a

    korábban is használt JDBC driver alkalmazására.

    A projekten belül létezik egy META-INF könyvtár, amiben van egy

    persistence.xml állomány. Ebben az XML fájlban inicializáltam az adatbázis

    kapcsolatot.

    Ezután létrehoztam egy entities nevű csomagot. Ebbe a csomagba, az

    Eclipse segítségével generáltam két Entity osztályt. Az egyik a Person osztály,

    amely az adatbázisban levő Person táblának felel meg, a másik pedig a

    Workspace osztály, ami a Workspace tábla leképzése.

    A következőkben a DataManager osztályt implementáltam. Itt elsőként egy

    EntityManagerFactory-t, majd egy EntityManager-t kellett létrehozni. Ezek után

    következett az egyes metódusok implementálása az osztályban.

    Elsőként a Connect() metódust definiáltam. Itt is egy ciklus fut le annyiszor,

    amennyi a kapott paraméter értéke. A cikluson belül létrehozok egy

    EntityManagerFactory objektumot, majd pedig bezárom azt. Ehhez a

    persistence.xml fájlban definiált kapcsolódási adatokat használom. Ha sikeres volt,

    akkor true értékkel tér vissza a metódus.

    Következőként a getWorkspace() metódust definiáltam, mely egy List

    objektummal tér vissza. A lista Workspace típusú entity objektumokat tárol. A

    lekérdezést az EntityManager createQuery metódusával valósítottam meg.

    Az initTable() metódus törzsében az EntityManager segítségével egy

    tranzakció folyamot nyitok. Ezután az előbb említett createQuery metódus

    segítségével kijelölöm a törlés utasítást. Ez csak akkor hajtódik végre, ha az

    executeUpdate utasítást is kiadjuk, továbbá a tranzakció folyamot commit()

    paranccsal zárjuk. Amennyiben sikeres volt a művelet, akkor a metódus true

    értékkel tér vissza, egyéb esetben pedig false-al.

  • Java alapú SQL API felületek hatékonyság elemzése

    41

    Az Insert() metódus paraméterként egy számot kap, mely alapján eldönti a

    beszúrandó rekordok számát. A metóduson belül elsőként létrehozok egy-egy

    Workspace és Person entity példányt, majd eltárolom a getWorkspace() metódus

    visszatérési értékét. Következő lépésben ciklust indítok, amelyen belül az

    EntityManager segítségével tranzakció folyamot nyitok, majd beállítom a beszúrni

    kívánt Person entity értékeit. Ezek után az EntityManager persist() metódusával

    beszúrom az adott entity-t és a commit() utasítással véglegesítem. A művelet

    sikerességétől függően a metódus logikai értékkel tér vissza. Ezt szemléltetem a

    15. ábrával.

    15. ábra: az Insert() metódus megvalósítása JPA-val.

    Az Update() metódus a kapott paramétertől függően dönti el, hogy melyik

    mezőt vagy mezőket módosítja. Ezután az EntityManager segítségével ismét egy

    tranzakció folyamot nyitok. Ha ez sikeres volt, akkor a következő lépésben a

    creatQuery() metódussal kijelölöm az SQL update utasítást, majd a

    setParameter() metódus segítségével beállítom az SQL update utasítás

    paramétereit. Végül az executeUpdate() és commit() utasításokkal végrehajtom a

    módosítási műveletet. Ez a metódus is logikai értékkel tér vissza a végrehajtott

    művelet sikerességétől függően.

    public boolean Insert(int record_number){

    Person p = new Person();

    Workspace w = new Workspace();

    List list = getWorkspace();

    try{

    for(int i=0; i

  • Java alapú SQL API felületek hatékonyság elemzése

    42

    A Select() metódus paraméterként egy szöveges értéket kap. Ez alapján

    meghatározza, hogy melyik táblákat vagy milyen módon (join vagy where) kell

    elvégezni a lekérdezést. A lekérdezésre itt is a createQuery() metódust

    használom, kivéve a join típusú lekérdezések esetében. Join végrehajtása során a

    creatNativeQuery() utasítást használom, melynek segítségével normál SQL join

    utasítást lehet végrehajtani. Ahhoz, hogy ezt használhassam, létrehoztam egy

    Join entity osztályt, melynek adattagjai a Person és Workspace entity objektumok

    adattagjainak összessége. A metódus minden esetben egy Query típusú

    objektummal tér vissza. Ezt szeretném bemutatni az alábbi kódrészlettel (16.

    ábra).

    16. ábra: a Select() metódus megvalósítása JPA-val.

    Összességében a JPA jól használható az objektum orientált Java nyelvvel,

    hiszen az objektum-relációs leképzés erre nagyszerű lehetőséget ad.

    Szintaktikailag sem bonyolultabb, mint a JDBC API.

    public Query Select(String table){

    try{

    Query query;

    if(table.equals("person")){

    query = em.createQuery("Select p from Person p",

    Person.class);

    }

    else if(table.equals("workspace")){

    query = em.createQuery("Select w from Workspace w",

    Workspace.class);

    }

    else if(table.equals("join")){

    query = em.createNativeQuery("select * from person inner

    join workspace on person.workspace =

    workspace.name", Join.class);

    }

    else{

    query = em.createQuery("Select p from Person p Where p.age

    > 50", Person.class);

    }

    return query;

    }

    catch(Exception ex){

    return null;

    }

    }

  • Java alapú SQL API felületek hatékonyság elemzése

    43

    private EntityManagerFactory factory =

    Persistence.createEntityManagerFactory("szakdolgozat_jinq");

    private JinqJPAStreamProvider provider = new JinqJPAStreamProvider(factory);

    5.3.4 JINQ API implementáció

    A JINQ erősen épít a JPA-ra, ezért az Eclipse-ben itt is egy JPA projektet

    hoztam létre, az előző pontban ismertetett módon. Ahhoz azonban, hogy JINQ

    működjön, további .jar fájlok letöltésére és hozzáadására volt szükség. Ezért jinq

    néven létrehoztam egy User Library-t, amibe bemásoltam az alábbi két .jar fájlt:

    jinq-all.jar és asm-all-5.0.1.jar.

    Ezután létrehoztam a JINQ használatához szükséges provider-t, melyet az

    alábbi kódrészlettel szemléltetek (17. ábra).

    17. ábra: provider létrehozásának kódrészlete.

    Látható, hogy elsőnek létre kell hozni egy EntityManagerFactory

    objektumot, akárcsak JPA esetében. Majd az így létrehozott factory objektumot át

    kell adni paraméterül a JingJPAStreamProvider osztály konstruktorának. Ekkor

    már közvetlenül tudunk kapcsolódni az adatbázishoz és tudjuk használni a JINQ

    nyújtotta lehetőségeket.

    Fontos megemlíteni a JINQ jelenlegi óriási hiányosságát. Ugyanis nem

    támogatja a beszúrás, módosítás és törlés műveleteket. Tehát JINQ-val csak a

    lekérdezés lehetséges. Az adatmanipulációs utasításokat pedig JPA-val kell

    megoldania a fejlesztőnek. Ezért a DataManager osztályban, csak a Select()

    metódus használ JINQ API-t, a többi JPA-val dolgozik.

    Maga a JINQ használata nagyon kényelmes, ugyanis egy sort kellett írni,

    ahhoz, hogy egy teljes táblát lekérdezhessek. Az eredményhalmazt egy tipizált

    List objektumban kaptam meg, ami azonban némi gondot okozott. A Java fordító

    kötelezi a fejlesztőt a List objektum generikus típusának fordítási időben történő

    megadására. Ez jelen esetben azt jelenti, hogy fordítási időben meg kell határozni

    azt, hogy az List objektumban milyen típusú Entity objektumok lesznek. Tehát - a

    korábbiakkal ellentétben - nem volt arra lehetőség, hogy a Select() metóduson

    belül, futás időben döntsem el, melyik táblán akarok lekérdezést futtatni, vagyis

    milyen típusú Entity objektumokból fog állni az eredményhalmaz.

  • Java alapú SQL API felületek hatékonyság elemzése

    44

    public List selectJoin(){

    JinqStream workspaces = provider.streamAll(em,

    Workspace.class);

    List pairs = workspaces.join(w ->

    JinqStream.from(w.getPersons())).toList();

    return pairs;

    }

    Ezért a Select() metódusból tulajdonképpen négy metódust csináltam