Fát építünk

Fát építünk

Fát építünkAz adatok strukturális és könnyen értelmezhető formában való megjelenítése egy szoftver felhasználói felületén átgondolt tervezést igényel. Az adatokhoz hozzá kell jutni, ki kell választani a megfelelő grafikus komponenst, a mögötte lévő adatmodellt, össze kell ezeket kötni. Gyakran előforduló feladat, hogy táblázatosan is ábrázolható adatokból – felhasználva az adatok közötti összefüggéseket és kapcsolatokat – csoportosítva jelenítsünk meg hierarchikusan, fa struktúrában, kinyitható-becsukható formában, ahogyan ezt a felhasználók jól ismerik a fájl- és menürendszereket használva.

Fát építünk kétféleképpen

Adatbázisból, az Oracle HR sémából lekérdezünk két összetartozó nevet: részleg és alkalmazott. A lekérdezés során figyelünk a megfelelő sorrendre, ami a későbbi feldolgozást megkönnyíti. Adatainkat részlegnév szerint növekvő, azon belül alkalmazott neve szerint is növekvő – ábécé szerinti – sorrendbe rendezzük. A vezérlő rétegben két függvényt írunk, amely a modell rétegtől jut hozzá az adatokat tartalmazó generikus listához – átvett paraméterként –, és a visszaadott érték a nézet réteghez kerül.

A csoportváltás algoritmust használjuk, amely 5 blokkból épül fel. A külső ciklus előtti 1. blokk és utáni 5. blokk egyszer hajtódik végre, az előkészítő és lezáró tevékenységek tartoznak ide. A külső ciklus elején és végén található 2. és 4. blokk a belső cikluson kívül fut le, csoportonként, kategóriánként, részlegenként egyszer (most összesen 11-szer mindkettő). A 3. blokk a belső cikluson belül található, és alkalmazottanként egyszer hajtódik végre (most összesen 106-szor).

Háromszintű fát építünk: a gyökérbe (0. szint) fix, beégetett szövegként kerül a cég neve és a teljes létszám. Az 1. szinten jelennek meg a részlegek nevei és a hozzájuk tartozó létszámok. A 2. szint az alkalmazottak neveiből áll.

1. megoldás

A megoldás faKeszit1() függvénye szöveges adatot eredményez. Ez jól használható teszteléshez: megvan-e az összes adat, megfelelő-e a részlegek sorrendje azon belül az alkalmazottak sorrendje, működik-e a csoportosítás, rendben van-e a megszámolás?

A faKeszit1() függvény egy sok lépésben összefűzött (konkatenált) szöveget ad vissza. Az 1. blokkban előkészítjük a fa gyökerét, ami StringBuilder típusú, hiszen sokszor manipuláljuk és inicializáljuk a lista indexelésére használt i ciklusváltozót. A 2. blokkban megjegyezzük az aktuális részleget és előkészítjük az ehhez tartozó alkalmazottak nevét tároló generikus listát ( faReszlegAlkalmazott). Az aktReszleg-hez tartozó alkalmazottak neveit összegyűjtjük a 3. blokkban. Egy részleg feldolgozását a 4. blokkban fejezzük be a fa aktuális 1. és 2. szinten lévő elemeinek szövegbe való beszúrásával. A belső ciklushoz kötődően megszámolást nem kell alkalmaznunk, hiszen az adott részlegben dolgozó alkalmazottak száma a generikus listától elkérhető ( size()). Építünk arra, hogy a külső ciklusból nézve az egymás után végrehajtódó 2. és 4. blokkban az aktReszleg nem változik meg. A 2. blokkban még nem tudjuk a fa aktuális 1. szintjét hozzáfűzni a szöveghez, hiszen a létszám csak a belső ciklusban felépülő kollekciótól kérhető el utólag. Szükséges némi késleltetés, hiszen a szöveg összefűzése és lényegesen egyszerűbb (mint utólag manipulálni megfelelő helyeken). Az 5. blokkban a csoportváltás algoritmushoz kötődő tevékenységünk nincs.

Az 1. megoldás eredménye

2. megoldás

A faKeszit2() függvénynél alkalmazkodunk ahhoz, hogy a JTree vizuális komponenshez DefaultTreeModel observable típusú modell szükséges, így ezzel térünk vissza ( faModell). A fa csomópontjai DefaultMutableTreeNode osztályú objektumok lesznek, amelyeknek a userObject tulajdonsága szükség esetén manipulálható. Az 1 blokkban beszúrjuk a fa gyökerét ( faGyoker), amihez a későbbiekben csatlakozik a fa többi eleme. A 2. blokkban megjegyezzük az aktuális részleget és előkészítjük – megjelenítendő szöveg nélkül – a faReszleg csomópontot. A 3. blokkban fabeli csomópontként a fa 1. szintjén megjelenő részleghez névtelenül hozzáadjuk a fa 2. szintjére kerülő – aktuális részleghez tartozó – alkalmazottak nevét. A 4. blokkban utólag módosítjuk a faReszleg csomópont megjelenítendő szövegét. Az aktuális részleg létszámát itt sem kell külön megszámolni, mert a faReszleg-től elkérhető ( getChildCount()). Az 5. blokkban itt sincs különösebb teendőnk.

A 2. megoldás eredménye

Fát építünk, képernyőkép

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

Attól függően, hogyan jutunk hozzá a megjelenítéshez szükséges adatokhoz, több tanfolyamunkhoz is kapcsolódik a feladat és a modell rétegben mindig másképpen tervezünk és implementálunk:

  • A Java SE szoftverfejlesztő tanfolyam 45-48. óra: Adatbázis-kezelés JDBC alapon, 1. rész alkalmán hagyományos SQL lekérdező utasítást készítünk JDBC környezetben.
  • A Java EE szoftverfejlesztő tanfolyam 25-32. óra: Adatbázis-kezelés JPA alapon alkalommal a perzisztencia szolgáltatásait vetjük be.
  • A Java adatbázis-kezelő tanfolyam 13-16. óra: Konzolos kliensalkalmazás fejlesztése JDBC alapon, 1. rész, 33-36. óra: Grafikus kliensalkalmazás fejlesztése JDBC alapon, 2. rész alkalmain hierarchikus lekérdezéseket használunk.

Táblázatos komponens testreszabása

táblázat logo

táblázat logoA Java programozási nyelv egyik ismert GUI csomagja a swing. Ennek népszerű grafikus komponense az adatok táblázatos megjelenítését biztosító JTable komponens. A táblázatos megjelenítéshez több beállítás is szükséges. A JTable egy MVC komponens, így külön kezelendők a modell, nézet és a vezérlő funkcióihoz kötődő beállítások. A modell tárolja az adatokat például DefaultTableModel típusú objektumban, amiben szétválaszthatók a fejlécben és a többi cellákban található adatok. A nézethez tartozik a betűméret, a cellák színezése, az adatok igazítása, megjelenítése, a gördítősáv. A viselkedést, a felhasználói reakciót a vezérlő határozza meg, például rendezés, görgetés, fókusz, kijelölés, oszlopok sorrendjének cseréje.

Feladat

Készítsünk olyan Java swing-es kliensprogramot, amely tetszőleges adatforrásból (XML vagy JSON a hálózatról, JDBC adatbázis kapcsolatból, ORM leképzésből származó objektumokból) képes az átvett adatok grafikus felületen való táblázatos megjelenítésére JTable komponenssel! Építsünk arra, hogy az adatokon kívül metaadatok is rendelkezésünkre állnak! A megoldás legyen univerzális!

Képernyőkép

OracleHR képernyőkép

Modell

A táblázatos GUI komponenst kezdetben inicializálni kell, illetve a benne tárolt adatok is törölhetők, ha újrahasznosításra kerül a sor:

Ki kell nyerni a tároláshoz és a megjelenítéshez kötődő adatokat (1. lépés). A metaadatokból a for() ciklus előállítja az oszlopTomb-öt, és az oszlopTipusTomb-be kerülnek az Oracle adattípusból Java objektumtípusként megfeleltetett adatok. Előbbi a fejléc feliratainak szövegeit tartalmazza, és az utóbbi befolyásolja az egyes cellákban az igazítást, illetve hatással van adott oszlop rendezésére is:

Ki kell nyerni a tároláshoz és a megjelenítéshez kötődő adatokat (2. lépés). A while() ciklus végigjárja az eredménytábla sorait és Object típusú tömböt állít elő az összetartozó rekord mezőiből. Ezek először generikus listába kerülnek, majd onnan kétdimenziós Object típusú tömbbe:

Mi indokolja a tömbökből álló generikus lista ( adatLista) alkalmazását?

A while() ciklus végrehajtása előtt nem tudjuk lekérdezni, hogy mennyi rekordot kaptunk vissza, így nem tudjuk rögtön az adatTomb-be tenni az adatokat. A Java nyelvben a tömbök mérete fix, és a deklaráció során meg kell adni. Az eredménytábla metaadatai között megtalálható a mezők száma, ami felhasználható a kétdimenziós tömb oszlopszámaként. A generikus lista dinamikus, annyi elemből fog állni, ahány lépésben végrehajtódik a while() ciklus. Ezután a listától lekérdezhető az elemszáma ( adatLista.size()), és ezzel megvan a kétdimenziós tömb sorainak száma, ami eddig hiányzott. Persze használhatnánk Vector-t is a tömbökből álló generikus lista helyett (mert a DefaultTableModel-nek van olyan túlterhelt konstruktora, ami átvenné paraméterként), de ezt inkább nem tesszük, hiszen a Vector már régóta obsolete kollekció.

Előállítjuk a vizuális komponens mögötti adatmodellt. Öröklődéssel kiegészítjük két hasznos függvénnyel, így cellák rajzolása/renderelése és rendezése megkaphatja a szükséges adattípust ( getColumnClass()), valamint letiltható a cellák szerkeszthetősége ( isCellEditable()). Utóbbiak inkább a vezérléshez kötődnek, de modellen keresztül itt és így kell beállítani:

Végül a vizuális komponens mögötti adatmodellt kell átadni:

Nézet

Adott betűtípus, betűstílus és betűméret használható a táblázat fejlécében, celláiban, illetve a betűmérettől függhet a sorok magassága:

Hasznos ha JScrollPane típusú gördítősáv tartozik a táblázathoz, így dinamikusan megjeleníthető/elrejthető a függőleges/vízszintes gördítősáv:

Vezérlés

Az adatokhoz valahogyan hozzá kell jutni. Most JDBC kapcsolatot használunk és az Oracle HR sémából kérdezünk le adatokat, de a forráskód-részlet univerzális. A folyamat a következő:

  • A driver osztályt betöltjük.
  • Autentikációval c kapcsolatot nyitunk az adatbázis-szerver felé.
  • Végrehajtjuk a lekérdező SQL parancsot.
  • Feldolgozzuk az eredményül kapott ResultSet típusú rs objektumot.
  • Végül lezárjuk a c hálózati kapcsolatot.

Ha engedélyezzük, akkor a megjelenő táblázat fejlécében az egyes oszlopok felirataira kattintva elérhetjük, hogy az adott oszlop típusának megfelelően növekvő vagy csökkenő sorrendbe átrendeződjenek az adatok:

A kivételkezelést nem részleteztük a fenti forráskódoknál, de természetesen kötelezően adott.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java SE szoftverfejlesztő tanfolyam 45-52. óra: Adatbázis-kezelés JDBC alapon, illetve Java adatbázis-kezelő tanfolyam 9-12. óra: Oracle HR séma elemzése, 33-36. óra: Grafikus kliensalkalmazás fejlesztése JDBC alapon, 2. rész alkalmaihoz kapcsolódik.

Ki kinek a vezetője?

Organogram logó

HR-organogram-logoAz SQL lekérdezések újabb típusát adják a hierarchikus lekérdezések. Az Oracle adatbázis-szerver már régóta támogatja ezt a lehetőséget. A hierarchia legtöbbször valamilyen fa adatszerkezethez kötődik. Ezek természetesen nem közvetlenül tárolódnak egy normalizált, relációs adatbázisban, de az adatok közötti kapcsolat értelmezése során felépíthető rekurzív módon a fa struktúra.

Hasonló feladat: Organogram készítése, reflexióra építve. Érdemes összehasonlítani a kétféle szemléletmódot.

Ki kinek a vezetője?

Az Oracle HR sémában az EMPLOYEES és DEPARTMENTS táblák között kétirányú 1:N kapcsolat van. Egy EMPLOYEE_ID egyedi kulccsal azonosított alkalmazotthoz tartozik egy nem kötelező DEPARTMENT_ID külső kulcs az EMPLOYEES táblában. Egy kivétellel minden alkalmazott részleghez hozzárendelt.

Oracle HR séma

Egy DEPARTMENT_ID egyedi kulccsal azonosított részleghez tartozik egy nem kötelező MANAGER_ID külső kulcs a DEPARTMENTS táblában. Minden olyan részlegnek van vezetője, amelyikhez legalább egy alkalmazott hozzárendelt. A DEPARTMENTS táblában csak olyan MANAGER_ID szerepelhet, amelyik megtalálható az EMPLOYEES táblában EMPLOYEE_ID-ként. A „legfelsőbb” szinten lévő vezetőnek nincs vezetője.

Előfordulhat, hogy egy-egy részlegen belül többszintű hierarchiát találunk az organogramban, ha a részlegek helyett az alkalmazottak oldaláról közelítjük meg a problémát. Ekkor építhetünk arra, hogy az EMPLOYEES tábla saját magával is kapcsolatban áll (reflexió): egy MANAGER_ID-hez több EMPLOYEE_ID is tartozhat. Másképpen: egy adott vezetőnek több beosztottja is lehet.

A hierarchikus (rekurzív) lekérdező parancs

Ki kinek a vezetője? - Hierarchikus SQL lekérdező parancs

A lekérdező utasítást bele kell építeni egy Java kliensprogramba (MVC architekturális tervezési minta szerint a modell rétegbe), ami JDBC alapon kapcsolódik az Oracle adatbázis-szerver HR sémájához olyan felhasználó nevében, aki csatlakozhat és lekérdezhet. Meg kell tervezni és felügyelni kell a biztonságos kapcsolatot (kivételkezeléssel), annak életciklusát (nyit, lekérdez, zár), valamint gondoskodni kell az eredménytábla megjelenítéséről.

Az eredménytábla (részlet)

Ki kinek a vezetője? - Eredménytábla

A keletkező eredménytábla exportálható Excel-be (XLS, XLSX formátumokba). Az első vagy utolsó oszlop adatait feldolgozva könnyen készíthető egy dinamikus adatmodellel rendelkező, fa struktúrát megjeleníteni képes komponens/felület, ahol szabadon böngészhető a szervezeti hierarchia.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java adatbázis-kezelő tanfolyam 9-12. óra: Oracle HR séma elemzése, 13-16. óra: Konzolos kliensalkalmazás fejlesztése JDBC alapon, 1. rész, 33-36. óra: Grafikus kliensalkalmazás fejlesztése JDBC alapon, 2. rész alkalomhoz kapcsolódik.

Az SQL forráskód formázásához a Free Online SQL Formatter-t használtam.

Tagadós lekérdezések

Tagadós lekérdezések

Tagadós lekérdezésekAz SQL nyelv utasításai többféleképpen csoportosíthatók. Például: adatdefiníciós utasítások (DDL), adatmanipulációs utasítások (DML), lekérdező utasítások (DQL), adatelérést vezérlő nyelv (DCL). A lekérdezések tanítása során másféle rendszerezés is adható. Például a tagadást tartalmazó lekérdezések önálló kategóriát alkothatnak.

Az Oracle HR sémát használjuk és bemutatunk tagadást tartalmazó lekérdezésre öt példát.

Oracle HR séma

1. Kik dolgoznak olyan részlegekben, ahonnan senki sem vett részt korábban projektmunkában?

A DEPARTMENTS és EMPLOYEES táblák között 1:N fokszámú kapcsolat van, és a DEPARTMENT_ID köti össze ezeket. A belső lekérdezés visszaadja azoknak a részlegeknek az azonosítóját ( DEPARTMENT_ID), amelyekből már legalább egyszer legalább egy alkalmazott részt vett korábban projektmunkában (ez most egy 6 elemből álló halmaz). A külső lekérdezésben a NOT IN predikátum – építve a belső lekérdezés eredményeire – megadja azon részlegek nevét ( DEPARTMENT_NAME), illetve az ott dolgozó alkalmazottak nevét ( EMPLOYEE_NAME), ahol az alkalmazotthoz tartozó részleg azonosítója nincs benne a belső lekérdezés által visszaadott eredménytáblában. 5 részlegben 15 alkalmazottra teljesül a feltétel. A NOT IN predikátum adja a tagadást.

SQL-1

Az eredmény:

SQL-1-eredmény

2. Melyek azok a részlegek, ahol nem minden alkalmazott azonos munkakörben dolgozik?

A DEPARTMENTS és EMPLOYEES táblák között 1:N fokszámú kapcsolat van, és a DEPARTMENT_ID köti össze ezeket. A lekérdezés csoportosítást végez részleg azonosítóra és névre ( DEPARTMENT_ID, DEPARTMENT_NAME), és aggregálja – most megszámolja – a csoportban előforduló egyedi munkakör azonosítókat ( JOB_ID), majd ezek közül kihagyja azokat, ahol a megszámolás 1-et ad (másképpen: azokat hagyja meg, ahol a megszámolás különbözik 1-től, 0 nem lehet, igazából 1-nél több) – ez biztosítja a tagadást. 7 részleget kapunk eredményül.

SQL-2

Az eredmény:

3. Kik azok az alkalmazottak, akik az életpálya modell alapján már nem kaphatnak fizetésemelést?

A JOBS és az EMPLOYEES táblák között 1:N fokszámú kapcsolat van, és a JOB_ID köti össze ezeket. Az életpálya modellhez tartozik egy munkakörhöz tartozó minimális és maximális fizetés ( MIN_SALARY és MAX_SALARY). Azok az alkalmazottak listázandók, akiknél a fizetés megegyezik a betöltött munkakörükhöz adható legmagasabb fizetéssel ( SALARY=MAX_SALARY). Ez jelenti a tagadást. Eredményül egyetlen alkalmazottat kapunk.

SQL-3

Az eredmény:

SQL-3-eredmény

4. Kik a nem vezető munkakörben dolgozó alkalmazottak?

A belső lekérdezés 18 olyan alkalmazott vezetőjének azonosítóját ( MANAGER_ID) adja vissza, akik lehetnek részlegvezetők vagy középvezetők. A külső lekérdezés minden olyan alkalmazott azonosítóját és nevét ( EMPLOYEE_ID, EMPLOYEE_NAME) adja vissza, akik nincsenek benne a belső lekérdezés által visszaadott eredményhalmazban. Ez adja a tagadást. Eredményül 89 alkalmazottat kapunk.

SQL-4

Az eredmény:

SQL-4-eredmény

5. Milyen részlegek találhatók nem amerikai régió területén lévő országokban?

A REGIONS, COUNTRIES, LOCATIONS, DEPARTMENTS táblák között balról-jobbra páronként 1:N fokszámú kapcsolat van és ugyanebben a sorrendben a REGION_ID, COUNTRY_ID, LOCATION_ID köti ezeket össze. A belső lekérdezés azért szükséges, mert vannak üres/fiktív részlegek is, amelyeket ki kell hagyni. Az amerikai régió azonosítója 2 ( REGION_ID), ezt adja most a tagadást. Eredményül 3 részleget kapunk 2 országban és 1 régióban.

SQL-5

Az eredmény:

SQL-5-eredmény

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladatok megoldása során nem foglalkoztam külön azzal az egy alkalmazottal, akinek nincs részlege. A feladatok a Java adatbázis-kezelő tanfolyam 13-16. óra: Konzolos kliensalkalmazás fejlesztése JDBC alapon, 1. rész alkalmához és a 33-36. óra: Grafikus kliensalkalmazás fejlesztése JDBC alapon, 1. rész alkalmához kötődnek.

Az SQL forráskód formázásához a Free Online SQL Formatter-t használtam.

Hello World! másképpen

Hello World! - Piet programozási nyelven

Hello World! - Piet programozási nyelvenA programozási nyelvek tanulásának első lépése a „Hello World!” szintaktikájának megismerése, és egyben teszt arra is, hogy megfelelő-e a fejlesztői környezet telepítése, konfigurálása. Megjelenik-e a „Hello World!” a konzolon, felbukkanó ablakban, önálló ablakban, weblapon, üzenetben? Mit kell ezért tenni? Néhány Java példát nézünk erre.

1. Konzolos megoldás

Ez a kiinduló állapot. Futtatva a programot, a konzolon jelenik meg a szöveg.

2. Swing 1. megoldás

Itt felbukkanó párbeszédablakban jelenik meg a szöveg. A JOptionPane ablaka itt önálló, így nincs olyan szülője/tulajdonosa ( null), ahonnan elveheti a fókuszt.

3. Swing 2. megoldás

Itt egy testre szabott JFrame utód készül, alapvető beállításokkal. Az ablak címsorában jelenik meg a szöveg. Az ablak saját magát példányosítja és főablakként viselkedik, vagyis gondoskodik saját maga láthatóságáról, fókusz- és eseménykezeléséről (utóbbi 2 most nincs).

4. JavaFX megoldás

Itt egy testre szabott  Application utód készül, minimál beállításokkal. Az ablak címsorában jelenik meg a szöveg. Az ablak saját magát példányosítja és főablakként viselkedik.

5. Applet megoldás

Böngészőben fut a testre szabott JApplet utód. A weblapon elfoglalt téglalap alakú területen vízszintesen balra és függőlegesen középen jelenik meg a címke komponensben a szöveg.

6. JSP 1. megoldás

Ez egy JSP weboldal automatikusan generált forráskódja. Böngészőben jelenik meg a szöveg.

7. JSP 2. megoldás

Ez egy JSP weboldal egyszerű direktívával a h1 címsorban.

8. Servlet megoldás

Itt egy szervlet által generált weboldal, amely fixen tartalmazza a szöveget.

9. Atipikus 1. megoldás

„Adatbázisból is lekérdezhető” a szöveg.

10. Atipikus 2. megoldás

Ebben az esetben a Java nyelv által biztosított véletlenszám generáló osztályra támaszkodva állítjuk elő a szöveget. Mivel a random objektum által előállított számok csupán a véletlenség látszatát keltik, de valójában egy algoritmus szerint készülnek, ezért előre teljes pontossággal megjósolható a kimenet. Csupán meg kell találni azt a kezdőértéket, ami után „véletlenül” pont a h, e, l, l, o betűk fognak következni. Megismételve a folyamatot egy másik kezdőértékkel, megkapjuk a w, o, r, l, d  betűket is.

A bejegyzéshez tartozó teljes forráskódot – több projektben – ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A példák a Java SE szoftverfejlesztő tanfolyam, a Java EE szoftverfejlesztő tanfolyam és a Java adatbázis-kezelő tanfolyam több alkalmához is kötődnek (kivéve 4. és 5.).

Hivatkozások a témakörben, amelyek más programozási nyelvek példáit is tartalmazzák: