Java saját szoftver készítése 12. rész – ModifyDeleteDialog

Nem volt még szó ModifyDeleteDialog-ról, ami, ahogy a neve is mutatja, az elemek módosításához és törléséhez nyújt segítséget. Ez a generikus Java osztály a StudiCore Java tanfolyam alatt megismert tudásra támaszkodik, és programozó képzés záró feladataként, többszöri áttervezésre alakult ki.

A terv

A megrendeléseket rögzítő szoftverem a Vállalkozásainkat, a Vevőinket, a Megrendeléseinket, a különféle Áfa típusokat, és a nekünk dolgozó Munkatársakat tárolja. Ezek felviteléhez külön osztályok készültek a programozás során, melyek megjelenítették a kapcsolódó űrlapot, és elvégezték a háttérben a hozzájuk tartozó feladatokat.

De mi történjen a módosításukkor és a törlésükkor? Mindegyik menü kapott egy kombinált törlés és módosítás menüpontot:

Nyilván módosítani vagy törölni akkor tudunk valamit, ha előtte kapunk egy listát a már meglévő elemekkel, amelyek közül ki lehet választani a törlendő vagy a módosítandó elemet. Ez a lista egy ablak, amelynek egyetlen dolga, hogy felsorolja a már létező elemeket, majd a megnyomott gombnak megfelelő műveletet hajtja végre a kiválasztotton.

A megvalósítás: generikus osztály

A fenti elgondolás már magával hozza a generikusok alkalmazását. Van egy osztályunk, ami mindig egy listát kap, amelynek elemeit mindig ki kell listázni. Nem volt észszerű tehát minden egyes elemtípushoz külön osztályt írni, csináltam egy generikust, mely mindegyiket képes fogadni.

Sajnos az elemek nagy fokú különbözősége miatt csak korlátozottan tudtam használni a generikus elveket, de így is elégedett vagyok. Előbb nézzük, hogy néz ki a ModifyDeleteDialog osztály (ablak) a különféle elemekkel feltöltve:

Nem akartam túlbonyolítani a dolgot, és kismillió adatot feldobni az ablakba, hanem csak azokat tettem bele, ami feltétlenül szükséges az elemek azonosításához. Könnyű belátni, hogy pl. a dolgozóknál a név nem elég, mert simán lehet két Kis János is.

A kód

Megmutatom a kódot is. Ugye a ModifDeleteDialog egy publikus, generikus osztály, amely a JDialog-ot használja az adatok megjelenítésére:

public class ModifyDeleteDialog<T> extends javax.swing.JDialog {

Ez pedig a konstruktora:

public ModifyDeleteDialog(java.awt.Frame parent, boolean modal, List<T> t) {
   super(parent, modal);
   initControllers();
   initComponents();
   this.setPreferredSize(new Dimension(JFrame.MAXIMIZED_HORIZ, JFrame.MAXIMIZED_VERT));
   list = t;
   String name = "";
   if (!t.isEmpty()) {
      for (T tinside : t) {
         name = tinside.getClass().getName();
      }
      switch (name) {
         case "entity.Vat":
            this.setTitle("ÁFA módosítása vagy törlése");
            vatToTable();
            break;
         case "entity.Employee":
            this.setTitle("Dolgozó módosítása vagy törlése");
            employeeToTable();
            break;
         case "entity.Company":
            this.setTitle("Vállalat módosítása vagy törlése");
            companyToTable();
            break;
         case "entity.Customer":
            this.setTitle("Ügyfél módosítása vagy törlése");
            customerToTable();
            break;
         case "entity.Order":
            this.setTitle("Megrendelés módosítása vagy törlése");
            orderToTable();
            break;
         default:
            break;
         }
      } else {
         JOptionPane.showMessageDialog(new javax.swing.JDialog(), "Nincs lekérdezhető adat.");
      }
 }

Ez egy nagyon egyszerű konstruktor. A megkapott List<T> típusától függően végzi el a dolgát, ezzel a generikus megoldással tudom fogadni a List<Company>, a List<Customer>, a List<Order>, a List<Vat> és a List<Employee> listákat is.

A paraméterként megkapott List<T> t listát elmentem egy privát belső listába, majd a getClass().getName() segítségével megkérdem a lista elemeitől az osztályukat. Attól függően, hogy milyen osztályba tartoznak, beállítom a felbukkanó ablak címét (this.setTitle()), majd lefuttatom a megfelelő metódust:

  • vatToTable() -> ez tölti fel a táblázatot Vat objektumokkal
  • employeeToTable() -> ez tölti fel a táblázatot Employee objektumokkal
  • companyToTable() -> ez tölti fel a táblázatot Company objektumokkal
  • customerToTable() -> ez tölti fel a táblázatot Customer objektumokkal
  • orderToTable() -> ez tölti fel a táblázatot Order objektumokkal

Nézzünk meg az egyik ilyen metódust is. Legyen a vatToTable():

private void vatToTable() {
   this.vats = (List<Vat>) list;
   dtm.addColumn("ID");
   dtm.addColumn("név");
   dtm.addColumn("érték");
   dtm.addColumn("belföldi?");
   for (Vat vat : vats) {
      Object[] obj = new Object[4];
      obj[0] = vat.getVatId();
      obj[1] = vat.getAbbr();
      obj[2] = vat.getValue();
      obj[3] = vat.isDomestic();  
      dtm.addRow(obj);  
   }
}

Ugye privát metódust, mert a ModifyDeleteDialog-on kívül másnak nincs hozzá köze, nem érheti el. A korábban a list változóba átmentett generikus listát fogom, és List<Vat> típusra kasztolom:

this.vats = (List<Vat>) list;

Ezáltal már nem T típusú objektumokat tartalmaz, hanem Vat típusokat. A dtm ez esetben a DefaultTableModel, és a táblázatot jelenti, ehhez hozzáadok négy oszlopot az addColumn metódussal, az ID-t, a nevet, az értéket, és hogy belföldi-e.

Ezután a Vat listámat simán végig iterálom, és minden egyes, a listában szereplő Vat objektumtól lekérem az ID-jét (getVatId()), a rövidítését (getAbbr()), az értékét (getValue()) és hogy belföldi-e (isDomestic()). Ezeket utána hozzáadom egy új sorként a táblázathoz.

Az iterálás során addig töltöm fel új sorral a táblázatot, míg van Vat objektum, és kész.

Ami még érdekes lehet, az a megrendelések kezelése. Az orders adattáblában az ügyfél id-jét, a vállalkozásunk id-jét, és a dolgozó id-jét tároljuk, viszont a táblázatban nyilván ezek nem segítenek rajtunk, hanem az ügyfél nevét, a vállalkozásunk nevét és a dolgozó nevét szeretnénk látni.

Ezért a ModifyDeleteDialog-ban az initControllers() metódus hoz létre ezekből egy-egy példányt, melyek intézkednek nekünk az id-k névvé alakításáról. Korábban már írtam arról, hogy az MVC modell alapján szétválasztottam az adatok kezelését és a felhasználói felületért felelős részeket.

private void initControllers() {
   dtm = new DefaultTableModel();
   customerController = new CustomerController();
   companyController = new CompanyController();
   employeeController = new EmployeeController();
}

A CustomerController felelős minden ügyféllel kapcsolatos dologért, ő lát mindenkit, és őt látja mindenki. Őtőle kérjük el a Customer objektumokat, és hogy ő ezt hogyan oldja meg, azt nem kell tudnunk.

Ugyanígy a CompanyControllertől kérjük el a Company, az EmployeeControllertől pedig az Employee objektumokat.

Nézzük akkor az orderToTable() metódust, hogyan írja ki a megrendelések sorait a táblázatba, és helyettesíti be az id-k helyére a megfelelő neveket:

private void orderToTable() {
   this.orders = (List<Order>) list;
   dtm.addColumn("ID");
   dtm.addColumn("megrendelő");
   dtm.addColumn("cég");
   dtm.addColumn("tárgy");
   dtm.addColumn("mennyiség");
   dtm.addColumn("érték");
   dtm.addColumn("író");
   dtm.addColumn("megrendelve");
   dtm.addColumn("állapot");
   try {
      for (Order order : orders) {
         Object[] obj = new Object[9];
         obj[0] = order.getOrderId();
         obj[1] = customerController.getOneCustomer(order.getCustomer());
         obj[2] = companyController.getOneCompany(order.getCompany());
         obj[3] = order.getOrderName();
         obj[4] = String.valueOf(order.getQuantity()) + " " + order.getUnit();
         obj[5] = order.getQuantity() * order.getUnitPrice();
         obj[6] = employeeController.getOneEmployee(order.getEmployee());
         obj[7] = order.getOrderDate();
         obj[8] = getStatus(order.getStatus());
         dtm.addRow(obj);
      }
   } catch (SQLException ex) {
      System.out.println("Hiba: " + ex.getMessage());
      JOptionPane.showMessageDialog(new javax.swing.JDialog(), "Adatbázishiba: " + ex.getMessage(), "Adatbázishiba", JOptionPane.ERROR_MESSAGE);
   }
}

A customerController objektum getOneCustomer metódusa visszaad egy Stringet, amit a paraméterként megkapot id alapján kér le a customer adattáblából. A companyControllernek szintén van egy getOneCompany metódusa, és az employeeControllernek szintén van egy getOneEmployee metódusa, gondolom a lényeg érthető, próbáltam azonos elnevezési mintákat használni.

A kiválasztott művelet

Mindegyik ModifyDeleteDialog egy-egy Szerkesztés, Törlés és Mégse gombbal rendelkezik. A Mégse gombra fűzött metódus:

private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {                                             
   selectedEvent = "cancel";
   this.dispose();
}

A Módosítás gomb metódusa:

private void editButtonActionPerformed(java.awt.event.ActionEvent evt) {                                           
   selectedEvent = "modify";
   this.dispose();
}

És végül a Törlés gomb metódusa:

private void deleteButtonActionPerformed(java.awt.event.ActionEvent evt) {                                             
   selectedEvent = "delete";
   this.dispose();
}

A választott műveletet beleírjuk egy selectedEvent privát változóba, és bezárjuk a ModifyDeleteDialog ablakot. Még ebben az osztályban érdemes megnéznünk két metódust, melyek fontos információt adnak a dialógusablak bezárása után is. A getSelectedEvent() azt adja vissza, hogy melyik gombot nyomtuk meg, azaz mit írtunk bele a selectedEvent változóba:

public String getSelectedEvent() {
   return selectedEvent;
}

A getSelectedId() pedig azt, hogy melyik id-jű elemet választottuk ki a táblázatban:

public String getSelectedId() {
   return dtm.getValueAt(elementsTable.getSelectedRow(), 0).toString();
}

Visszatérés a Controllerhez

A ModifyDeleteDialog ablakot mindig az egyik Controller osztály hívja meg, és ő is adja át neki a megfelelő osztályt. Nézzük a CompanyControllert példaként:

public void modifyDeleteCustomer() throws SQLException {
   List<Customer> customerList = customerRepo.findAll();
   final ModifyDeleteDialog modifyDelete = new ModifyDeleteDialog(new javax.swing.JFrame(), true, customerList);
   modifyDelete.setVisible(true);
   modifyDelete.addWindowListener(new WindowAdapter() {
      @Override
         public void windowClosed(WindowEvent e) {
            if (modifyDelete.getSelectedEvent() != null) {
               if (modifyDelete.getSelectedEvent().equals("delete")) {
                  try {
                     deleteCustomer(Integer.valueOf(modifyDelete.getSelectedId()));
                     modifyDeleteCustomer();
                  } catch (SQLException ex) {
                     System.out.println("Hiba: " + ex.getMessage());
                     JOptionPane.showMessageDialog(new javax.swing.JDialog(), "Adatbázishiba: " + ex.getMessage(), "Adatbázishiba", JOptionPane.ERROR_MESSAGE);
                  }
               } else if (modifyDelete.getSelectedEvent().equals("modify")) {
                  try {
                     modifyCustomer(Integer.valueOf(modifyDelete.getSelectedId()));
                     modifyDeleteCustomer();
                  } catch (SQLException ex) {
                     System.out.println("Hiba: " + ex.getMessage());
                     JOptionPane.showMessageDialog(new javax.swing.JDialog(), "Adatbázishiba: " + ex.getMessage(), "Adatbázishiba", JOptionPane.ERROR_MESSAGE);
                 }
            }
         }
      }
   });
}

Ez történik:

  1. Meghívjuk a ModifyDeleteDialog ablakot, átadjuk neki a customerList-et.
  2. A ModifyDeleteDialog ettől tudja, hogy az ügyfeleket kell listáznia a táblázatban.
  3. Választok egy ügyfélt, és megnyomok egy gombot.
  4. A ModifyDeleteDialog bezáródik, visszatér a vezérlés a CustomerControllerhez.
  5. A CustomerController lekérdezi a megnyomott gomb értékét a getSelectedEvent() segítségével.
  6. Ha a Törlés gomb lett megnyomva, akkor a saját törlési metódusával törli az adott id-jű ügyfelet az adatbázisból.
  7. Ha a Módosítás gomb lett megnyomva, akkor a saját módosítási metódusát hívja meg, ami meg megnyitja az ügyfél adatait a már ügyfélbevitelkor is használt ablakban.
  8. Mindkét választás után újra feldobjuk a ModifyDeleteDialog-ot, és betöltjük a már aktualizált ügyféllistát, amiből a törlés esetén hiányzik a törölt ügyfél, módosítás esetén pedig módosult adatokkal szerepel az ügyfél. Azért dobjuk fel megint a dialógusablakot, mert ha több ügyfelet szeretnénk törölni vagy módosítani, akkor ne kelljen már mindig a menüből kiválasztani az Ügyfél módosítása/törlése menüpontot. Ha már nem akarunk több ügyfelet törölni vagy módosítani, akkor a ModifyDeleteDialog ablaknál a Mégse gombot nyomjuk meg, ezt érzékeli a Controller és a vezérlés visszatér a szoftver főablakába.

Záró gondolatok

A programozás tanfolyam során nagy nehézségem voltak a Java generikusok megértésével, mert eléggé elvontnak tűnt. Aztán a mentorom azt ajánlotta, hogy tanulmányozzam a Java ArrayList-jét, ott jól megismerhető, ők hogyan használták a generikusokat:

A StudiCore kurzusán komolyabb feladat volt a generikusokkal, mint ahogy én ezt a ModifyDeleteDialognál felhasználtam, de azt már nem mutatom be, aki eljut odáig a programozás tanulás során, megoldja majd.