Tartalmi kivonat
Adatbázis kliens alkalmazás NetBeans-ben Szükséges programok: NetBeans PostgreSQL PostgreSQL JDBC Driver Az adatbázis Az adatbázis nem bonyolult, de a célnak megfelel. Adatbázis tulajdonos felhasználó létrehozása CREATE ROLE citizen LOGIN ENCRYPTED PASSWORD md5406dff156756e03e214f3c52c5dec68c NOSUPERUSER INHERIT CREATEDB CREATEROLE; A jelszó citizen. Adatbázis létrehozása CREATE DATABASE WITH OWNER ENCODING = TABLESPACE citizensdb = citizen UTF8 = pg default; Táblák létrehozása A cities tábla CREATE TABLE cities ( city id serial NOT NULL, city name character varying(64) NOT NULL, CONSTRAINT cities pkey PRIMARY KEY (city id), CONSTRAINT cities ukey UNIQUE (city name) ) WITHOUT OIDS; ALTER TABLE cities OWNER TO citizen; A citizens tábla CREATE TABLE citizens ( citizen id serial NOT NULL, citizen name character varying(64) NOT NULL, citizen city id integer NOT NULL, CONSTRAINT citizens pkey PRIMARY KEY (citizen id), CONSTRAINT citizens cities fkey FOREIGN
KEY (citizen city id) REFERENCES cities (city id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION ) WITHOUT OIDS; ALTER TABLE citizens OWNER TO citizen; A projektek létrehozása A programhoz két projektet hozunk létre: a fő alkalmazást, és az adatbázishoz kötött vezérlőinket tartalmazó programkönyvtárat. Ezeket azért tesszük külön programkönyvtárba, hogy újrafelhasználhatóak legyenek más projektekben is. A fő projekt File->New Project. Categories listadobozban kijelöljük a General-t, a Projects listadobozban a Java Application-t. Next A Project Name szövegdobozba beírjuk hogy Citizens, kikapcsoljuk a Create Main Class-t, és Finish. Elsőként a PostgreSQL JDBC Driver-t kell beletenni a projektünkbe. Menjünk be a DB project Properties-be, Libraries, Add JAR/Folder. Keressük meg a postgresql-jdbcjar-t, és adjuk hozzá (Ez nálam Fedora 8-on a /usr/share/java mappában volt.) OK Innentől a DB projektünk képes kapcsolódni postgresql
adatbázishoz. A Source Packages-be hozzunk létre egy új csomagot, a neve citizens. A csomagba hozzunk létre két JFrame Class-t, az egyiknek a neve CitizensFrame, a másiknak CitiesFrame. Most mást nem fogunk csinálni, a többihez már szükség van az új vezérlőinkre. A programkönyvtár létrehozása File->New Project. Categories listadobozban kijelöljük a General-t, a Projects listadobozban a Java Class Library-t. Next A Project Name szövegdobozba beírjuk hogy DB, és Finish Ezután még mindig a Citizens projekt marat az aktív, ez nem baj, berakjuk hozzá függőségnek a DB-t. A Citizens projekten jobbgomb->Properties A Categories listadobozban válasszuk ki a Librariest. Jobboldalt Add Project, válasszuk ki a DB projektünk főkönyvtárát, és OK Mostantól a Citizens projektünk függ a DB-től, ha valamit változtatunk a DB-ben, újra fogja fordítani a DB-t is, és a Citizens-t is. Ezért maradhat nyugodtan a Citizens fő projektnek Ehhez a projekthez
is adjuk hozzá a PostgreSQL JDBC Drivert. A DB projektben a Source Packages-hez hozzunk létre egy db nevű Java Package-t. Ezzel a két alap projektünket el is készítettük. Az adatbázishoz kötött osztályok létrehozása A DBConnection osztály Ez az osztály fogja tárolni az adatbázis kapcsolatunkat. Minden egyes vezérlőnk ehhez az osztályhoz lesz kötve, ezen keresztül tudják frissíteni a megjelenített adatokat. A DB csomagba hozzunk létre egy DBConnection nevű Java Class-t. Az osztályben öt féle adatot fogunk tárolni: host felhasználónév jelszó adatbázisnév kapcsolat Csináljunk négy String típusú változót, rögtön az osztály fejléc után, valamint egy Connection típusút (java.sqlConnection) public class DBConnection { private private private private private String host; String username; String password; String dbName; Connection connection; A Connection-t első körben valószínűleg aláhúzza pirossal, ez azért van,
mert még nem importáltuk. A Connection-ön hobbgomb->Fix Imports, és kész Készítsük el a kapcsolódás/újrakapcsolódás/bontás metódusainkat. public void connect() { if(host != null && dbName != null && username != null && password != null) { disconnect(); try { Class.forName("orgpostgresqlDriver"); } catch(ClassNotFoundException e) { e.printStackTrace(); } try { String url = "jdbc:postgresql://" + host + "/" + dbName; Properties props = new Properties(); props.setProperty("user", username); props.setProperty("password", password); connection = DriverManager.getConnection(url, props); } catch(SQLException e) { e.printStackTrace(); } } } public void reconnect() { disconnect(); connect(); } public void disconnect() { try { if(connection != null && !connection.isClosed()) { connection.close(); } } catch(SQLException e) { e.printStackTrace(); } } Kellenek még a get/set metódusok a
változókhoz: public String getHost() { return host; } public void setHost(String aHost) { host = aHost; reconnect(); } public String getDatabase() { return dbName; } public void setDatabase(String aDbName) { dbName = aDbName; reconnect(); } public String getUsername() { return username; } public void setUsername(String aUsername) { username = aUsername; reconnect(); } public String getPassword() { return password; } public void setPassword(String aPassword) { password = aPassword; reconnect(); } public Connection getConnection() { return connection; } public void setConnection(Connection aConnection) { connection = aConnection; } Elvileg ha lenyitogatjátok a Projects ablakban a DBConnection osztályt, ott van Bean Patterns, és hozzá lehet adni ezeket a tulajdonságokat, megcsinálja a get/set metódusokat is alapból, de állítólag NetBeans 6-ból ki fogják szedni ezt a Bean dolgot, hogy áttervezzék, mert régi, meg nemjó, ezért csináltuk inkább kézzel. Csinálunk
egy isOpen metódust, ezzel vizsgáljuk a DBResultSet-ben, hogy nyitott-e a kapcsolat. public boolean isOpen() { try { if(connection != null && !connection.isClosed()) { return true; } } catch (SQLException ex) { ex.printStackTrace(); } return false; } Most néhány szó arról, mért csináltuk ilyen „automata” csatlakozásosra az egészet. A connect metódusban megnézi hogy minden be van-e állítva, ha igen, akkor megpróbál csatlakozni. A set metódusokban is mindig újracsatlakozunk automatikusan. Ez azért kell, mert a DBConnection osztályunkat ki szeretnénk tenni form-ra, és ott grafikus felületből tudjuk ilyen tulajdonságok párbeszédablakban állítgatni ezeket a dolgokat, a get/set metódusokon keresztül. Ezzel a módszerrel rögtön kapcsolódni is fog az osztályunk, ha minden adatot megadtunk. DBConnection.java-n jobbgomb->Tools->Add to palette, és adjuk hozzá a Beans szekcióhoz, vagy ahova akarjuk. Innentől az osztályunkat
ráhúzhatjuk a form-okra, mint a gombot, stb A DBConnection osztályunk egy láthatatlan bean lesz, az Other Components-nél fog megjelenni a formon. Ha a palettáról ráhúzzuk a form-ra, kell hogy csináljon egy dBConnection1 nevű példányt belőle, és a properties ablakban tudjuk állítgatni a tulajdonságokat. Emiatt kellett a get/set metódusokba beletenni a connect()-et, stb hogy ilyenkor is kapcsolódjon, ha itt állítgatjuk. Ki is lehet próbálni: ha mindent beállítunk, csatlakozik. Egyelőre a DBConnection-el nem foglalkozunk többet, megcsináljuk a DBResultSet osztályunkat. A DBResultSet osztály Ebben az osztályban fogjuk tárolni a lekérdezések eredményeit. Ebből fognak táplálkozni a vezérlőink is. A db csomagban hozzunk lérte egy DBResultSet Java Class-t. Ehhet is kell a Serializable interfész, hogy tudjuk bean ként használni. public class DBResultSet implements Serializable { Ide is négy változó kell: a kapcsolat az sql parancs az
eredményhalmaz a statement (elképzelésem nincs, mi lehet magyarul) private private private private DBConnection connection; String command; ResultSet resultSet; Statement statement; Kellenek open/close/reopen metódusok, hasonlóan a DBConnection connect-ekhez. Az open metódusban megcsináljuk a lekérdezést, és eltároljuk a resultSet válaozóba. public void open() { if(connection != null && connection.isOpen() && command != null) { try { statement = connection.getConnection()createStatement(); resultSet = statement.executeQuery(command); resultSet.next(); } catch(SQLException e) { e.printStackTrace(); } } } A zárás egyértelmű. private void close() { try { if(resultSet != null) { resultSet.close(); resultSet = null; } if(statement != null) { statement.close(); statement = null; } } catch(SQLException e) { e.printStackTrace(); } } Ez is: private void reopen() { close(); open(); } Ide is teszünk isOpen metódust: public boolean isOpen() {
if(resultSet != null) { return true; } return false; } Kellenek még a get/set metódusok: public DBConnection getConnection() { return connection; } public void setConnection(DBConnection aConnection) { connection = aConnection; reopen(); } public String getCommand() { return command; } public void setCommand(String aCommand) { command = aCommand; reopen(); } public ResultSet getResultSet() { return resultSet; } public void setResultSet(ResultSet aResultSet) { resultSet = aResultSet; } Megírjuk még a destuktort, amiben szépen lezárjuk a dolgokat: protected void finalize() throws Throwable { close(); super.finalize(); } Ezzel a nagy rész meg is van. A következőkben az üzenetküldést csináljuk meg a két osztály között Ugye ha a kapcsolat létrejön, szólni kell az eredményhalmaz objektumoknak, hogy megcsinálhatják a lekérdezést stb. Ehhez visszatérünk először a DBConnection osztályhoz A DBConnection osztály üzenetei Hozzunk létre a db Java Package-en
belül egy event Java Package-t. Az event csomagba 3 új osztályt csinálunk: DBConnectionOpenedEvent DBConnectionChangedEvent DBConnectionClosedEvent Ezek az üzenetek fognak menni a DBResultSet-eknek. Természetesen ha Opened eventet kap a DBResultSet, megtudja hogy van kapcsolat, megcsinálja a lekérdezést. A Changed eventre ugyanez Ha Closed, akkor a DBResultSet is lezár. Mindhárom osztály az ActionEvent osztályból lesz származtatva, a konstruktorok paraméterei a küldő objektum, és az üzenet azonosító. public class DBConnectionOpenedEvent extends ActionEvent { public DBConnectionOpenedEvent(Object aSender, int aId) { super(aSender, aId, "DBCONNECTION OPENED"); } } public class DBConnectionChangedEvent extends ActionEvent { public DBConnectionChangedEvent(Object aSender, int aId) { super(aSender, aId, "DBCONNECTION CHANGED"); } } public class DBConnectionClosedEvent extends ActionEvent { public DBConnectionClosedEvent(Object aSource, int
aId) { super(aSource, aId, "DBCONNECTION CLOSED"); } } Ezután teszünk a DBConnection-be egy EventListenerList változót, ebbe fogjuk tárolni azokat a DBResultSet-eket, amik kapcsolatban vannak a DBConnection-en, tehát akiket értesíteni kell, ha vmi változás történik. private EventListenerList eventListenerList = new EventListenerList(); Megcsináljuk a metódusokat, amivel tudunk hozzáadni és elvenni listenert. public void addActionListener(ActionListener aListener) { eventListenerList.add(ActionListenerclass, aListener); } public void removeActionListener(ActionListener aListener) { eventListenerList.remove(ActionListenerclass, aListener); } Hátravannak még az üzenetküldő metódusok. Egy, ami minden listenernek küld egy bizonyos üzenetet, valamint mindhárom üzenetre külön egy-egy. public void fireActionEvent(ActionEvent aEvent) { ActionListener listeners[] = eventListenerList.getListeners(ActionListenerclass); for(int i = 0; i <
listeners.length; i++) { listeners[i].actionPerformed(aEvent); } } public void fireDBConnectionOpenedEvent() { fireActionEvent(new DBConnectionOpenedEvent(this, ActionEvent.ACTION PERFORMED)); } public void fireDBConnectionChangedEvent() { fireActionEvent(new DBConnectionChangedEvent(this, ActionEvent.ACTION PERFORMED)); } public void fireDBConnectionClosedEvent() { fireActionEvent(new DBConnectionClosedEvent(this, ActionEvent.ACTION PERFORMED)); } Már csak annyi van hátra, hogy belerakjuk az üzenetküldéseket a megfelelő helyekre. Először az connect metódusba, a sikeres kapcsolódás után. fireDBConnectionOpenedEvent(); Majd a close metódusba. fireDBConnectionClosedEvent(); A végén a setConnection metódusba. fireDBConnectionChangedEvent(); A DBConnection osztályunk ezzel kész is van, rátérhetünk a DBResultSet üzeneteire. A DBResultSet osztály üzenetei Az előzőekben látott módon létrehozunk a db.event csomagban négy üzenetet: DBResultSetOpened
DBResultSetChanged DBResultSetDataChanged DBResultSetClosed A DBResultSetDataChanged üzenetet később fogjuk használni, majd ha már változtatni is tudunk az adatokon, egyelőre az a cél, hogy megjelenítsük az adatbázisban lévő adatokat. Itt térnék ki arra, hogy egy teljesen általános DBResultSet esetében, a DBConnectionOpenedEvent és DBConnectionChangedEvent event után nem csak újra le kellene kérni az adatokat, ahogy én csinálom, hanem ilyenkor lehetne leellenőrizni, hogy a kapott új kapcsolatban megvan-e minden tábla, amire a DBResultSet-ben megadott parancs hivatkozik stb. Ezzel én most nem foglalkozok, ugyis dobja a kivételt ha gond van, de teljesen korrektre így kellene megírni. A DBResultSet osztályunkat az ActionListener-ből is származtatjuk. public class DBResultSet implements Serializable, ActionListener { Ezután már bele tudjuk rakni a DBConnection EventListener listájába. Kell még egy actionPerformed metódus, ez fog
meghívódni, ha a DBConnection küld valami üzenetet. public void actionPerformed(ActionEvent e) { if(e.getActionCommand()equals("DBCONNECTION OPENED")) { reopen(); } if(e.getActionCommand()equals("DBCONNECTION CHANGED")) { reopen(); } if(e.getActionCommand()equals("DBCONNECTION CLOSED")) { close(); connection = null; } } Ugyanígy, itt is kell EventListener lista, mert a DBResultSet is fog üzeneteket küldeni a hozzá kapcsolt vezérlőkhöz. private EventListenerList eventListenerList = new EventListenerList(); Az többi metódus ugyanaz, vagy hasonló mint a DBConnection esetében, csak itt négy féle üzenetet küldünk. public void addActionListener(ActionListener aListener) { eventListenerList.add(ActionListenerclass, aListener); } public void removeActionListener(ActionListener aListener) { eventListenerList.remove(ActionListenerclass, aListener); } public void fireActionEvent(ActionEvent aEvent) { ActionListener listeners[] =
eventListenerList.getListeners(ActionListenerclass); for(int i = 0; i < listeners.length; i++) { listeners[i].actionPerformed(aEvent); } } public void fireDBResultSetOpenedEvent() { fireActionEvent(new DBResultSetOpenedEvent(this, ActionEvent.ACTION PERFORMED)); } public void fireDBResultSetChangedEvent() { fireActionEvent(new DBResultSetChangedEvent(this, ActionEvent.ACTION PERFORMED)); } public void fireDBResultSetDataChangedEvent() { fireActionEvent(new DBResultSetDataChangedEvent(this, ActionEvent.ACTION PERFORMED)); } public void fireDBResultSetClosedEvent() { fireActionEvent(new DBResultSetClosedEvent(this, ActionEvent.ACTION PERFORMED)); } Most be kell rakni a különböző üzenetek küldését a megfelelő helyekre. Az open metódusba. fireDBResultSetOpenedEvent(); A close metódusba. fireDBResultSetClosedEvent(); A setConnection metódus picit nagyobb átírást igényel. Ha már volt kapcsolat előtte, abból ki kell venni, hogy többet ne üzengessen, ha már nem
is hozzá tartozik a DBResultSet. if(connection != null) { connection.removeActionListener(this); } Ha beállítunk egy új kapcsolatot, az új kapcsolat EventListener listájába be kell tennünk a DBResultSet-et, hogy kapja tőle az üzeneteket. connection.addActionListener(this); Ezzel végeztünk is az unalmas részekkel, következnek a modellek, amik már a vezérlőink adatait fogják szolgáltatni. A modellek Ebben a leírásban két modellt fogok adatbázishoz kötni: PlainDocument (a JTextField modellje) DefaultComboBoxModel (a JComboBox modellje) Kell pár szót szólnunk a java vezérlőkről, és modellekről. A vezérlőket, és a bennük tárolt adatot szétszedték két részre, a vezérlő csak megjelenít, az adatot valójában a modell tárolja. Egyszerű példa: csinálok két szövegmezőt, mindegyiknek ugyanazt a modellt adom meg (ugyanazt a példányt). Ha a modellben átállítom a szöveget, mindkét szövegmezőben meg fog válzotni Minden típusú
vezérlőnek meg van a saját modellje. Ebből következik, hogy nekünk a megfelelő modellt kell összekötni az adatbázissal, a vezérlő akkor már onnan fogja kapni az adatot. A DBDocument osztály Ez az osztályunk lesz az adatbázishoz kötött szövegmezők forrása. Az osztály helye a db csomagban lesz. Ahogy a DBResultSet, a PlainDocument-ből lesz származtatva, és eseményeket is fogadhat. public class DBDocument extends PlainDocument implements ActionListener { Csinálunk két változót: a DBResultSet, amiből a modell veszi az adatot az adatbásis mező neve, amit meg akarunk jeleníteni private DBResultSet resultSet; private String column; Kell egy refresh metódus, ami a DBResultSet alapján frissíti az adatot. A fireInsertUpdate a modellhez csatolt vezérlőknek küld üzenetet, hogy változott a megjelenítendő adat, ez alapból megvan, így működik a modell/view architektúra. Nekünk csak az adatbázis üzeneteket kell külön megírni. public
void refresh() { if(resultSet != null && resultSet.isOpen()) { ResultSet rs = resultSet.getResultSet(); try { String value = rs.getString(column); try { remove(0, getLength()); insertString(0, value, null); fireInsertUpdate(new DefaultDocumentEvent(0, value.length(), DocumentEventEventTypeCHANGE)); } catch(BadLocationException e) { e.printStackTrace(); } } catch(SQLException e) { e.printStackTrace(); } } } Természetesen itt is lesznek get/set metódusok a két változóhoz. public DBResultSet getResultSet() { return resultSet; } public void setResultSet(DBResultSet aResultSet) { if(resultSet != null) { resultSet.removeActionListener(this); } resultSet = aResultSet; resultSet.addActionListener(this); refresh(); } public String getColumn() { return column; } public void setColumn(String aColumn) { column = aColumn; refresh(); } A setResultSet metódusban, ahogy a DBResultSet setConnection-ben, itt is kivesszük a régi DBResultSet listájából, és beletesszük az újéba.
Hátravan még az actionEvent metódus, ugyanúgy, ahogy a DBResultSet-nél, ez fog végrehajtódni, ha a DBResultSet valamilyen üzenetet küld. public void actionPerformed(ActionEvent event) { if(event.getActionCommand()equals("DBRESULTSET OPENED")) { refresh(); } if(event.getActionCommand()equals("DBRESULTSET CHANGED")) { refresh(); } if(event.getActionCommand()equals("DBRESULTSET DATACHANGED")) { refresh(); } if(event.getActionCommand()equals("DBRESULTSET CLOSED")) { try { insertString(0, "", null); } catch(BadLocationException e) { e.printStackTrace(); } } } Ide már belevettük a DBResultSetDataChanged üzenetet is, ha valamilyen másik vezérlő változtatott az adatokot, akkor frissítünk (ezt az eseményt a DBResultSet még nem küldi, később lesz róla szó). Ezzel kész is vagyunk, ki lehet próbálni amit eddig csináltunk. A DBResultSet és a DBDocument vezérlőt is tegyük rá a palettára a Beans szekcióba,
ahogy a DBConnection-t. A próbaalkalmazás Először tegyünk adatokat az adatbázisba, legalább egy várost, és egy embert. Ez mindenképpen kell, mivel a DBResultSet megnyitásánál bentvan egy next(), automatikusan lép az első rekordra, hogy legyen mit megjeleníteni. Ezt majd később kivesszük, egyelőre így kényelmes Nyissuk meg a Citizens projekt CitizensFrame fájlját, tervező nézetben. A palettáról tegyönk a form-ra egy DBConnection, egy DBResultSet ,egy DBDocument és egy JTextField vezérlőt. A JTextField-et helyezzük el ahogy akarjuk, a másik három DB osztály az Instpector-ban az Other Components-nél lesz, mivel nem látható komponensek. A példányok nevei dBConnection1, dBResultSet1 stb, automatikusan adja, át lehet utána nevezni. Állítgassuk be a komponensek tulajdonságait. DBConnection: database = citizens host = hát ez épp ami username = citizen password = citizen DBResultSet: command = SELECT * FROM citizens connection =
válasszuk ki a Bean-t, és dBConnection1 DBDocument: column = citizen name resultSet = dBResultSet1 JTextField: document = dBDocument1 Ha minden jól megy, akkor a tervező nézetben már meg is jelent az első polgárunk neve. Rekordműveletek hozzáadása Mozgás műveletek Ehhez kell négy metódus a DBResultSet osztályba. public void first() { try { resultSet.first(); } catch(SQLException e) { e.printStackTrace(); } fireDBResultSetDataChangedEvent(); } public void prev() { try { resultSet.previous(); } catch(SQLException e) { e.printStackTrace(); } fireDBResultSetDataChangedEvent(); } public void next() { try { resultSet.next(); } catch(SQLException e) { e.printStackTrace(); } fireDBResultSetDataChangedEvent(); } public void last() { try { resultSet.last(); } catch(SQLException e) { e.printStackTrace(); } fireDBResultSetDataChangedEvent(); } A működésük elég egyértelmű: mozognak az eredményhalmazban, és üzennek a vezérlőknek, hogy változás történt.
Ezzel együtt vegyük ki a DBResultSetopen metódusból a next-et, amivel alapból az első rekordra léptünk, hogy üres táblánál ne kavarjon be. A probléma ezzel annyi, hogy alapból a rekordok előtt van a mutató, és szépen kapunk egy kivételt, mikor a programunk indul. Ezt azzal tudjuk orvosolni, hogy a DBDocument osztályban megvizsgáljuk, hogy valós rekordon állunk-e, és csak abban az esetben frissítjük az adatokat. Beteszünk egy feltételt az refresh metódusba if(!rs.isBeforeFirst() && !rsisAfterLast()) { . } Hogy használni is tudjuk őket, tegyünk egy JToolBar-t a CitizensFrame-re. Tegyünk rá négy JButtont, nevezzük át őket firstButton, prevButton, nextButton, lastButton-ra. A gombokon jobbgomb->Events->Action->actionPerformed, ez hozzáadja a forráshoz a gombhoz tartozó metódust, ami végrehajtódik, ha megnyomjuk. A négy metódusba természetesen a mozgás műveleteket hívjuk meg. private void
lastButtonActionPerformed(java.awteventActionEvent evt) { dBResultSet1.last(); } private void nextButtonActionPerformed(java.awteventActionEvent evt) { dBResultSet1.next(); } private void prevButtonActionPerformed(java.awteventActionEvent evt) { dBResultSet1.prev(); } private void firstButtonActionPerformed(java.awteventActionEvent evt) { dBResultSet1.first(); } Ha teszünk be több adatot a citizens táblába, és kipróbáljuk, tudunk mozogni a rekordok között. Új, Frissít és Töröl műveletek Adjunk hozzá az eszköztárunkhoz még három elemet: egy JToggleButton-t, ami az új rekord gomb lesz egy JButton-t, ami a frissítés és mégegy JButton-t, ami a törlés A gombok működése a következő lesz: A hozzáadás gombunk a togglebutton: ha megnyomjuk, átváltunk „új rekord” módba, beírhatjuk az új rekord adatait, és a frissítés gombbal felvehetjük az új rekordot. Ha „új rekord” módban megnyomjuk mégegyszer a hozzáadás gombot
(kikapcsoljuk), az lesz a mégse, ilyenkor nem veszi fel az új rekordot. A frissítés gomb nem „új rekord” módban az aktuális adatokat fogja felírni az adatbázisba (változtatás). A törlés gomb egyértelmű, törölni fogja az aktuális rekordot. Nevezzük át a három gombot addToggleButton, updateButton, és delButton-ra. Hozzunk létre egy DBResultSetDataUpdate eseményt, a szokásos módon. public class DBResultSetDataUpdateEvent extends ActionEvent { public DBResultSetDataUpdateEvent(Object aSender, int aId) { super(aSender, aId, "DBRESULTSET DATAUPDATE"); } } Írjuk meg hozzá a küldő metódust a DBResultSet-ben. public void fireDBResultSetDataUpdateEvent() { fireActionEvent(new DBResultSetDataUpdateEvent(this, ActionEvent.ACTION PERFORMED)); } A DBDocument osztályba tegyünk egy update metódust, ez fogja a szövegmezőben lévő adatot visszaírni a DBResultSet-be. public void update() { if(resultSet != null) { ResultSet rs =
resultSet.getResultSet(); try { if(!rs.isBeforeFirst() && !rsisAfterLast()) { try { rs.updateString(column, getText(0, getLength())); } catch(BadLocationException e) { e.printStackTrace(); } } } catch(SQLException e) { e.printStackTrace(); } } } A DBDocument actionPerformed metódusába írjuk bele az új üzenet kezelését is. if(event.getActionCommand()equals("DBRESULTSET DATAUPDATE")) { update(); } Ezután a DBResultSet osztály update metódusát kell megírni, ami elküldi az üzenetet minden vezérlőnek, és az új adatokat visszaírja az adatbázisba. public void update() { fireDBResultSetDataUpdateEvent(); try { resultSet.updateRow(); } catch (SQLException ex) { ex.printStackTrace(); } } Most az updateButton actionPerformed metódusát kell magírni, hogy hívja az update-et. private void updateButtonActionPerformed(java.awteventActionEvent evt) { if(addToggleButton.getModel()isSelected()) { dBResultSet1.insertRow();
addToggleButton.getModel()setSelected(false); } else { dBResultSet1.update(); } } Ki lehet próbálni, ha átírjuk a polgárunk nevét, és megnyomjuk az update gombot, frissülni fog az adatbázisban. Az if első fele az új rekord, felveszi az adatbázisba, majd kikapcsolja a toggleButtont Most csináljuk meg a törlést, ez a legegyszerűbb. A DBResultSet-be kell egy delete metódus public void delete() { try { resultSet.deleteRow(); } catch(SQLException e) { e.printStackTrace(); } fireDBResultSetDataChangedEvent(); } Ezután a delButton actionPerformed metódusa. Ebbe ugye illene egy rákérdezés hogy tényleg akarjuk-e törölni, most kimarad. private void delButtonActionPerformed(java.awteventActionEvent evt) { dBResultSet1.delete(); } Következik a bonyolultabb rész: az új rekord felvétele. Ehhez három új metódust kell csinálnunk a DBResultSet-be. Az insertMode átvált az „új rekord” módba, az insertRow felveszi az új rekordot, a cancelInsertRow
visszatér normál módba, a rekord felvétele nélkül. public void insertMode() { try { resultSet.afterLast(); fireDBResultSetDataChangedEvent(); resultSet.moveToInsertRow(); } catch (SQLException ex) { ex.printStackTrace(); } } public void insertRow() { try { fireDBResultSetDataUpdateEvent(); resultSet.updateInt("citizen city id", 1); resultSet.insertRow(); fireDBResultSetDataChangedEvent(); } catch (SQLException ex) { ex.printStackTrace(); } } public void cancelInsertRow() { try { resultSet.moveToCurrentRow(); fireDBResultSetDataChangedEvent(); } catch (SQLException ex) { ex.printStackTrace(); } } Az insertMode-ban azért megyünk az afterLast-ra, hogy a vezérlőink tartalma törlődjön, és üres lappal kezdje a felhasználó a rekord felvételét. Így nem kell kézzel kitörölgetni őket előtte Az insertRow-ban a resultSet.updateInt(„citizen citi id”, 1) azért van, mert a városhoz a combobox még nincs kész, és nem engedne null értéket. Így ki tudjuk
próbálni a rekordfelvételt, bár mindenki az 1-es városban fog lakni. Ezt a sort később majd ki kell venni Most még az új rekord gomb vannak hátra a CitizensFrame-ben. private void addToggleButtonItemStateChanged(java.awteventItemEvent evt) { if(addToggleButton.getModel()isSelected()) { dBResultSet1.insertMode(); } else { dBResultSet1.cancelInsertRow(); } } Ezzel kész is a rekord felvétele