Ich plane eine Java Desktop Anwendung basierend auf Swing zu erstellen. Dort will ich dann auch das MVC Model richtig anwenden. Ich hab dazu einiges schon gelesen und werde das MVC nach dem Beispiel von Java SE Application Design With MVC erstellen.
Die Informationen die im View angezeigt werden befinden sich dort stehts im Model. Das Model symbolisiert also ein Objekt, das die Daten "temporär" zwischenspeichert. Ein Model kann danach so aussehen (Darstellung stark verkürzt):
Dadurch ergibt sich dann mir folgendes Problem: Wie lassen sich diese Model-Objekte sinnvoll abspeichern (die Daten in den Objekten)?
Muss für sowas JPA oder sowas wie Hibernate verwendet werden, um die Daten sinnvoll zu speichern und selbstverständlich auch zu laden und verändern?
Die Model kannst du natürlich mit JPA abspeichern über Annotationen oder über ein orm.xml.
Musst halt die entsprechenden Regeln und Conventions einhalten.
Die Model kannst du natürlich mit JPA abspeichern über Annotationen oder über ein orm.xml.
Musst halt die entsprechenden Regeln und Conventions einhalten.
Ja, den Link hatte ich bereits bei der Forensuche gefunden .
Für Models heißt das dann:
Einfügen von Annotations oder über orm.xml. Das speichern eines Models (also der Daten) erfolgt dann z.B. durch Aufruf eines EntityManagers, der dieses Model "verwaltet".
Ja, den Link hatte ich bereits bei der Forensuche gefunden .
Für Models heißt das dann:
Einfügen von Annotations oder über orm.xml. Das speichern eines Models (also der Daten) erfolgt dann z.B. durch Aufruf eines EntityManagers, der dieses Model "verwaltet".
Danke werde ich mir dann anschauen.
Bin erst beim Designen des Views.
Eine Frage zu den Models:
Entities stelle Models dar. In der View müssen, z.B. in einer JTable mehrere Zeilen dargestellt werden. Jede Zeile = ein Datensatz. Daraus ergibt sich das jeder Datensatz ein Objekt der Klasse "DatensatzModell" (ein Entity) ist.
Wie macht man das jetzt richtig?
Erstellt man eine HauptKlasse die alle Entities-Objekte enthält und die Entities werden in einer List (oder ähnlichem) "gespeichert". Die HauptKlasse verwaltet dann quasi die Objekte in Form eines Zwischenspeichers. Objekte können über getter/setter-Methoden geladen werden z.B. mit lazy-loading.
classModelVerwalter{privateSet kunden;publicSetgetKunden(){//Lade Kunden nach API des jeweiligen ORMs}
Wenn ich das so mache, dann komm ich in das Problem das das View nicht weiß welches Objekt es jetzt ansprechen muss (bzw. vom Controller angesprochen werden muss) um Daten zu ändern.
Gibt es 3 Kunden-Objekte, jeder füllt z.B. eine Zeile in einer JTable.
Es erfolgt ein Aufruf eines Listeners, der auf den Controller (den Mediator) mappt. Der Controller kann herausfinden welche Zeile selected ist.
Wie erkennt er nun welches Objekt selected ist, er kennt ja nur die Zeile!?
Ich denke wahrscheinlich nur zu kompliziert, aber es stellt für mich ein richtiges Problem dar, Model-Objekte richtig zu verwalten.
ich muss wahrscheinlich eigene TableModels, Lists etc. für diese Komponenten erzeugen, richtig?
Dort kann ich die einmal geladenen Objekte "speichern".
Ja ganz normaler Aufbau, einem MVC entsprechend.
Über den EntityManager können Entities-Objekte aus der Datenbank erstellt werden.
Die füge ich z.B. in eine JTable ein => das ist nur möglich mit einer Klasse die AbstractTableModel erweitert (oder mit Beans Binding). Ich hatte da lediglich einige Denkfehler.
Nur durch ein eigenes TableModel kann ein Objekt richtig in der Tabelle angezeigt werden (zuordnung der Objekt-Felder zu den Rows / Cols der Tabelle). Nur durch ein TableModel kann ich, bei Aufruf der Methode getSelectedRow() genau das ausgewählte Objekt wieder bekommen.
Ich hatte davor einen Aufbau das die Objekte außerhalb (ohne eignes TableModel) mit setValueAt() in der Tabelle dargestellt wurden. Das führt dann später zu Problemen, dass ich nicht wissen kann, welches Objekt genau ausgewählt ist in der Tabelle.
Ja ganz normaler Aufbau, einem MVC entsprechend.
Über den EntityManager können Entities-Objekte aus der Datenbank erstellt werden.
Die füge ich z.B. in eine JTable ein => das ist nur möglich mit einer Klasse die AbstractTableModel erweitert (oder mit Beans Binding). Ich hatte da lediglich einige Denkfehler.
AbstractTableModel erweitert (oder mit Beans Binding). Ich hatte da lediglich einige Denkfehler.
Nur durch ein eigenes TableModel kann ein Objekt richtig in der Tabelle angezeigt werden (zuordnung der Objekt-Felder zu den Rows / Cols der Tabelle). Nur durch ein TableModel kann ich, bei Aufruf der Methode getSelectedRow() genau das ausgewählte Objekt wieder bekommen.
Ich hatte davor einen Aufbau das die Objekte außerhalb (ohne eignes TableModel) mit setValueAt() in der Tabelle dargestellt wurden. Das führt dann später zu Problemen, dass ich nicht wissen kann, welches Objekt genau ausgewählt ist in der Tabelle.
Ja kommt drauf wenn du Swing verwendest kann das schon passen.
Deine Probleme sind einfach dass du kein Plan von einer JTable hast oder? Solltest erst mal das Tutorial zur JTable durchmachen
Die Notifications und die Struktur sind vorhanden.
Wo ich die Entites lade: Bisher noch garnicht - an dem Punkt der Anwendung bin ich noch nicht angelangt. Ich habe zuerst sämtliche Views gerade erstellt.
Wo habe ich sie vor zu laden: Es gibt viele Unterschiedliche Varianten des MVC Patterns. Wenn bei mir ein Modell ein Entity darstellt, so kann dieses entweder nur von einem Modell geladen werden, dass kein Entity ist - also die Logik zum laden der Entites ist in einem weiteren Modell, oder sie werden im Controller geladen.
Ja kommt drauf wenn du Swing verwendest kann das schon passen.
Deine Probleme sind einfach dass du kein Plan von einer JTable hast oder? Solltest erst mal das Tutorial zur JTable durchmachen
Das Problem ist nicht die JTable an sich, sondern dass ich das erste mal mit Entities arbeite und diese auch irgendwie sinnvoll benutzen möchte.
In einer vorherigen Anwendung hatte ich alles durch direkte SQL Queries gelöst. Das bringt je komplexer die Datenstruktur wird jedoch viele Probleme, deswegen JPA.
Die Notifications und die Struktur sind vorhanden.
Wo ich die Entites lade: Bisher noch garnicht - an dem Punkt der Anwendung bin ich noch nicht angelangt. Ich habe zuerst sämtliche Views gerade erstellt.
Wo habe ich sie vor zu laden: Es gibt viele Unterschiedliche Varianten des MVC Patterns. Wenn bei mir ein Modell ein Entity darstellt, so kann dieses entweder nur von einem Modell geladen werden, dass kein Entity ist - also die Logik zum laden der Entites ist in einem weiteren Modell, oder sie werden im Controller geladen.
Wie gesagt dass hat NICHTS mehr mit MVC zu tun!!!!
Deine Model sollten wiederverwendbar sein, denkst du da ist es schlaub JPA einzupflanzen?
Normalerweise solltes du einen Service machen der auf den EM zugreift.
Das Problem ist nicht die JTable an sich, sondern dass ich das erste mal mit Entities arbeite und diese auch irgendwie sinnvoll benutzen möchte.
In einer vorherigen Anwendung hatte ich alles durch direkte SQL Queries gelöst. Das bringt je komplexer die Datenstruktur wird jedoch viele Probleme, deswegen JPA.
Du programmierst genau gleich es gibt keinen Unterschied. Ich versteh es nicht sorry mach mal ein KSKB was genau dein Problem ist.
Du musst auch aufpassen wenn du JPA verwendest, wenn du attach Objekte in der GUI veränderst, beim nächsten commit kommt alles in die DB. Also solltest du dich mit JPA beschäftigen und deine Architektur fertig ausdenken.
Wie gesagt dass hat NICHTS mehr mit MVC zu tun!!!!
Deine Model sollten wiederverwendbar sein, denkst du da ist es schlaub JPA einzupflanzen?
Normalerweise solltes du einen Service machen der auf den EM zugreift.
Du programmierst genau gleich es gibt keinen Unterschied. Ich versteh es nicht sorry mach mal ein KSKB was genau dein Problem ist.
Du musst auch aufpassen wenn du JPA verwendest, wenn du attach Objekte in der GUI veränderst, beim nächsten commit kommt alles in die DB. Also solltest du dich mit JPA beschäftigen und deine Architektur fertig ausdenken.
So, ich brauchte jetzt ein wenig Zeit zum überdenken des Ganzen.
Im Grunde stimmt doch dann folgender Aufbau:
View-Controller->Services->DAO->DAO-Model
Der Service ist zuständig für die Verwaltung des EM und führt gewisse Sachen damit aus. Über ein DAO wird Unabhängigkeit und Wiederverwendbarkeit geschaffen. Das View muss nicht direkt über das Model bescheidwissen, dass muss nur der Controller. Dabei wird ein Interface implementiert, dass Properties und Methoden für ein Model definiert. Somit ist es dem Controller/View egal ob es ein HibernateKundenModel oder ein EclipselinkKundenModel bekommt, solange beide das Interface KundenModel implementieren.
Stimmt das jetzt so?
Wenn ja dann geb ich die auch noch nen KSKB nachher.
So, ich brauchte jetzt ein wenig Zeit zum überdenken des Ganzen.
Im Grunde stimmt doch dann folgender Aufbau:
View-Controller->Services->DAO->DAO-Model
Der Service ist zuständig für die Verwaltung des EM und führt gewisse Sachen damit aus. Über ein DAO wird Unabhängigkeit und Wiederverwendbarkeit geschaffen. Das View muss nicht direkt über das Model bescheidwissen, dass muss nur der Controller. Dabei wird ein Interface implementiert, dass Properties und Methoden für ein Model definiert. Somit ist es dem Controller/View egal ob es ein HibernateKundenModel oder ein EclipselinkKundenModel bekommt, solange beide das Interface KundenModel implementieren.
Stimmt das jetzt so?
Wenn ja dann geb ich die auch noch nen KSKB nachher.
Jop passt , ich hab hier sogar ein KSKB hier im Forum welches genau diesen Aufbau darstellt.
Wie gesagt bei Controller musst du aufpassen, es gibt imho GUI-Controller (UI abhängig z.B. in Swing die Listener) und Controller welche UI unabhängig sind und deine Services aufruft.
Im eigentlichen MVC sind die Controller eigentlich die UI abhängigen also in Swing jeder Listener ist ein Controller...
Jop passt , ich hab hier sogar ein KSKB hier im Forum welches genau diesen Aufbau darstellt.
Wie gesagt bei Controller musst du aufpassen, es gibt imho GUI-Controller (UI abhängig z.B. in Swing die Listener) und Controller welche UI unabhängig sind und deine Services aufruft.
Im eigentlichen MVC sind die Controller eigentlich die UI abhängigen also in Swing jeder Listener ist ein Controller...
packagecoreplugin.dao;importcoreplugin.db.QueueEntry;importjava.util.List;/**
* Created by IntelliJ IDEA.
* User: Dennis
* Date: 03.01.12
* Time: 18:50
* To change this template use File | Settings | File Templates.
*/interfaceQueueDao{ObjectcreateQueueEntry(QueueEntry queueEntry);ObjectdeleteQueueEntry(QueueEntry queueEntry);ObjectupdateQueueEntry(QueueEntry queueEntry);ListgetAllQueueEntry();ObjectfindQueueEntry(int identifier);ObjectgetFirstQueueEntry();}
EclipselinkQueueDaoImpl
Java:
packagecoreplugin.dao;importcoreplugin.db.QueueEntity;importcoreplugin.db.QueueEntry;importcoreplugin.util.EclipseUtil;importjavax.persistence.EntityManager;importjavax.persistence.Query;importjava.util.List;/**
* Created by IntelliJ IDEA.
* User: Dennis
* Date: 03.01.12
* Time: 18:53
* To change this template use File | Settings | File Templates.
*/@SuppressWarnings("HardCodedStringLiteral")publicclassEclipselinkQueueDaoImplimplementsQueueDao{privatefinalEntityManager entityManager =EclipseUtil.getEntityManager();@OverridepublicQueueEntrycreateQueueEntry(QueueEntry queueEntry){
entityManager.persist(queueEntry);return queueEntry;}@OverridepublicQueueEntrydeleteQueueEntry(QueueEntry queueEntry){
entityManager.remove(queueEntry);return queueEntry;}@OverridepublicQueueEntryupdateQueueEntry(QueueEntry queueEntry){
entityManager.merge(queueEntry);return queueEntry;}@OverridepublicListgetAllQueueEntry(){return entityManager.createQuery("Select q from QueueEntity q",QueueEntity.class).getResultList();}@OverridepublicQueueEntryfindQueueEntry(int identifier){Query query = entityManager.createQuery("select q from QueueEntity q where q.identity in :identifier");
query.setParameter("identifier", identifier);return(QueueEntry) query.getSingleResult();}@OverridepublicQueueEntrygetFirstQueueEntry(){returnnull;//To change body of implemented methods use File | Settings | File Templates.}}
QueueModel
Java:
packagecoreplugin.db;importjava.beans.PropertyChangeListener;importjava.beans.PropertyChangeSupport;importjava.util.ArrayList;importjava.util.List;/**
* Created by IntelliJ IDEA.
* User: Dennis
* Date: 03.01.12
* Time: 18:08
* To change this template use File | Settings | File Templates.
*/publicclassQueueModel{privatefinalList<QueueEntry> queueEntryList =newArrayList<QueueEntry>();protectedfinaltransientPropertyChangeSupport listeners =newPropertyChangeSupport(this);/**
* Adds a property-change listener.
*
* @param l the listener
*/publicvoidaddPropertyChangeListener(PropertyChangeListener l){if(l ==null){thrownewIllegalArgumentException();}this.listeners.addPropertyChangeListener(l);}publicvoidremovePropertyChangeListener(PropertyChangeListener l){this.listeners.removePropertyChangeListener(l);}/**
* Notificates all listeners to a model-change
*
* @param prop the property-id
* @param old the old-value
* @param newValue the new value
*/protectedvoidfirePropertyChange(String prop,Object old,Object newValue){if(this.listeners.hasListeners(prop)){this.listeners.firePropertyChange(prop, old, newValue);}}publicvoidaddQueueEntry(QueueEntry queueEntry){
queueEntryList.add(queueEntry);firePropertyChange("queueEntryAdded",null, queueEntry);}publicList<QueueEntry>getQueueList(){returnnewArrayList<QueueEntry>(queueEntryList);}}
So kurz zusammenfassend noch dazu:
Es muss noch die Service-Schicht ausgebaut werden. Es kommt eine Klasse QueueServiceImpl mit Interface QueueService hinzu. Dieser "Service" führt Operationen über das DAO aus, wodurch mit der Datenbank kommuniziert wird.
Einige PropertyChangeEvents wie queueEntryAdded müssen noch verarbeitet werden.
Nach diesem Aufbau passiert doch eigentlich folgendes:
User macht eine Eingabe über das View. Dieses mappt auf den Controller. Der Controller führt Aktionen aus und kann (muss aber nicht) einen Datenbankzugriff einfordern. Dies geschieht über 2 Sachen:
1. Dem Service-Layer: Hier findet der direkte Zugriff über ein DAO auf die Datenbank statt. Hier werden keine PropertyChangeEvents gefeuert
2. Einem Model: Ihm wird das selbe Objekt übergeben. Das Model feuert anschließend ein PropertyChangeEvent, sodass sich das View updaten kann.
Ich seh grad, ich hab da vermutlich noch ein Fehler in der Umsetzung. Wenn ich es richtig sehe, dann kann ich die Klasse QueueTableModel und QueueModel mergen. Der Fehler kam wohl durch die Übernahme "deiner" Struktur.
Jetzt halt nur nochmal die Frage, passt das so, oder sollte ich noch was ändern?
So, hab jetzt mein QueueTableModel und mein QueueModel gemerged. Sollte das selbe darstellen.
Jetzt noch eine Frage:
Der Aufbau ist ja: View, Controller -> Service -> DAO
Warum benutzt man einen Service-Layer, in deinem KSKB macht der Service das selbe wie das DAO. Es gibt somit viel Coderedundanz. Gibt es also Gründe einen Service-Layer noch dazwischen zu setzen?
Edit: Achso, das man evtl sagt: Das DAO beinhaltet nur einfache Vorgänge wie: updaten, hinzufügen, entfernen, finden und alle zurückgeben.
Der Service hingegen kann mehrere DAO Funktionalitäten bündeln und viele weitere Aktionen durchführen!?
Ich geb mal wieder mein Senf dazu
1. Dein Interface von oben ist total unschön ein Interface sollte keine Variablen beinhalten, sondern nur Methoden vorgegeben nehm die raus und schreib diese in deine xxxxImpl rein.
2. Ich würde dir raten die JPA Annotationen dann an die Member Variable zu machen, da dass übersichtlicher ist.
3. Für CRUD Funktonalitäten brauchst du nicht unbedingt ein DAO, da der EntityManager typsicher ist und quasi dein DAO darstellt.
4. Du brauchst eine Service Schicht für das Transaktionshandling. In deinem Service Layer liegt deine Business Logik mit Fehlerbehandlung usw. Da musst du dir Gedanken machen was passiert bei Fehler xyz. Wie geht es weiter mit der Transaktion und vor allem muss dur dir Gedanken über die JPA Sachen machen bezüglich attach und dettach Objekten!!!
5. Würde ich keinen PropertyChange Support mehr verwenden. Der hat ein paar unschöne Nebeneffekte. z.B. kannst du einen GLEICHEN(!!!) Listener mehrmals deinem Model adden, aber beim remove wird er nur 1 mal entfernt.
Schau dir mal den EventBus an, finde persönlich sehr gelungen und einfach zu verstehen.
Ich geb mal wieder mein Senf dazu
1. Dein Interface von oben ist total unschön ein Interface sollte keine Variablen beinhalten, sondern nur Methoden vorgegeben nehm die raus und schreib diese in deine xxxxImpl rein.
2. Ich würde dir raten die JPA Annotationen dann an die Member Variable zu machen, da dass übersichtlicher ist.
4. Du brauchst eine Service Schicht für das Transaktionshandling. In deinem Service Layer liegt deine Business Logik mit Fehlerbehandlung usw. Da musst du dir Gedanken machen was passiert bei Fehler xyz. Wie geht es weiter mit der Transaktion und vor allem muss dur dir Gedanken über die JPA Sachen machen bezüglich attach und dettach Objekten!!!
5. Würde ich keinen PropertyChange Support mehr verwenden. Der hat ein paar unschöne Nebeneffekte. z.B. kannst du einen GLEICHEN(!!!) Listener mehrmals deinem Model adden, aber beim remove wird er nur 1 mal entfernt.
Schau dir mal den EventBus an, finde persönlich sehr gelungen und einfach zu verstehen.
Auf EventBus bin ich bereits gestoßen als ich nach Best Practices gesucht hatte. PropertyC. oder Oberserver. "Den Eventbus" entnehm ich doch aus dem Repositorie von Eventbus: Subversion — Java.net. Da brauch ich die ganz normale Eventbus.jar R 60?
So ungefähr hab ich mir das Gedacht vorhin. War mir aber nicht sicher.
Auf EventBus bin ich bereits gestoßen als ich nach Best Practices gesucht hatte. PropertyC. oder Oberserver. "Den Eventbus" entnehm ich doch aus dem Repositorie von Eventbus: Subversion — Java.net. Da brauch ich die ganz normale Eventbus.jar R 60?
Standalone client Anwendung? Falls ja
Wie gesagt ich denke du brauchst dann nicht extra eine DAO-Schicht, diese stellt dein EntityManager dar.
Ich würde sogar sagen dass du deinen AbstractController genauso wenig brauchst, wenn du einen Service Layer einbaust. Der AbstractController manchmal auch ServiceLocator genannt war dafür gesagt remote Services aufzurfen, aber wenn du keine hast, könntest die auch rein theoretisch weglassen.
Ich hab den EventBus noch nie selber eingebaut, der war in meinem Projekten schon immer im repo vorhanden.
Aber sonst sieht dein Aufbau soweit okay aus.
Ich würde dir noch Google Guice als DI Framework ans Herz legen, da es für deinen Fall leicht gewichtiger als Spring ist. Ist eigentlich ziemlich einfach zu verstehen und dann ist deine Anwendung wirklich sauber aufgebaut.
Standalone client Anwendung? Falls ja
Wie gesagt ich denke du brauchst dann nicht extra eine DAO-Schicht, diese stellt dein EntityManager dar.
Ich würde sogar sagen dass du deinen AbstractController genauso wenig brauchst, wenn du einen Service Layer einbaust. Der AbstractController manchmal auch ServiceLocator genannt war dafür gesagt remote Services aufzurfen, aber wenn du keine hast, könntest die auch rein theoretisch weglassen.
Ich hab den EventBus noch nie selber eingebaut, der war in meinem Projekten schon immer im repo vorhanden.
Aber sonst sieht dein Aufbau soweit okay aus.
Ich würde dir noch Google Guice als DI Framework ans Herz legen, da es für deinen Fall leicht gewichtiger als Spring ist. Ist eigentlich ziemlich einfach zu verstehen und dann ist deine Anwendung wirklich sauber aufgebaut.
Jo standalone client Anwendung.
Der AbstractController war nur von vorher noch drinnen, aber komplett leer.
Ich werde mir das DI Framework dann noch anschauen. Sieht erstmal mächtig kompliziert aus.
Was ich soweit verstanden habe: DI macht die Anwendungsteile unabhängiger. Unit Tests werden dadurch einfacher.
Aus:
Jo standalone client Anwendung.
Der AbstractController war nur von vorher noch drinnen, aber komplett leer.
Ich werde mir das DI Framework dann noch anschauen. Sieht erstmal mächtig kompliziert aus.
Was ich soweit verstanden habe: DI macht die Anwendungsteile unabhängiger. Unit Tests werden dadurch einfacher.
Aus:
Wie das dann alles mit den Annotations funktioniert etc. muss ich noch genau durchlesen.
Genau es wird alles loser gekoppelt und bei JUnit Test kannst du einfacher deine Sachen mocken. So kannst du MockService Schreiben die nicht auf die DB zugreifen usw.
Außerdem musst du dich nicht mehr um Singeltons kümmern, weil du keine mehr hast.
Danke, schau ich mir an sobald ich durch die Guice Wiki durch bin.
Ich hab nun auch EventBus statt PropertyChange drinnen.
Eine Frage hätte ich da aber noch:
Controller Methode, wird vom View aufgerufen:
In irgendeiner anderen Klasse befindet sich dann der Subscriber. Im Subscriber würde dann z.B. ein SwingWorker aufgerufen. Darf man das so, oder sollte man den Service im Controller direkt aufrufen?
In irgendeiner anderen Klasse befindet sich dann der Subscriber. Im Subscriber würde dann z.B. ein SwingWorker aufgerufen. Darf man das so, oder sollte man den Service im Controller direkt aufrufen?
Vesteh ich nicht... Wenn du etwas direkt aufrufen kannst, warum solltest du es nicht direkt aufrufen?
Der EventBus oder ein Notification ist dafür da auf etwas zu reagieren, wenn sich etwas ändert oder ausgelöst wird.