Folytatjuk Tankockák blog bejegyzés sorozatunkat. A csoportba rendezés feladatban 24 db rövidítést, fogalmat kell a megfelelő csoportba húzni. A 3 csoportot az adatbázis-kezelés, a fájlkezelés és a hálózatkezelés adja.
A fájlkezelés témakör és az adatbázis-kezelés témakör JDBC része a Java SE szoftverfejlesztő tanfolyamunkhoz, az adatbázis-kezelés témakör ORM része a Java adatbázis-kezelő tanfolyamunkhoz, a hálózatkezelés a Java EE szoftverfejlesztő tanfolyamunkhoz kötődik. Többször előfordulhat a megoldás során, hogy nem tűnik egyértelműnek egy-egy kulcsszó, osztály, fogalom, rövidítés, azonban kizárásos alapon mégis mindig az.
A 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épek
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:
1
2
JTable tbEredmeny=newJTable();
tbEredmeny.setModel(newDefaultTableModel());
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:
1
tbEredmeny.setModel(dtm);
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:
1
2
3
4
Font betutipus=newFont("Tahoma",Font.PLAIN,18);
tbEredmeny.getTableHeader().setFont(betutipus);
tbEredmeny.setFont(betutipus);
tbEredmeny.setRowHeight(24);
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:
1
spGorditosav.setViewportView(tbEredmeny);
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ő:
Betöltjük a
driver osztályt.
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.
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:
1
tbEredmeny.setAutoCreateRowSorter(true);
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.
Tipikus hibaforrás, ha az osztály és/vagy interfész neve a különböző csomagok esetén megegyezik és megszokásból, rutinból, figyelmetlenségből rossz csomagból importálunk. Nem biztos, hogy rögtön triviális: mi a hiba, miért az a hiba, hogyan oldjuk meg. Például Timer osztály van a java.util és a javax.swing csomagokban is és nagyon nem mindegy, hogy mikor melyiket (és persze mire, hogyan) használjuk.
Hasonlítsuk össze a részlegeket fókuszálva arra, hogy az alkalmazottak mennyire vettek korábban részt projektmunkákban! Hányan igen és hányan nem? Van(nak) olyan részleg(ek), amelyik vezetője egyetlen alkalmazottat sem vont be projektmunkába? Van(nak) olyan részleg(ek), ahonnan mindenki csatlakozott? Vannak a feladatkiosztásban olyan aránytalanságok, amelyek kimutathatók és így a későbbiek során korrigálhatók? Készítsünk egy kimutatást arról, hogy részlegenként hány fő vett részt projektmunkában és mi a létszám! (Persze tudjuk, hogy nem minden munkakörből vonhatók be alkalmazottak.) Milyen projektjeink szoktak lenni? Van olyan részleg, ahol érdemes bővíteni a létszámot, esetleg átcsoportosítani oda erőforrást? Ezekre a kérdésekre keressük a választ.
Tervezés
Az Oracle HR sémában három tábla kapcsolódik a feladathoz:
JOB_HISTORY,
EMPLOYEES,
DEPARTMENTS. A kapcsolatok fokszámai láthatók az alábbi ábrán. Egy részlegben több alkalmazott is lehet. Egy alkalmazott részt vehetett korábban több projektmunkában is.
A
DEPARTMENTS táblában található a részleg azonosítója (
DEPARTMENT_ID, kulcs) és neve (
DEPARTMENT_NAME). A többi adat most nem kell. 11 olyan részleg van, amihez tartozik alkalmazott.
A
JOB_HISTORY tábla tárolja, hogy a már befejeződött projektekben ki (
EMPLOYEE_ID, külső kulcs) és melyik részlegből (
DEPARTMENT_ID, külső kulcs) vett részt. A dátumokat (
START_DATE,
END_DATE) és a munkakör külső kulcsát (
JOB_ID) most nem használjuk. Minden projekt lezárt. 10 lezárt projekt van.
Az
EMPLOYEES táblából szükséges az alkalmazott azonosítója (
EMPLOYEE_ID, kulcs), valamint részlegének azonosítója (
DEPARTMENT_ID, külső kulcs). A többi adatra most nincs szükség, de egy részletesebb – például név szerinti – kimutatáshoz már igen. 106 olyan alkalmazott van, akihez tartozik részleg (1-nek nincs).
Hozzunk létre négy oszlopból álló eredménytáblát:
DEPARTMENT_ID,
DEPARTMENT_NAME,
COUNT_PROJECT_EMPLOYEES,
COUNT_EMPLOYEES. Ennek áttekintésével választ kaphatunk a fenti kérdésekre.
1. megoldás
Induljunk ki abból, hogy a
JOB_HISTORY táblában lévő
DEPARTMENT_ID-hez hozzárendeljük a
DEPARTMENTS táblából a
DEPARTMENT_NAME-t. Ezekre csoportosítva könnyen aggregálható az adott részlegből projektmunkát végző alkalmazottak száma:
COUNT_PROJECT_EMPLOYEES. Végül egy belső lekérdezés (összekapcsolva a
JOB_HISTORY és az
EMPLOYEES táblákat) megadja az adott részleg alkalmazotti létszámát. Az SQL lekérdezés:
A részeredmény:
Ezután állítsuk elő a hiányzó adatokat! Tudjuk, hogy azokban a részlegekben, amelyek
DEPARTMENT_ID-je nem szerepel a
JOB_HISTORY táblában, de szerepel az
EMPLOYEES táblában, azok léteznek, de nem „adtak” projektmunkára alkalmazottat (azaz
COUNT_PROJECT_EMPLOYEES=0). Nevük és alkalmazottaik száma ugyanúgy megadható, ahogyan az előbb. Az SQL lekérdezés:
A részeredmény:
A két részeredményt egyesíteni kell és egyben hasznos
DEPARTMENT_NAME szerint növekvő sorrendbe rendezni az alábbi lekérdező paranccsal:
Az eredmény:
2. megoldás
Kiindulhatunk abból is, hogy a
DEPARTMENTS egy szótártábla, így közvetlenül hozzáférhető a
DEPARTMENT_ID és a
DEPARTMENT_NAME, de össze kell kapcsolni az
EMPLOYEES táblával, hogy csak olyan részlegeket adjon vissza a lekérdezés, ahol van(nak) alkalmazott(ak). Az eredményhez szükséges további két oszlop könnyen aggregálható az adott részlegre vonatkozóan: a
JOB_HISTORY táblában előforduló
EMPLOYEE_ID-k száma adja a
COUNT_PROJECT_EMPLOYEES-t (probléma nélkül tud 0 lenni) és az
EMPLOYEES táblában előforduló
EMPLOYEE_ID-k száma adja a
COUNT_EMPLOYEES-t. A rendezés most is szükséges. Lényegesen tömörebb lekérdező parancsot kapunk:
Az eredményül kapott táblázat megegyezik az 1. megoldás eredményével.
A két megoldás teljesen különböző gondolatmenettel született. Mindkettőben vannak olyan elemek, amelyek – konkrét feladatból általánosítva – univerzálisan használhatók. Természetesen összehasonlítjuk a két megoldás végrehajtási tervét és részletesen elemezzük is.
A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.
Az Oracle HR sémából építünk organogramot, amivel megjeleníthető a szervezeti hierarchia. Személyenként készítünk csomópontokat. (Másképpen is lehetne: például részlegenként.) A megvalósítás során kétszer konvertálunk A-ból B-be. Először az adatbázisból/adatforrásból SQL lekérdezéssel jutunk hozzá a szükséges adatokhoz, amelyeket generikus listába képezzük le. Ezután a listát feldolgozva generálunk HTML fájlt, amely tartalmaz egy Organization Chart diagramot.
Hasonló feladat: Ki kinek a vezetője?, rekurzív lekérdezéssel. Érdemes összehasonlítani a kétféle szemléletmódot.
Tervezés
Most pedig azt használjuk fel, hogy az Oracle HR sémában az
EMPLOYEES táblában reflexió van, amelyet az
EMPLOYEE_ID és a
MANAGER_ID mezők biztosítanak.
Az Organization Chartnál három adatsor adható meg. Ezek most testre szabva (mindegyik szöveges):
'Employee lastname',
'Job ID', valamint jelmagyarázatként további három mező összefűzve:
'Employee name, Department name, Job title'. Az organogramon megjelenő adatok például:
"Raphaely",
"PU_MAN", valamint a csomópontra fókuszálva megjelenő tooltip:
"Employee: Den Raphaely, Department: Purchasing, Job: Purchasing Manager". A
DEPARTMENTS táblából – az
EMPLOYEES-zel a
DEPARTMENT_ID-vel összekötve – megkapjuk a
DEPARTMENT_NAME-t. A
JOBS táblából pedig – az
EMPLOYEES-zel a
JOB_ID-vel összekötve – megkapjuk a
JOB_TITLE-t.
A lekérdező parancs
Az
EMPLOYEE_ID elsődleges kulcs, vagyis kötelező. A
MANAGER_ID nem kötelező, a hierarchia tetején álló vezetőnél ez a mező null értékű. Mivel a
MANAGER_ID nem kötelező, így külön lekérdező parancsban kell előállítani a 15 középvezetőt együtt a 2 felső vezetővel, valamint az egyetlen felső vezetőt, akinek a
MANAGER_ID-ja null. Ezt a két részeredményt össze kell fűzni (
UNION).
Az eredménytábla
Az adatfeldolgozás lépései
Java programozási nyelven kötelező a kivételkezelés a JDBC kapcsolatfelvétel, SQL parancs futtatása, valamint a fájlkezelés során. A
JDBCConnection interfészben definiált szöveges konstansok:
DRIVER,
URL,
USER,
PASSWORD (az adatbázis-szerverrel való kommunikációhoz),
SQL (a lefuttatandó lekérdező parancs). Az
OrganizationChart interfészbe került a
HTML_FILE_PATH (a generálandó HTML fájl
Path útvonala) és a
HTML (konstans váz az organogram testre szabott HTML+JavaScript forráskódja). Az SQL parancs
ResultSet eredménytáblájának feldolgozása során áll elő az
orgChartDataList generikus lista. A HTML konstans szövegben lévő
#OrgChartData# elemet ki kell cserélni a generikus listából Stream API-val dinamikusan összefűzött adatokra. A fenti példa ide kapcsolódó része:
"[{'v':'Raphaely', 'f':'Raphaely<div style="color:red;font-style:bold">PU_MAN</div>'}, 'King', 'Employee: Den Raphaely, Department: Purchasing, Job: Purchasing Manager']". Ezt követően a
java.nio csomag
Files osztályának
write() metódusával fájlba menthető az előállított fájltartalom. A konkrét Java forráskódot most nem részletezem.
Az elkészült organogram
A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.
Weboldalunkon cookie-kat (sütiket) használunk, melyek célja, hogy teljesebb szolgáltatást nyújtsunk látogatóink részére. További böngészésével hozzájárul ezek használatához. ElfogadAdatkezelési szabályzat
Privacy Overview
This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.
Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.