# 2 ComboBox(en)



## maGG (2. Apr 2019)

Ich habe 2 ComboBox(en) und in beide wird mit einer fill-Methode Labels, mit Strings, aus einer SQL Datenbank geladen. Genauer gesagt werden mit "SELECT *" alle Spalten ausgewählt, aber nur die LABEL(s) aus entsprechender Spalte als String Werte in die ComboBox(en) geladen.

Jetzt möchte ich folgendes errreichen: Wenn man in der ersten ComboBox ein Wert auswählt, der nicht dem ersten entspricht ("Bitte auswählen"), dann soll die andere ComboBox auf den ersten Wert ("Bitte auswählen") springen. Das Ganze hat den Hintergrund, dass man nicht in beiden ComboBox(en) einen Wert auswählen soll, da beide Boxen die selben Variablen mit Werten belegen. Im nicht Sehbaren ist das egal, da die Werte vom letzten Action Performed einfach überschrieben werden. Ich möchte jetzt das Ganze visuell unterstützen, so dass der Nutzer ganz klar sehen kann was die zu letzt gesetzte ComboBox war.

Also schematisch: Wähle ich in ComboBox 1 etwas aus, was nicht dem Index 0 entspricht, dann soll bei ComboBox 2 der Index auf 0 gesetzt werden und andersrum. Das führt dazu das man nur einen Wert in beiden ComboBox(en) gleichzeitig auswählen kann - und genau so will ich es.

Ohne SQL und Datenbank Anbindung geht das ganz leicht, mit kommen bei mir jedoch Fehler...

.setSelectedIndex(0); führt dazu, dass die Datenbank gelockt wird.
.setModel("Bitte auswählen"); führt zu dem Fehler: String cannot be converted to ComboBoxModel<String>

Wie kann man das möglichst elegant lösen? Bzw. habt ihr mich verstanden?


----------



## mihe7 (2. Apr 2019)

Wenn beim Auswahl eines Wertes aus einer ComboBox die DB gesperrt wird, ist etwas grundlegend falsch. Recht viel mehr kann man ohne Details nicht dazu sagen.


----------



## maGG (2. Apr 2019)

Danke für deine Antwort. Das ist leider nicht ganz korrekt. Wenn man einen Wert in der ComboBox auswählt entsteht kein Fehler. Nur bei den oben genannten zwei Beispielen.


----------



## mihe7 (2. Apr 2019)

Gut, mal anders: warum und was passiert denn auf der DB, wenn Du in der ComboBox etwas auswählst?


----------



## maGG (2. Apr 2019)

In der Datenbank sollte eigentlich nix verändert werden, lediglich Werte aus der Datenbank in GUI geladen werden. Wenn man einen Wert in der ComboBox setzt, dann werden Attribute gesetzt (benutze sqlitejdbc sowie rs2xml):

```
private void jComboBoxFilialenDeutschlandActionPerformed(java.awt.event.ActionEvent evt) {                                                             
        try{
            int row = jComboBoxFilialenDeutschland.getSelectedIndex();
            String sql = "SELECT * FROM Filialen_Deutschland WHERE id='" + row + "' ";
            pst = conn.prepareStatement(sql);
            rs = pst.executeQuery();
                strasze = rs.getString("STRASZE");
                plzort = rs.getString("PLZ_ORT");
                ort = rs.getString("ORT");
                telLaendercode = "" + rs.getInt("TEL_LAND");
                telStartInt = "" + rs.getInt("TEL_ANFANG_INT");
                telStartGer = rs.getString("TEL_ANFANG_TXT");
                telEndDefault = "" + rs.getInt("TEL_ENDE_DEFAULT");
                faxStart = rs.getString("FAX_ANFANG_TXT");
                faxEndDefault = rs.getString("FAX_ENDE_TXT");
                Preview();
                Testpreview();
                //jComboBoxFilialenAusland.setSelectedIndex(0);
                //jComboBoxFilialenAusland.setModel("Bitte auswählen");
        }catch(Exception e){
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, e);
        }finally{
            try{
                rs.close();
                pst.close();
            }catch(SQLException e){
                e.printStackTrace();
                JOptionPane.showMessageDialog(null, e);
            }
        }
    }
```


----------



## mihe7 (3. Apr 2019)

Hier findet keine saubere Trennung zwischen DB und UI statt. Du solltest die DB separat behandeln (s. z. B. Repository-Pattern). Aktuell verschachtelst Du ggf. mehrere SQL-Abfragen, d. h. Du hast mehrere Cursor gleichzeitig offen. Das kann zu Problemen führen.

Als Quick & Dirty-Lösungsansatz kannst Du den DB-Part in eine separate Methode auslagern. Den folgende Code bitte als Pseudocode verstehen (ich habe das nur hier im Forum editiert, kann sein, dass er nicht übersetzt wird):

```
private void jComboBoxFilialenDeutschlandActionPerformed(java.awt.event.ActionEvent evt) {                                                            
        int row = jComboBoxFilialenDeutschland.getSelectedIndex();
        try {
            loadGermanBranch(row);
            Preview();
            Testpreview();
            jComboBoxFilialenAusland.setSelectedIndex(0);
        }catch(SQLException e){
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, e);
            return false;
        }
    }
   
    private void loadGermanBranch(int row) throws SQLException {
        String sql = "SELECT * FROM Filialen_Deutschland WHERE id=?";
        try(pst = conn.prepareStatement(sql)) {
            pst.setInt(1, row);
            try(rs = pst.executeQuery()) {
                strasze = rs.getString("STRASZE");
                plzort = rs.getString("PLZ_ORT");
                ort = rs.getString("ORT");
                telLaendercode = "" + rs.getInt("TEL_LAND");
                telStartInt = "" + rs.getInt("TEL_ANFANG_INT");
                telStartGer = rs.getString("TEL_ANFANG_TXT");
                telEndDefault = "" + rs.getInt("TEL_ENDE_DEFAULT");
                faxStart = rs.getString("FAX_ANFANG_TXT");
                faxEndDefault = rs.getString("FAX_ENDE_TXT");
            }
        }
    }
```
Analog für die andere ComboBox. Noch ein paar grundsätzliche Anmerkungen:
1. Baue Parameter in SQL-Strings nie manuell ein. Verwende die setXXX-Methoden von PreparedStatement.
2. Verwende try-with-resources (die Ressourcen werden dann automatisch geschlossen)
3. Verlass Dich nicht darauf, dass Ergebnisse vorhanden sind (habe ich im Code jetzt nicht berücksichtigt). Die row könnte -1 sein und zwischen dem Laden der Werte der ComboBox und der Auswahl durch den Benutzer liegt genügend Zeit, dass jemand ausgerechnet die Filiale löscht, die der Benutzer dann auswählt.


----------



## Xyz1 (3. Apr 2019)

mihe7 hat gesagt.:


> Hier findet keine saubere Trennung zwischen DB und UI statt.


Das wäre als Antipattern die so genannte Gottklasse oder Blob.   Siehe hier: https://de.wikipedia.org/wiki/Gottobjekt


----------



## maGG (3. Apr 2019)

Ok, das verstehe ich nicht ganz; muss man das so trennen? Ich habe einige SQL Methode, zum Befüllen, editieren, etc. muss man das immer trennen? Weil das würde enorm viel mehr Zeilen Code bedeuten :O


----------



## Elenteria (3. Apr 2019)

Muss man nicht, aber wenn es Sauber und leichter erweiterbar sein soll tust du gut daran das zu Trennen.


----------



## maGG (3. Apr 2019)

> 1. Baue Parameter in SQL-Strings nie manuell ein. Verwende die setXXX-Methoden von PreparedStatement.
> 2. Verwende try-with-resources (die Ressourcen werden dann automatisch geschlossen)
> 3. Verlass Dich nicht darauf, dass Ergebnisse vorhanden sind (habe ich im Code jetzt nicht berücksichtigt). Die row könnte -1 sein und zwischen dem Laden der Werte der ComboBox und der Auswahl durch den Benutzer liegt genügend Zeit, dass jemand ausgerechnet die Filiale löscht, die der Benutzer dann auswählt.


habe ich das mit finally -> rs.close() und pst.close() nicht schon sichergestellt? Also das es geschlossen wird meine ich. Der Code ist von mir zusammengesetzt; habe leider kaum Beispiele dazu im Internet gefunden wie man das perfekt machen kann. Auch meine anderen SQL Methode sehen ähnlich aus und wären dann vermutlich nicht optimal, hmm ...


----------



## mihe7 (3. Apr 2019)

maGG hat gesagt.:


> habe ich das mit finally -> rs.close() und pst.close() nicht schon sichergestellt?


Jein. Man kann das mit finally machen, das war in Java 6 Standard (ging nicht anders). Der Code ist aber hässlich, weil Du überall finally-Blöcke brauchst, die allerdings etwas anders aussehen müssten als Deiner. Wenn bei Dir z. B. eine Exception in prepareStatement aufritt, ist Dein ResultSet (normalerweise, ich sehe bei Dir gar keine Deklaration) null, was im finally-Block bei rs.close() zu einer NullPointerException führt. Seinerzeit habe ich (wie vermutlich viele andere auch) dafür Utility-Methoden geschrieben, die in etwa so aussahen:

```
public static void close(ResultSet rs, Statement stmt) {
    if (rs != null) {
        try { rs.close(); } catch (SQLException ex) { /* hier ggf. Warnung loggen. */ }
    }
    if (stmt != null) {
        try { stmt.close(); } catch (SQLException ex) { /* hier ggf. Warnung loggen. */ }
    }
}
```
Die Methode wure dann im finally-Block verwendet `close(rs, stmt);`. Seit Java 7 braucht man den ganzen Käse Dank try-with-resources nicht mehr. 



maGG hat gesagt.:


> Ok, das verstehe ich nicht ganz; muss man das so trennen?


Müssen muss man gar nichts... Im konkreten Fall sorgt die Trennung dafür, dass das SQL-Gedöns abgeschlossen ist, bevor irgend etwas anderes passiert. Wenn Du Dir jetzt den actionListener ansiehst, fällt Dir viel schneller auf, dass Du Events hin und her feuerst, weil der row-Index nicht abgeprüft wird  (Du musst am Anfang noch prüfen, ob der row-Index 0 ist).


----------



## maGG (3. Apr 2019)

Oh man  hatte eigentlich gedachte das wäre leichter :O
habe gerade mal deine Anpassung ausprobiert und bei mir kommen folgende Fehler:

```
try(pst = conn.prepareStatement(sql)){ // hier kommt bei mir "variables in try-with-resources not supported in -source 1.8"
try(rs = pst.executeQuery()){ // hier kommt "the try-with-resources resource must either be a variable declaration or an expression
```
 denoting a reference to a final or effectively final variable"


----------



## mrBrown (3. Apr 2019)

maGG hat gesagt.:


> Oh man  hatte eigentlich gedachte das wäre leichter :O
> habe gerade mal deine Anpassung ausprobiert und bei mir kommen folgende Fehler:
> 
> ```
> ...


Stell deine IDE auf Java 8 und deklarier die Variablen dort, wo du sie auch initialisierst


----------



## mihe7 (3. Apr 2019)

Ja, da fehlen die Deklarationen:

```
try(PreparedStatement pst = conn.prepareStatement(sql)) {
            pst.setInt(1, row);
            try(ResultSet rs = pst.executeQuery()) {
```


----------



## mihe7 (3. Apr 2019)

maGG hat gesagt.:


> Oh man  hatte eigentlich gedachte das wäre leichter :O


Software hat das Problem, inhärent komplex zu sein. Als wäre das nicht genug, soll sich Software im Gegensatz zu anderen Gütern möglichst einfach an neue bzw. veränderte Anforderungen anpassen lassen. Um das in den Griff zu bekommen, haben sich über die Zeit verschiedene Herangehensweisen, Paradigmen, Pattern etc. herausgebildet. Und nach wie vor muss man feststellen, dass es kein Patentrezept gibt. Kurz: schnell, gut, kostengünstig - such Dir zwei aus.


----------



## maGG (3. Apr 2019)

Ja das ist leider richtig und es kann sehr frustrierend sein das anderen Menschen verständlich zu machen, die nur die Oberfläche sehen und sich denken das wäre so leicht 

Also mit deinen Code habe ich es geschafft die Methode auf zwei zu trennen. Jedoch habe ich jetzt gerade ein anderes Problem:


```
private void loadTitel(int row) throws SQLException{
        String sql = "SELECT * FROM Titel WHERE id=?";
        try(PreparedStatement pst = conn.prepareStatement(sql)){
            pst.setInt(1, row);
            try(ResultSet rs = pst.executeQuery()){
            String label = rs.getString("LABEL");
            boolean infront = rs.getBoolean("VORNE");
            if(infront = false){
                titelvorn = label;
                titelhinten = "";
            }else{
                titelhinten = label;
                titelvorn = "";
            }
            }
        }
        }
  private void jComboBoxTitelblaActionPerformed(java.awt.event.ActionEvent evt) {
        int row = jComboBoxTitelbla.getSelectedIndex();
        try{
            loadTitel(row);
            Testpreview();
        }catch(SQLException e){
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, e);
            //return false; // wozu return false???
        }
}
```
wie man glaub unschwer erkennen kann soll die Variable titelvorn gesetzt werden wenn infront true ist und wenn es false ist soll titelhinten gesetzt werden. Leider zeigt er bei der if-Verzweigung alle Titel vorn an wenn ich if(infront = true)... schreibe und alle hinten wenn ich if(infront = false)... schreibe ... ist stehe etwas auf den Schlauch gerade, HÄÄÄ :'(


----------



## Elenteria (3. Apr 2019)

probiers mal mit == statt = im if-statement. Mit == vergleichst du Dinge, mit = weißt du sie zu


----------



## maGG (3. Apr 2019)

Oh man, ja das war dumm 
hmm geht aber leider immer noch nicht; es zeigt nur eine Richung an, er belegt nur titelvorn (egal bei welchen Wert). Eigentlich müsste titelvorn zu mindest mal irgendwann leer sein.

Danke übrigens nochmal für eure Hilfe!


----------



## maGG (3. Apr 2019)

Hab noch ein Fehler entdeckt: Wenn ich auf das erste Item der ComboBox klicke kommt "java.sql.Exception: REsultSet closed"


----------



## mihe7 (3. Apr 2019)

In dem Stacktrace steht genau, wo die Exception auftritt.


----------



## maGG (3. Apr 2019)

OK, habe den Fehler behoben; der Logikfehler bei der Verzweigung bleibt jedoch. Also die Attribute titelvorn und titelhinten werden nicht richtig gesetzt bzw. es wird nur titelhinten mit Werten belegt.

```
private void loadTitel(int row) throws SQLException{
            String sql = "SELECT * FROM Titel WHERE id=?";
            try(PreparedStatement pst = conn.prepareStatement(sql)){
                pst.setInt(1, row);
                try(ResultSet rs = pst.executeQuery()){
                    if(row != 0){
                        String label = rs.getString("LABEL");
                        boolean infront = rs.getBoolean("VORNE");
                        if(infront == true){
                            titelvorn = label;
                            titelhinten = "";
                        }else{
                            titelvorn = "";
                            titelhinten = label;
                        }
                    }
                }
            }
        }
```


```
CREATE TABLE Titel (
    id    INTEGER PRIMARY KEY AUTOINCREMENT,
    LABEL TEXT,
    VORNE BOOLEAN
);
```
So kompliziert kann das Ganze doch nicht sein ...  aber es funktioniert leider noch nicht, seht ihr direkt etwas falsches?


----------



## mrBrown (3. Apr 2019)

Sollte nicht noch ein `rs.next()` nötig sein?


----------



## maGG (3. Apr 2019)

mrBrown hat gesagt.:


> Sollte nicht noch ein `rs.next()` nötig sein?


was würde das denn machen? :O


----------



## mrBrown (3. Apr 2019)

Prüft, ob im ResultSet eine weitere Zeile verfügbar ist, und "springt" zu dieser. 
Nach Ausführen der Query zeigt das ResultSet noch auf kein Ergebnis (es könnte ja auch leer sein), mit next zeigt es dann auf die erste Zeile (und mit weiteren Aufrufen dann auf die danach.)


----------



## mihe7 (3. Apr 2019)

maGG hat gesagt.:


> So kompliziert kann das Ganze doch nicht sein ...  aber es funktioniert leider noch nicht, seht ihr direkt etwas falsches?


LOL, ne kompliziert ist das alles nicht. Die Prüfung auf row == 0 sollte ganz am Anfang stehen (s. Kommentar #11, ggf. besser noch in der Methode, die loadTitel aufruft) und nach dem Ausführen der Abfrage sollte man prüfen, ob die Abfrage überhaupt ein Ergebnis liefert (s. Kommentar #6, Nummer 3):

```
private void loadTitel(int row) throws SQLException{
            if (row == 0) {
                return;
            }
            String sql = "SELECT * FROM Titel WHERE id=?";
            try(PreparedStatement pst = conn.prepareStatement(sql)){
                pst.setInt(1, row);
                try(ResultSet rs = pst.executeQuery()){
                    if(rs.next()){
                        String label = rs.getString("LABEL");
                        boolean infront = rs.getBoolean("VORNE");
                        if(infront){
                            titelvorn = label;
                            titelhinten = "";
                        }else{
                            titelvorn = "";
                            titelhinten = label;
                        }
                    }
                }
            }
        }
```


----------



## maGG (4. Apr 2019)

Vielen lieben Dank @mihe7 und euch anderen für die Hilfe. Es sieht bei mir jetzt so aus:


```
private void jComboBoxTitelblaActionPerformed(java.awt.event.ActionEvent evt) {                                                 
        int row = jComboBoxTitelbla.getSelectedIndex();
        if (row == 0) {           
            titelvorn = "";
            titelhinten = "";
            Testpreview();
            return;
        }           
        try{
            loadTitel(row);
            Testpreview();
        }catch(SQLException e){
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, e);
            //return false;
        }

        private void loadTitel(int row) throws SQLException{           
            String sql = "SELECT * FROM Titel WHERE id=?";
            try(PreparedStatement pst = conn.prepareStatement(sql)){
                pst.setInt(1, row);
                try(ResultSet rs = pst.executeQuery()){
                    if(rs.next()){
                        String label = rs.getString("LABEL");
                        boolean infront = rs.getBoolean("VORNE");
                        if(infront){
                            titelvorn = label + " ";
                            titelhinten = "";
                        }else{
                            titelvorn = "";
                            titelhinten = ", " + label;
                        }
                    }
                }
            }
        }
```
Ist es jetzt richtig so? 
Ich verstehe ehrlich gesat nicht so ganz was das "return;" und "return false" genau macht 
Also return ist ja eine Rückgabe, aber da ist jetzt gar keine Variable mit drin?


----------



## mihe7 (4. Apr 2019)

Das sieht mal nicht verkehrt aus. Das return sorgt dafür, dass an der Stelle der Methodenaufruf (die Methode muss ja von irgendwo aus aufgerufen worden sein) beendet wird. Einen Rückgabewert gibt es hier nicht, weil die Methode mit dem Rückgabetyp "void", also "nichts", deklariert wurde. Ein "return false" wäre dem entsprechend falsch, da die Methode nicht mit dem Rückgabetyp "boolean" deklariert wurde.

Konkret: der Listener prüft ab, ob row == 0 gilt, falls ja, werden testvorn und testhinten gesetzt und die die Methode Testpreview() wird aufgerufen. Dann wird die Ausführung der Listener-Methode per return beendet. Ist row != 0, dann werden loadTitel(row) und Testpreview() aufgerufen. Tritt dabei eine SQLException auf, wird der betreffende catch-Block ausgeführt (Stacktrace geschrieben, JOptionPane angezeigt).


----------



## maGG (4. Apr 2019)

Ok, cool Dankschön @mihe7!

Ich hab leider noch ein paar weitere Methoden, die ich wohl optimieren sollte, z.B. diese hier:


```
private void fillComboBoxFilialenDeutschland(){
            try{
                String sql = "SELECT * FROM Filialen_Deutschland";
                pst = conn.prepareStatement(sql);
                rs = pst.executeQuery();
                //jComboBoxSchablonen.addItem(sql);
                while(rs.next()){
                    String label = rs.getString("LABEL");
                    jComboBoxFilialenDeutschland.addItem(label);
                }
            }catch(Exception e){
                e.printStackTrace();
            }finally{
                try{
                    rs.close();
                    pst.close();
                    //conn.close();
                }catch(SQLException e){
                    e.printStackTrace();
                    JOptionPane.showMessageDialog(null, e);
                }
        }
        }
```
Ich habe das mal versucht, mit euren Vorschlägen anzupassen:

```
private void fillComboBoxGermanBranches() throws SQLException{
            String sql = "SELECT * FROM Filialen_Deutschland";
            try(PreparedStatement pst = conn.prepareStatement(sql)){
                try(ResultSet rs = pst.executeQuery()){
                    while(rs.next()){
                        String label = rs.getString("LABEL");
                        jComboBoxFilialenDeutschland.addItem(label);
                    }
                }
            }
        }
```
Könnte das so in etwa funktionieren? Ich hätte gerne eine Fehlerausgabe mit JOptionPane, wo würde ich das reinschreiben?

Update: Kommen folgende Fehler:

```
C:\...\GUI.java:130: error: unreported exception SQLException; must be caught or declared to be thrown
        fillComboBoxGermanBranches();
1 error
C:\...\nbproject\build-impl.xml:953: The following error occurred while executing this line:
C:\...\nbproject\build-impl.xml:270: Compile failed; see the compiler error output for details.
BUILD FAILED (total time: 11 seconds)
```


----------



## mihe7 (4. Apr 2019)

maGG hat gesagt.:


> Ich hätte gerne eine Fehlerausgabe mit JOptionPane, wo würde ich das reinschreiben?


s. Kommentar #6 

EDIT: damit ich Dich nicht auf die falsche Spur führe, sollte ich noch klarstellen, dass es nur um das try-catch im ActionListener geht.


----------



## maGG (4. Apr 2019)

mihe7 hat gesagt.:


> s. Kommentar #6
> 
> EDIT: damit ich Dich nicht auf die falsche Spur führe, sollte ich noch klarstellen, dass es nur um das try-catch im ActionListener geht.


Danke für deine Antwort!

Ok habe es jetzt um das Aufruf-Statement geschrieben:

```
try {
            fillComboBoxGermanBranches();
        } catch (SQLException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, e);
        }
```
Das heißt natürlich, dass ich jetzt jedes mal ein Try-Catch-Block brauche, wenn ich die Methode aufrufe. Ich rufe die Methode u.a. beim Start im Standardkonstruktor auf. Ist das das die eleganteste Lösung oder kann ich das auch in der Methode selbst einbetten, oder macht man das nicht?


----------



## mihe7 (4. Apr 2019)

In diese Methode bettet man das nicht ein, wenn man sauber trennen will (in dem Fall ruft man dort auch keine Methoden der JComboBox auf...) Und eigentlich sollte die Methode in eine eigene Klasse.

Die Methode fillComboBoxGermanBranches müsste eigentlich loadGermanBranches heißen und eine Liste zurückgeben, z. B.


```
private List<String> loadGermanBranches() throws SQLException{
            String sql = "SELECT * FROM Filialen_Deutschland";
            try(PreparedStatement pst = conn.prepareStatement(sql)){
                try(ResultSet rs = pst.executeQuery()){
                    List<String> result = new ArrayList<>();
                    while(rs.next()){
                        result.add(rs.getString("LABEL"));
                    }
                    return result;
                }
            }
        }
```

Wie gesagt, eigentlich lagert man diesen Code in separate Klassen aus. Die fillComboBoxGermanBranches-Methode könnte dann so aussehen:

```
private void fillComboBoxGermanBranches() {
        try {
            for (String label : loadGermanBranches()) {
                jComboBoxFilialenDeutschland.addItem(label);
            }
        } catch (SQLException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, e);
        }
    }
```

Du kannst auch hergehen und in der load-Methode die SQLException abfangen und durch eine unchecked Exception ersetzen, dann fallen die try-catch-Blöcke ganz weg. Dann müsstest Du aber beim Thread einen Exception-Handler für nicht abgefangene Exceptions registrieren.


----------



## maGG (4. Apr 2019)

Ah ok, man lernt nie aus  bin kein richtiger Informatiker, also hab interdisziplinär Grundlagen der Ingenieurinformatik (Java) gehört und Datenbanken für Ingenieuranwendungen (CAD, Revit, C# mit Visual Basic und SQL mit Java), aber nach zwei Module und eine Hand voll von Hausübungen befähigt einen das längst noch nicht sowas mal locker von der Hand runterzuschreiben ;'D
Für mein Aushilfsjob hier wird eh nix erwartet, mache das alles freiwillig.

Hmm deiner Variante wäre wohl besser, evtl. mache ich das noch am Ende, wenn die Hauptfunktionen des Programms alle da sind.


----------



## maGG (4. Apr 2019)

Hmm neues Problem 

Der Code bei #26 hat einen Nachteil: Wenn ich die Methode fillComboBoxGermanBranches() mehrmals aufrufe werden die Einträge der ComboBox immer größer und ich habe Dubletten. Wie kann ich das elegant vermeiden?
Habe es mit .removeAllItems() probiert, aber das funktioniert leider nicht richtig. Ich will ja nicht alle Einträge löschen, z.B. der erste "Bitte auswählen" soll bleiben. Habt ihr eine Idee?


----------



## mrBrown (4. Apr 2019)

maGG hat gesagt.:


> Hmm neues Problem









maGG hat gesagt.:


> Der Code bei #26 hat einen Nachteil: Wenn ich die Methode fillComboBoxGermanBranches() mehrmals aufrufe werden die Einträge der ComboBox immer größer und ich habe Dubletten. Wie kann ich das elegant vermeiden?
> Habe es mit .removeAllItems() probiert, aber das funktioniert leider nicht richtig. Ich will ja nicht alle Einträge löschen, z.B. der erste "Bitte auswählen" soll bleiben. Habt ihr eine Idee?


Die Methode nicht mehrmals aufrufen?  

Warum willst du die Methode denn mehrmals aufrufen? (Falls das schon irgendwo steht hab ich's überlesen...)


----------



## maGG (4. Apr 2019)

mrBrown hat gesagt.:


> Warum willst du die Methode denn mehrmals aufrufen? (Falls das schon irgendwo steht hab ich's überlesen...)


Also wenn man das Programm startet sollen die ComboBoxen alle gefüllt werden. Dann gibt es aber ein zweites Fenster, welches man öffnen kann und wo man die Datenbank im Programm selbst in Tabellen-form editeren kann - hat man das getan und z.B. einen neuen Eintrag hinzugefügt, dann kehrt man auf das Hauptfenster zurück. Beim Zurückkehren soll das Zweitfenster geschlossen werden und alle Comboboxen neu befüllt werden, denn schließlich könnte man an der Datenbank etwas geändert haben. Natürlich könnte ich auch einfach das Programm neustarten, aber ich denke man kann das auch so irgendwie lösen.

Hmm macht das für dich Sinn?


----------



## mrBrown (4. Apr 2019)

Entweder alle entfernen, und dann alle inklusive des ersten Eintrags neu hinzufügen, oder ein eigenes ComboBoxModel nutzen, welches das Updaten der Einträge unterstützt und dabei den ersten immer erhält.


----------



## maGG (9. Apr 2019)

Heyho, habe ein neues Problem 
(Habe das versucht aufzuteilen, aber irgendetwas funktioniert noch nicht, also die Vorschau Funktion geht, aber das Updaten der Datenbank leider noch nicht)

Seht ihr einen Fehler? Vielen Dank! 


```
private void jButtonUpdateA1ActionPerformed(java.awt.event.ActionEvent evt) {                                               
        int row = Integer.parseInt(jTextFieldF1.getText());
        String label = jTextFieldF2.getText();
        String strasze = jTextFieldF3.getText();
        String plzort = jTextFieldF4.getText();
        String ort = jTextFieldF5.getText();
        int telland = Integer.parseInt(jTextFieldF6.getText());
        long telanfangint = Long.parseLong(jTextFieldF7.getText());
        String telanfangtxt = jTextFieldF8.getText();
        String telendedefault = jTextFieldF9.getText();
        String faxanfangtxt = jTextFieldF10.getText();
        String faxendetxt = jTextFieldF11.getText();
        if  (jTextFieldF1.getText().equals("") || label.equals("") || strasze.equals("")
            || plzort.equals("") || ort.equals("") || jTextFieldF6.getText().equals("")
            || jTextFieldF7.getText().equals("") || telanfangtxt.equals("") || telendedefault.equals("")
            || faxanfangtxt.equals("") || faxendetxt.equals("")){
            JOptionPane.showMessageDialog(null, "Fehler: Bitte notwendige Parameter setzen");
        }else {           
            try{
                updateTableGermanBranches(row, label, strasze, plzort, ort, telland, telanfangint, telanfangtxt, telendedefault, faxanfangtxt, faxendetxt);
                viewTableGermanBranches();
                JOptionPane.showMessageDialog(null, "Filiale aktualisiert");
            }catch(SQLException e){
                e.printStackTrace();
                JOptionPane.showMessageDialog(null, e);
            }
        }
    }
```


```
public void updateTableGermanBranches(int row, String label, String strasze, String plzort, String ort, int telland, long telanfangint, String telanfangtxt, String telendedefault, String faxanfangtxt, String faxendetxt) throws SQLException{
        String sql = "UPDATE Filialen_Deutschland SET id=?, LABEL=?, STRASZE=?, PLZ_ORT=?, ORT=?,TEL_LAND=?, TEL_ANFANG_INT=?,"
                        + " TEL_ANFANG_TXT=?, TEL_ENDE_DEFAULT=?, FAX_ANFANG_TXT=?, FAX_ENDE_TXT=? WHERE id=?";
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
            try (ResultSet rs = pst.executeQuery()) {
                if (rs.next()) {
                    pst.setInt(1, row); // id
                    pst.setString(2, label); //LABEL
                    pst.setString(3, strasze); //STRASZE
                    pst.setString(4, plzort); //PLZ_ORT
                    pst.setString(5, ort); //ORT
                    pst.setInt(6, telland); //TEL_LAND
                    pst.setLong(7, telanfangint); //TEL_ANFANG_INT
                    pst.setString(8, telanfangtxt); //TEL_ANFANG_TXT
                    if (telendedefault.equals("")) {
                        pst.setObject(9, null);
                    } else {
                        pst.setInt(9, Integer.parseInt(telendedefault)); //TEL_ENDE_DEFAULT
                    }
                    pst.setString(10, faxanfangtxt); //FAX_ANFANG_TXT
                    if (faxendetxt.equals("")) {
                        pst.setObject(11, null);
                    } else {
                        pst.setString(11, faxendetxt); //FAX_ENDE_TXT
                    }
                    pst.setInt(12, row); // id (doppelt)
                    pst.executeUpdate();                               
                }
            }
        }   
    }
```


```
public void viewTableGermanBranches() throws SQLException{
         String sql = "SELECT * FROM Filialen_Deutschland";
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
            try (ResultSet rs = pst.executeQuery()) {
                if (rs.next()) {
                    jTableFilialen_Deutschland.setModel(DbUtils.resultSetToTableModel(rs));
                }
            }
        }   
    }
```


```
java.sql.SQLException: Query does not return results
    at org.sqlite.jdbc3.JDBC3PreparedStatement.executeQuery(JDBC3PreparedStatement.java:68)
    at signatur.GUI.updateTableGermanBranches(GUI.java:792)
    at signatur.GUI.jButtonUpdateA1ActionPerformed(GUI.java:5154)
    at signatur.GUI.access$1000(GUI.java:60)
    at signatur.GUI$11.actionPerformed(GUI.java:1493)
```


----------



## mrBrown (9. Apr 2019)

maGG hat gesagt.:


> GUI.java:5154


WTF.


Spoiler



bzw: *WTF?!?!*





maGG hat gesagt.:


> ```
> public void updateTableGermanBranches(int row, String label, String strasze, String plzort, String ort, int telland, long telanfangint, String telanfangtxt, String telendedefault, String faxanfangtxt, String faxendetxt) throws SQLException{
> String sql = "UPDATE Filialen_Deutschland SET id=?, LABEL=?, STRASZE=?, PLZ_ORT=?, ORT=?,TEL_LAND=?, TEL_ANFANG_INT=?,"
> + " TEL_ANFANG_TXT=?, TEL_ENDE_DEFAULT=?, FAX_ANFANG_TXT=?, FAX_ENDE_TXT=? WHERE id=?";
> ...



Du solltest das PreparedStatement erst befüllen, und dann die Query ausführen.

etwa:

```
try (PreparedStatement pst = conn.prepareStatement(sql)) {
    pst.setInt(1, row); // id
    pst.setString(2, label); //LABEL
    ....
    pst.executeUpdate()
}
```
ResultSet ist dabei überflüssig, du willst ja ein Update und kein Select ausführen.


----------



## maGG (9. Apr 2019)

mrBrown hat gesagt.:


> WTF.


haha naja Methoden ausgelagert habe ich noch nicht soviele und es ist viel noch auskommentiert, da wird einiges an Zeilen wieder von verschwinden.
Hat mit deinen Tipps auf jeden Fall geklappt jetzt, hab noch ein paar Datentypen Fehler, aber die krieg ich morgen bestimmt noch weg, Dankeschön!


----------



## mihe7 (9. Apr 2019)

maGG hat gesagt.:


> naja Methoden ausgelagert habe ich noch nicht soviele


Ich hatte im letzten Quartal Fremdcode vor mir, da hatte eine einzige Methode mehr Zeilen - die dafür fast alle sehr ähnlich waren... Da brauchst Du Karte und Kompass, dass Du noch weißt, ob Du an der richtigen Stelle editierst.


----------



## mrBrown (9. Apr 2019)

Mehr als 5_000 Zeilen? :O Bei 1/100 der Länge bin ich ja schon kurz davor, laut schreiend im Kreis durchs Büro zu rennen...


----------



## mihe7 (9. Apr 2019)

Hab gerade extra nochmal nachgesehen: die Klasse hat 12.380 Zeilen, es gibt ein paar kleinere Methoden sowie eine mit 5.733, eine mit 3.556 und eine mit 1.649 Zeilen... 


Spoiler



WTF?!?


----------



## maGG (10. Apr 2019)

Hmm bei meiner Vorschau-Methode für die Tabelle wird der erste Wert mit id=1 nicht angezeigt:

```
public void viewTableForeignBranches() throws SQLException{
        String sql = "SELECT * FROM Filialen_Ausland";
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
            try (ResultSet rs = pst.executeQuery()) {
                if (rs.next()) {
                    jTableFilialen_Ausland.setModel(DbUtils.resultSetToTableModel(rs));
                }
            }
        }   
    }
```
 Ne Idee woran das liegen könnte?


----------



## mihe7 (10. Apr 2019)

Vermutlich lädst Du in Deiner resultSetToTableModel-Methode in einer while-Schleife die Daten. Die Schleifenbedingung wird dort rs.next() sein. D. h. Du rufst zu Beginn rs.next() zweimal auf: einmal hier im if und einmal im while in der Methode.


----------



## maGG (10. Apr 2019)

jup, das wars, Danke!


----------



## maGG (10. Apr 2019)

Ich habe noch ein Anliegen: Mein Programm umfasst zwei Fenster. Man kann von einem Fenster zu anderen gelangen. Das habe ich bisher so gelöst:


```
//von Fenster 1 zu Fenster 2
this.setVisible(false);
Filialen.setVisible(true); //das zweite Fenster

//von Fenster 2 zu Fenster 1
Filialen.setVisible(false);
this.setVisible(true);
```

Jetzt wollte ich ja, dass die ComboBox(en) nach Editieren der Datenbank aktualisiert werden. Ein eigenes ComboBox Model zu schreiben kann ich aber nicht und wäre glaub zu aufwändig. Daher kam mir die Idee ich könnte doch von Fenster 2 auf Fenster 1 das Programm einfach neustarten - dann würde die ComboBox(en) richtig befüllt sein.

Kann ich das so machen?


```
Filialen.dispose(); //soll das zweite Fenster schließen
GUI.main(null); //soll Programm neustarten
```

Oder muss ich mit System.exit() arbeiten?

Ich verstehe die Unterschiede nicht so richtig.


----------



## mihe7 (10. Apr 2019)

maGG hat gesagt.:


> Ein eigenes ComboBox Model zu schreiben kann ich aber nicht und wäre glaub zu aufwändig.


Quatsch und Quatsch.

Abgesehen davon: hattest Du nicht eine Methode, die das ComboBox-Model (mit Daten aus der DB) liefert? Dann bräuchtest Du ja nur das Model der ComboBox zu ersetzen.


----------



## mihe7 (10. Apr 2019)

Je mehr ich darüber nachdenke, desto weniger verstehe ich, was Du hier eigentlich betreibst. Hast Du mal einen Screenshot oder ähnliches?


----------



## maGG (10. Apr 2019)

mihe7 hat gesagt.:


> Abgesehen davon: hattest Du nicht eine Methode, die das ComboBox-Model (mit Daten aus der DB) liefert? Dann bräuchtest Du ja nur das Model der ComboBox zu ersetzen.


Ja, aber dann hatte ich das Problem das die Füllmethode einmal beim Start des Programms ausgeführt wird und dann nochmal beim Klicken des Buttons "Zurück zum Hauptfenster". Das heißt für die ComboBox, dass die sie doppelt befüllt wird. Das könnte ich mit Löschen und dann Befüllen lösen, aber dann fehlt der erste Wert mit "Bitte auswählen".

Hier mal zwei Bilder zum Verständnis:

Erstes Fesnter (Hauptfenster):


Zweites Fenster (zum Editieren der DB):


----------



## Robat (10. Apr 2019)

Hast du schon mal an das CardLayout gedacht?


----------



## mihe7 (10. Apr 2019)

maGG hat gesagt.:


> Das heißt für die ComboBox, dass die sie doppelt befüllt wird. Das könnte ich mit Löschen und dann Befüllen lösen, aber dann fehlt der erste Wert mit "Bitte auswählen".


Das kannst Du auch lösen, indem Du beim Befüllen nicht direkt die ComboBox befüllst, sondern ein z. B. DefaultComboBoxModel und dann das Model der ComboBox ersetzt.

Statt

```
private void fillComboBoxGermanBranches() {
        try {
            for (String label : loadGermanBranches()) {
                jComboBoxFilialenDeutschland.addItem(label);
            }
        } catch (SQLException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, e);
        }
    }
```
hättest Du dann z. B.

```
private void fillComboBoxGermanBranches() {
        try {
            DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
            for (String label : loadGermanBranches()) {
                model.addElement(label);
            }
            jComboBoxFilialenDeutschland.setModel(model);
        } catch (SQLException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, e);
        }
    }
```

EDIT: model in der for-Schleife war abhanden gekommen. Hinzugefügt

Zum Screenshot: jetzt verstehe ich, worum es geht. Ich würde Dir dringend(!) empfehlen, den Code zu restrukturieren.


----------



## maGG (10. Apr 2019)

Meine Füll-Methode sieht aktuell so aus:

```
private void fillComboBoxGermanBranches() throws SQLException {
        String sql = "SELECT * FROM Filialen_Deutschland";
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
            try (ResultSet rs = pst.executeQuery()) {
                while (rs.next()) {
                    String label = rs.getString("LABEL");
                    //jComboBoxFilialenDeutschland.removeAllItems();
                    //jComboBoxFilialenDeutschland.addItem("Bitte auswählen");
                    jComboBoxFilialenDeutschland.addItem(label);
                }
            }
        }
    }
```
Was meinst du mit restrukturieren? Das ich es noch mehr aufteilen sollte auf verschiedene Methoden und diese auslagere in Klassen? Was wäre denn der Vorteil davon? Leider wird dieses Programm vermutlich nie wieder jemand anfassen außer mir. In der Firma in der ich als studentische Aushilfe arbeite kann niemand programmieren und die Anforderungen sind extrem gering bzw. habe ich ein sehr begrenztes Zeitbudget (30h lol) und derartige Bemühungen wird vermutlich am Ende niemand hier schätzen bzw. müsste ich diese in meiner Freizeit machen. :/
Oder meinst du etwas anderes damit?


----------



## mihe7 (10. Apr 2019)

maGG hat gesagt.:


> Meine Füll-Methode sieht aktuell so aus:


Dann sieht sie halt in Zukunft wie folgt aus  

```
private void fillComboBoxGermanBranches() throws SQLException {
        String sql = "SELECT * FROM Filialen_Deutschland";
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
            try (ResultSet rs = pst.executeQuery()) {
                DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
                model.addItem("Bitte auswählen");
                while (rs.next()) {
                    String label = rs.getString("LABEL");
                    model.addElement(label);
                }
                jComboBoxFilialenDeutschland.setModel(model);
            }
        }
    }
```



maGG hat gesagt.:


> Das ich es noch mehr aufteilen sollte auf verschiedene Methoden und diese auslagere in Klassen? Was wäre denn der Vorteil davon?


Antworte ich gleich noch darauf.


----------



## mihe7 (10. Apr 2019)

Hat länger gedauert. Der Vorteil davon ist, dass Du a) Struktur in den Code bekommst und b) Klassen und Objekte leichter wiederverwenden kannst. 

Zum Beispiel: die Tabelle im zweiten Fenster und die ComboBox im ersten Fenster sind unterschiedliche Ansichten derselben Objektliste. Wenn Du diese in einem "Repository" separat darstellst und dieses in beiden Fällen verwendest, weiß das Repository, wann sich etwas geändert hat. Daher kann es interessierte Objekte (Observer) über derlei Änderungen informieren, die ihrerseits entsprechend darauf reagieren können. 

Anderes Beispiel: durch bessere Abstraktion kannst Du Code sparen. So unterscheiden sich die verschiedenen ComboBoxen ja nur in der Tabelle, die verwendet wird.  

Aber, wenn Du so zurechtkommst und es Dir keiner dankt -> vergiss es.


----------



## maGG (16. Apr 2019)

Achso, verstehe.

Wie kann ich denn meine Methoden "auslagern"? Bisher habe ich das teilweise mit anderen Klassen gemacht und diese dann mit Konstruktoraufruf in meiner Hauptklasse eingebunden. Aber wenn ich jetzt eine neue Klasse "LoadSQL"erstelle und in dieser all meine Methoden mit SQL Anweisungen schreiben möchte, dann geht das bisher nicht. Die Methoden funktionieren ja in Hauptklasse schon, mit einem simplen Copy&Paste in LoadSQL ist es aber wohl nicht getan - scheinbar findet es die Methode dann nicht mehr. Könnte das an einem fehlenden Import liegen? (Die Klasse befindet sich im selben package wie meine Hauptklasse "GUI" - benutze Netbeans)


----------



## mihe7 (16. Apr 2019)

Nein, so einfach geht das natürlich nicht, weil die Methoden immer noch stark an das UI gebunden sind. Du müsstest sie erst etwas umschreiben (s. Kommentar #31), so dass sie unabhängig vom UI Ergebnis liefern. In der Regel schreibt man sich ein Repository und bindet das UI daran.


----------



## maGG (16. Apr 2019)

mihe7 hat gesagt.:


> Nein, so einfach geht das natürlich nicht, weil die Methoden immer noch stark an das UI gebunden sind. Du müsstest sie erst etwas umschreiben (s. Kommentar #31), so dass sie unabhängig vom UI Ergebnis liefern. In der Regel schreibt man sich ein Repository und bindet das UI daran.


Ja die Methoden habe ich schon tesweise mal geändert, d.h. das ich z.B. GUI-Elemente als übergabe Paramenter allgemein deklariert habe. Also z.B. JComboBox box1 ... und dann setze ich beim Aufruf der Methode entsprechende Comboboxen ein. Das Problem entsteht also nicht in meiner neuen Klasse (da wird nix rot angezeigt), sondern beim Aufruf in der Hauptklasse. (scheinbar findet er meine Methode nicht). Ich weiß aber leider nicht warum


----------



## mihe7 (16. Apr 2019)

maGG hat gesagt.:


> d.h. das ich z.B. GUI-Elemente als übergabe Paramenter allgemein deklariert habe.


Nein, das UI-Elemente haben dort nichts verloren.


----------



## maGG (16. Apr 2019)

mihe7 hat gesagt.:


> Nein, das UI-Elemente haben dort nichts verloren.


Das ist aber glaub die einzige Möglichkeit das Ganze auf 2 Methoden zu beschränken. Wenn ich es so mache, wie du es vorgeschlagen hast, brauche ich noch eine dritte Methode. Objektorientiert zu programmieren, um möglichst Copy&Paste zu vermeiden verstehe ich und finde ich sinnvoll. Das andere kann ich nicht ganz nachvollziehen, evtl. fehlt mir da die Erfahrung im Bereich Softwareprogrammierung. Aktuell scheint mir dass das Ganze nur unnötig zu verkomplizieren :O

Trotzdem danke für deine Hilfe!


----------



## mihe7 (16. Apr 2019)

Kein Problem, wie gesagt:


mihe7 hat gesagt.:


> Aber, wenn Du so zurechtkommst und es Dir keiner dankt -> vergiss es.


----------



## maGG (17. Apr 2019)

Hmm ok, also ich hab mal recherchiert; scheinbar kann man in Java gar keine Methoden auslagern, jedenfalls nicht im herkömmlichen Sinne. Damit meine ich folgendes:

Hauptklasse {
Methode 1{ Anweisungen... }
}

=>

Hauptklasse {
Methode 1;
}
ausgelagerteKlasse {
Methode 1{ Anweisungen... }
}

Alle Dinge die man in Google dazu findet sind Beispiele, die nicht zielführend sind oder Forenbeiträge von Leute, die ähnliche Fragen haben wie ich und wo es keine Lösung gibt. Die Aufteilung in #31 funktioniert auch nicht bei mir. Sobald ich eine Methode in eine andere Klasse stecke findet er sie nicht mehr, auch mit Erben nicht. Es sei denn ich schreibe die Methode als allgemeinen Konstruktor. Dann müsste ich aber für jede Methode eine eigene Klasse schreiben; was noch viel mehr Arbeit ist und alles noch unübersichtlicher macht, weil sonst müsste ich etwa 100 neue Klasse schreiben. Oder habe ich dabei etwas übersehen? Langsam bin ich echt genervt von Java... :;O


----------



## mihe7 (17. Apr 2019)

Im Prinzip ist das ganz einfach: Du schreibst Dir Repository-Klassen, die dazu dienen, dem Gros der Anwendung eine vom Speichermedium unabhängige Schnittstelle anzubieten, um auf Entities zuzugreifen.

Zum Beispiel kannst Du ein BranchRepository schreiben, das "alles" abdeckt, was unmittelbar mit Filialen zu tun hat. Das könnte etwa so aussehen:

```
class BranchRepository {
    public enum Location {
        DOMESTIC, FOREIGN
    }

    private static final EnumMap<Location, String> queries = new EnumMap<>(Location.class);
    static {
        queries.put(Location.DOMESTIC, "SELECT * FROM Filialen_Deutschland");
        queries.put(Location.FOREIGN, "SELECT * FROM Filialen_Ausland");
    }

    private Connection conn;

    public BranchRepository(Connection conn) {
        this.conn = conn;
    }

    public List<String> findNames(Location where) {
        String sql = queries.get(where);
        try(PreparedStatement pst = conn.prepareStatement(sql)){
            try(ResultSet rs = pst.executeQuery()){
                List<String> result = new ArrayList<>();
                while(rs.next()){
                    result.add(rs.getString("LABEL"));
                }
                return result;
            }
        }
    }

    public List<Branch> findAll() { ... }
    public List<Branch> findByLocation(Location where) { ... }
    // usw.
}
```

Branch wäre dabei natürlich eine Klasse, die eine Filiale darstellt. Die findNames-Methode entspricht der Methode loadGermanBranches aus Kommentar #31. Ich habe sie nur ein wenig abstrakter gestaltet. Natürlich kannst Du auch konkretere findGermanBranches und findForeignBranches oder ähnliche Methoden implementieren.

Der Punkt hierbei ist: diese Klasse hat genau eine Aufgabe (das "Zeug" aus der DB laden und ggf. in der DB speichern) und absolut nichts mit einem User-Interface zu tun.

Dem UI werden dann z. B. solche Repositories mitgegeben. Dann kann dieses einfach darauf zugreifen. Das würde dann in einer Methode wie fillComboBoxGermanBranches() aus Kommentar #31 entsprechen.


----------



## mrBrown (17. Apr 2019)

maGG hat gesagt.:


> Objektorientiert zu programmieren, um möglichst Copy&Paste zu vermeiden verstehe ich und finde ich sinnvoll.



BTW: Objektorientiert programmiert man nicht um Copy&Paste zu vermeiden  (Auch wenn gutes OO-Design oftmals deutlich weniger C&P zur Folge hat.)


----------



## maGG (17. Apr 2019)

mihe7 hat gesagt.:


> ganz einfach


 Also einfach finde ich das überhaupt nicht! Vielen Dank für die Mühe! Ich muss mir nochmal anschauen was ein enum und eine EnumMap. So versteh ich geradenur Bahnhof.

Wofür ich "private Connection conn;" bringt ist mir auch noch ein Rätsel.



mrBrown hat gesagt.:


> nicht um Copy&Paste zu vermeiden


achso ok, das hat unser Professor uns immer so erklärt ^^


----------



## mihe7 (17. Apr 2019)

maGG hat gesagt.:


> Also einfach finde ich das überhaupt nicht! Vielen Dank für die Mühe! Ich muss mir nochmal anschauen was ein enum und eine EnumMap. So versteh ich geradenur Bahnhof.


Ich speichere die zwei SELECTs einfach in einer Map, damit ich mir eine Methode in der öffentlichen Schnittstelle spare. Du kannst, wie bereits erwähnt, natürlich auch zwei Methoden schreiben.


```
public List<String> findGermanNames() { 
        return findNames("SELECT * FROM Filialen_Deutschland");
    }

    public List<String> findForeignlNames() {
        return findNames("SELECT * FROM Filialen_Ausland");
    }

    private List<String> findNames(String sql) {
        try(PreparedStatement pst = conn.prepareStatement(sql)){
            try(ResultSet rs = pst.executeQuery()){
                List<String> result = new ArrayList<>();
                while(rs.next()){
                    result.add(rs.getString("LABEL"));
                }
                return result;
            }
        }
    }
```



maGG hat gesagt.:


> Wofür ich "private Connection conn;" bringt ist mir auch noch ein Rätsel.


Beim Erzeugen des Repositories gibst Du die zu verwendende Verbindung zur DB an. Das Repository interessiert sich also prinzipiell nicht dafür, wo die Verbinung herkommt, welcher Treiber verwendet wird oder welches DBMS im Einsatz ist (hier gilt allerdings die Einschränkung, dass der SQL-Dialekt passen muss). Willst Du also DB tauschen, brauchst Du an diesem Code nichts mehr zu ändern.


----------



## maGG (17. Apr 2019)

Ok, cool! Vielen lieben Dank!

Verstehe ich das richtig, dass ich in meiner Hauptklasse dann sowas schreiben müsste?


```
private void fillComboBoxGermanBranches(){
    for(String label: findNames(BranchRepository.Location.DOMESTIC)){
        JComboBoxFilialenDeutschland.addItem(label);
    }
}
```

Da kommt bei mir gerade "cannot find Symbol" hmmm extends GUI habe habe ich schon bei der Repository Klasse im Kopf geschrieben. Oder was hab ich falsch gemacht?


----------



## mihe7 (17. Apr 2019)

maGG hat gesagt.:


> Verstehe ich das richtig, dass ich in meiner Hauptklasse dann sowas schreiben müsste?


Fast. Du musst in Deinem UI schon eine Instanz Deines Repositories verwenden (daher bekommst Du auch die Fehlermeldung)


```
private BranchRepository repos; // Achtung: das muss irgendwo noch initialisiert werden 

private void fillComboBoxGermanBranches(){
    // hier dann repos.findNames:
    for(String label: repos.findNames(BranchRepository.Location.DOMESTIC)){
        JComboBoxFilialenDeutschland.addItem(label);
    }
}
```

Kannst Du maven-Projekte bauen? Dann schustere ich Dir mal ein lauffähiges Programm zusammen.



maGG hat gesagt.:


> hmmm extends GUI habe habe ich schon bei der Repository Klasse im Kopf geschrieben


Nein, GUI taucht im Repository nicht auf


----------



## maGG (17. Apr 2019)

Nein, habe Maven noch nie benutzt. Hieße das ich müsste ein neues Projekt erstellen und dieses dann teilen? 

Erm mist hab ne NullPointerException haha 


```
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at signatur.GUI.fillComboBoxGermanBranches2(GUI.java:1155)
    at signatur.GUI.<init>(GUI.java:156)
    at signatur.GUI$95.run(GUI.java:5114)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.awt.EventQueue$3.run(EventQueue.java:703)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
```


----------



## mihe7 (17. Apr 2019)

maGG hat gesagt.:


> Nein, habe Maven noch nie benutzt. Hieße das ich müsste ein neues Projekt erstellen und dieses dann teilen?


Was heißt teilen? Du benutzt ja hoffentlich eine IDE, welche wäre das denn?



maGG hat gesagt.:


> Erm mist hab ne NullPointerException haha


Ich vermute mal ganz stark, dass Du die Variable repos nicht gesetzt hast und daher beim Zugriff darauf eine NPE geworfen wird (was steht denn in der genannten Zeile 1155?)


----------



## maGG (17. Apr 2019)

mihe7 hat gesagt.:


> Was heißt teilen? Du benutzt ja hoffentlich eine IDE, welche wäre das denn?


Ich benutze Netbeans  oder worauf wolltest du hinaus wegen Maven?

da steht:

```
//Zeile 1555
for(String label: repos.findNames(BranchRepository.Location.DOMESTIC)){
```
und repos habe ich ziemlich weit oben so deklariert:

```
private BranchRepository repos;
```
oder meinst du was anderes mit "gesetzt"?


----------



## mihe7 (17. Apr 2019)

maGG hat gesagt.:


> oder meinst du was anderes mit "gesetzt"?


Ja, Du hast die Variable nur deklariert aber nicht initialisiert (standardmäßig werden Referenzen mit null initialisiert).

Du musst auch ein BranchRepository-Objekt erzeugen. 

Wo bekommst Du denn Deine Connection her? Wird die bei Dir in der GUI-Klasse aufgebaut und bekommt die GUI-Klasse die Connection übergeben?



maGG hat gesagt.:


> Ich benutze Netbeans  oder worauf wolltest du hinaus wegen Maven?


NetBeans ist OK. Damit (und eigentlich sollte das mit jeder IDE funktionieren) kannst Du Maven-Projekte öffnen. Mal schauen, vielleicht komme ich noch dazu, eine kleine Demo zu basteln. Sollte ja kein großer Akt sein.


----------



## maGG (17. Apr 2019)

mihe7 hat gesagt.:


> Wo bekommst Du denn Deine Connection her? Wird die bei Dir in der GUI-Klasse aufgebaut und bekommt die GUI-Klasse die Connection übergeben?


Achso, stimmt das könnte echt der Grund sein. Im Moment so:


```
//im Konstruktor
public GUI(){
    //...
    ConnectDB();
    //...
}

Connection conn = null;

public Connection ConnectDb() {
    try {
        Class.forName("org.sqlite.JDBC");
        //die Datenbank soll später übers Internet zugänglich sein
        conn = DriverManager.getConnection("jdbc:sqlite:" + System.getProperty("user.home") + "/Desktop/Adressen.db");
        return conn;
    } catch (Exception e) {
        JOptionPane.showMessageDialog(null, e);
        return null;
    }
}
```


----------



## mihe7 (18. Apr 2019)

Ändere mal Deinen Konstruktor

```
public GUI() {
  // ...
  ConnectDB();
  repos = new BranchRepository(conn);
}
```


----------



## maGG (18. Apr 2019)

Dank dir! Leider habe ich jetzt ein anderen Error:



```
Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.util.HashMap.newNode(HashMap.java:1742)
    at java.util.HashMap.putVal(HashMap.java:630)
    at java.util.HashMap.put(HashMap.java:611)
    at java.beans.ChangeListenerMap.add(ChangeListenerMap.java:91)
    at java.beans.PropertyChangeSupport.addPropertyChangeListener(PropertyChangeSupport.java:117)
    at java.awt.Component.addPropertyChangeListener(Component.java:8262)
    at java.awt.Container.addPropertyChangeListener(Container.java:3599)
    at javax.swing.plaf.basic.BasicLabelUI.installListeners(BasicLabelUI.java:344)
    at javax.swing.plaf.basic.BasicLabelUI.installUI(BasicLabelUI.java:326)
    at javax.swing.JComponent.setUI(JComponent.java:666)
    at javax.swing.JLabel.setUI(JLabel.java:261)
    at javax.swing.JLabel.updateUI(JLabel.java:275)
    at javax.swing.JLabel.<init>(JLabel.java:164)
    at javax.swing.JLabel.<init>(JLabel.java:235)
    at javax.swing.DefaultListCellRenderer.<init>(DefaultListCellRenderer.java:92)
    at javax.swing.DefaultListCellRenderer$UIResource.<init>(DefaultListCellRenderer.java:344)
    at javax.swing.plaf.basic.BasicLookAndFeel$2.createValue(BasicLookAndFeel.java:611)
    at javax.swing.UIDefaults.getFromHashtable(UIDefaults.java:231)
    at javax.swing.UIDefaults.get(UIDefaults.java:161)
    at javax.swing.MultiUIDefaults.get(MultiUIDefaults.java:64)
    at javax.swing.UIManager.get(UIManager.java:955)
    at javax.swing.plaf.basic.BasicListUI.installDefaults(BasicListUI.java:788)
    at javax.swing.plaf.basic.BasicListUI.installUI(BasicListUI.java:881)
    at javax.swing.JComponent.setUI(JComponent.java:666)
    at javax.swing.JList.setUI(JList.java:523)
    at javax.swing.JList.updateUI(JList.java:538)
    at javax.swing.JList.<init>(JList.java:433)
    at javax.swing.plaf.basic.BasicComboPopup$1.<init>(BasicComboPopup.java:487)
    at javax.swing.plaf.basic.BasicComboPopup.createList(BasicComboPopup.java:487)
    at javax.swing.plaf.basic.BasicComboPopup.<init>(BasicComboPopup.java:332)
    at javax.swing.plaf.basic.BasicComboBoxUI.createPopup(BasicComboBoxUI.java:442)
    at com.sun.java.swing.plaf.windows.WindowsComboBoxUI.createPopup(WindowsComboBoxUI.java:371)
```


----------



## mihe7 (18. Apr 2019)

D. h. dass der Garbage Collector unverhältnismäßig viel Zeit benötigt um unverhältnismäßig wenig Speicher freizugeben. D. h. in der Regel, dass Du irgendwo massenhaft temporäre Objekte erzeugst oder z. B. die JVM zu wenig Heap zugewiesen bekommen hat.


----------



## maGG (18. Apr 2019)

mihe7 hat gesagt.:


> D. h. dass der Garbage Collector unverhältnismäßig viel Zeit benötigt um unverhältnismäßig wenig Speicher freizugeben. D. h. in der Regel, dass Du irgendwo massenhaft temporäre Objekte erzeugst oder z. B. die JVM zu wenig Heap zugewiesen bekommen hat.


Danke. Ok, gibts da eine Möglichkeit das zu checken, also ob soviele temporäre Objekte erzeugt werden. Wenn ich die Füllmethode über die Repository Klasse tätige, dann komm der Fehler. Wenn ich sie auskommentiere und mein alten Code nehme schafft es das Programm. Ist das nicht widersprüchlich? Die Anzahl an temporären Objekten müsste doch verhältnismäßig konstant bleiben, oder?


----------



## mihe7 (18. Apr 2019)

Da scheint irgendwo ein Fehler zu sein, die Meldung ist keineswegs "normal". Kannst Du den Code online stellen?


----------



## maGG (18. Apr 2019)

mihe7 hat gesagt.:


> Da scheint irgendwo ein Fehler zu sein, die Meldung ist keineswegs "normal". Kannst Du den Code online stellen?


Ja, habe eine PDF Datei in Google Drive hochgeladen, deren Link ich dir schicken kann, leider kann ich dein Profil nicht einsehen, kannst du mich hinzufügen?


----------



## maGG (19. Apr 2019)

Hab meine Methoden zum Editieren der Datenbank, in Anlehnung an deinen Vorschlag, jetzt so ausgelagert:


```
public class DatabaseRepository {
   
    public enum View{
        DOMESTIC, FOREIGN, COMPANY, TITEL, ROLE
    }
   
    public enum Update{
        DOMESTIC, FOREIGN, COMPANY , TITEL, ROLE
    }
   
    public enum Insert{
        DOMESTIC, FOREIGN, COMPANY , TITEL, ROLE
    }
   
    public enum Delete{
        DOMESTIC, FOREIGN, COMPANY , TITEL, ROLE
    }
   
    private static final EnumMap<View, String> queryView = new EnumMap<>(View.class);
   
    private static final EnumMap<Update, String> queryUpdate = new EnumMap<>(Update.class);
   
    private static final EnumMap<Insert, String> queryInsert = new EnumMap<>(Insert.class);
   
    private static final EnumMap<Delete, String> queryDelete = new EnumMap<>(Delete.class);
   
    static{
        queryView.put(View.DOMESTIC, "SELECT * FROM Filialen_Deutschland");
        queryView.put(View.FOREIGN, "SELECT * FROM Filialen_Ausland");
        queryView.put(View.COMPANY, "SELECT * FROM Filialen_Ausland");
        queryView.put(View.TITEL, "SELECT * FROM Filialen_Ausland");
        queryView.put(View.ROLE, "SELECT * FROM Filialen_Ausland");
       
        queryUpdate.put(Update.DOMESTIC, "UPDATE Filialen_Deutschland SET id=?, LABEL=?, STRASZE=?, PLZ_ORT=?, ORT=?,TEL_LAND=?, " +
                "TEL_ANFANG_INT=?, TEL_ANFANG_TXT=?, TEL_ENDE_DEFAULT=?, FAX_ANFANG_TXT=?, FAX_ENDE_TXT=? WHERE id=?");      
        queryUpdate.put(Update.FOREIGN, "UPDATE Filialen_Ausland SET id=?, LABEL=?, STRASZE=?, PLZ_ORT=?, ORT=?,TEL_LAND=?, " +
                "TEL_ANFANG_INT=?, TEL_ANFANG_TXT=?, TEL_ENDE_DEFAULT=?, FAX_ANFANG_TXT=?, FAX_ENDE_TXT=? WHERE id=?");
        queryUpdate.put(Update.COMPANY, "UPDATE Firmierungen SET id=?, LABEL=? WHERE id=?");
        queryUpdate.put(Update.TITEL, "UPDATE Titel SET id=?, LABEL=?, VORNE=? WHERE id=?");      
        queryUpdate.put(Update.ROLE, "UPDATE Funktionen SET id=?, LABEL=? WHERE id=?");
       
        queryInsert.put(Insert.DOMESTIC, "INSERT INTO Filialen_Deutschland (id,LABEL,STRASZE,PLZ_ORT,ORT,TEL_LAND,TEL_ANFANG_INT," +
                "TEL_ANFANG_TXT,TEL_ENDE_DEFAULT,FAX_ANFANG_TXT,FAX_ENDE_TXT) VALUES(?,?,?,?,?,?,?,?,?,?,?)");
        queryInsert.put(Insert.FOREIGN, "INSERT INTO Filialen_Ausland (id,LABEL,STRASZE,PLZ_ORT,ORT,TEL_LAND,TEL_ANFANG_INT," +
                "TEL_ANFANG_TXT,TEL_ENDE_DEFAULT,FAX_ANFANG_TXT,FAX_ENDE_TXT) VALUES(?,?,?,?,?,?,?,?,?,?,?)");
        queryInsert.put(Insert.COMPANY, "INSERT INTO Firmierungen (id,LABEL) VALUES(?,?)");
        queryInsert.put(Insert.TITEL, "INSERT INTO Titel (id,LABEL,VORNE) VALUES(?,?,?)");
        queryInsert.put(Insert.ROLE, "INSERT INTO Funktionen (id,LABEL) VALUES(?,?)");
       
        queryDelete.put(Delete.DOMESTIC, "DELETE FROM Filialen_Deutschland WHERE id=?");
        queryDelete.put(Delete.FOREIGN, "DELETE FROM Filialen_Ausland WHERE id=?");
        queryDelete.put(Delete.COMPANY, "DELETE FROM Firmierungen WHERE id=?");
        queryDelete.put(Delete.TITEL, "DELETE FROM Titel WHERE id=?");
        queryDelete.put(Delete.ROLE, "DELETE FROM Funktionen WHERE id=?");
    }
   
    private Connection conn;
   
    public DatabaseRepository (Connection conn){
        this.conn = conn;      
    }    

    public void viewTable(View view, JTable table) throws SQLException{
        String sql = queryView.get(view);
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
            try (ResultSet rs = pst.executeQuery()) {
                table.setModel(DbUtils.resultSetToTableModel(rs));
            }
        }  
    }  
   
    public void updateTable(Update update, JTextField textField, String f2, String f3, String f4, String f5, int f6, long f7, String f8,
            String f9, String f10, String f11) throws SQLException{
        String sql = queryUpdate.get(update);
        int id = Integer.parseInt(textField.getText());
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
                pst.setInt(1, id); // id
                pst.setString(2, f2); //LABEL
                pst.setString(3, f3); //STRASZE
                pst.setString(4, f4); //PLZ_ORT
                pst.setString(5, f5); //ORT
                pst.setInt(6, f6); //TEL_LAND
                pst.setLong(7, f7); //TEL_ANFANG_INT
                pst.setString(8, f8); //TEL_ANFANG_TXT
                if (f9.equals("")) {
                    pst.setObject(9, null);
                } else {
                    pst.setInt(9, Integer.parseInt(f9)); //TEL_ENDE_DEFAULT
                }
                pst.setString(10, f10); //FAX_ANFANG_TXT
                if (f11.equals("")) {
                    pst.setObject(11, null);
                } else {
                    pst.setString(11, f11); //FAX_ENDE_TXT
                }
                pst.setInt(12, id); //id (doppelt)
                pst.executeUpdate();
        }  
    }  
   
    public void updateTable(Update update, JTextField textField, String label) throws SQLException{
        String sql = queryUpdate.get(update);
        int id = Integer.parseInt(textField.getText());
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
            pst.setInt(1, id);
            pst.setString(2, label);
            pst.setInt(3, id);
            pst.executeUpdate();
        }  
    }  
   
    public void updateTableTitels(Update update, JTextField textField, String label, boolean infront) throws SQLException{
        String sql = queryUpdate.get(update);
        int id = Integer.parseInt(textField.getText());
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
            pst.setInt(1, id);
            pst.setString(2, label);
            pst.setBoolean(3, infront);
            pst.setInt(4, id);
            pst.executeUpdate();
        }  
    }  
   
    public void insertTable(Insert insert, JTextField textField, String f2, String f3, String f4, String f5, int f6, long f7, String f8,
            String f9, String f10, String f11) throws SQLException{
        String sql = queryInsert.get(insert);
        int id = Integer.parseInt(textField.getText());
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
            pst.setInt(1, id); //id
            pst.setString(2, f2); //LABEL
            pst.setString(3, f3); //STRASZE
            pst.setString(4, f4); //PLZ_ORT
            pst.setString(5, f5); //ORT
            pst.setInt(6, f6); //TEL_LAND
            pst.setLong(7, f7); //TEL_ANFANG_INT
            pst.setString(8, f8); //TEL_ANFANG_TXT
            if (f9.equals("")) {
                pst.setObject(9, null);
            } else {
                pst.setInt(9, Integer.parseInt(f9)); //TEL_ENDE_DEFAULT
            }
            pst.setString(10, f10); //FAX_ANFANG_TXT
            if (f11.equals("")) {
                pst.setObject(11, null);
            } else {
                pst.setString(11, f11); //FAX_ENDE_TXT
            }
            pst.execute();
        }  
    }  
   
    public void insertTable(Insert insert, JTextField textField, String label) throws SQLException{
        String sql = queryInsert.get(insert);
        int id = Integer.parseInt(textField.getText());
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
            pst.setInt(1, id);
            pst.setString(2, label);
            pst.execute();
        }  
    }
   
    public void insertTable(Insert insert, JTextField textField, String label, boolean infront) throws SQLException{
        String sql = queryInsert.get(insert);
        int id = Integer.parseInt(textField.getText());
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
            pst.setInt(1, id);
            pst.setString(2, label);
            pst.setBoolean(3, infront);
            pst.execute();
        }  
    }  
   
    public void deleteTable(Delete delete, JTable table) throws SQLException{
        String sql = queryDelete.get(delete);
        int row = table.getSelectedRow();
        String tableClick = (table.getModel().getValueAt(row, 0).toString());        
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
            pst.setString(1, tableClick);
            pst.execute();
        }
    }  
   
}
```
Cool oder? kann man das so machen?


----------



## mihe7 (19. Apr 2019)

maGG hat gesagt.:


> Cool oder? kann man das so machen?


Können tut man schon aber das erstens macht das Ding zu viel, zweitens orientiert es sich nicht an Business Entities, drittens geht es schöner und viertens haben UI-Klassen (wie JTable) dort nichts verloren. 

Zum Thema UI-Klassen: stell Dir vor, nächsten Monat fällt die Entscheidung: oh, Swing ist Mist, wir machen jetzt JavaFX. Dann musst Du hier überall rumändern. Die Klasse muss so gestrickt sein, dass sie mit jedem x-beliebigen Client funktioniert.

Du versuchst, zu viel in eine Klasse zu stopfen. Vielleicht mal zum Unterschied: das BranchRepository verwaltet Filialen (Branch) und nur Filialen - keine Firmierungen, keine Companies, keine Funktionen. Das Location-enum gibt es dort nur, weil zwischen inländischen und ausländischen Filialen unterschieden wird. Somit dient die Location einfach als Query-Filter. Tatsächlich wäre die Location Teil des Domain Models und das enum müsste außerhalb der BranchRepository-Klasse definiert werden. Die EnumMap gibt es nur, weil zwei verschiedene Tabellen betroffen sind.

Es ist auf jeden Fall mal ein Schritt in die richtige Richtung und alles ist besser als sämtlichen Code in eine Klasse zu packen.


----------



## maGG (19. Apr 2019)

Hm ok, welcheBusiness Entities meinst du? Noch schöner? Hmm ich find das schon recht schön hehe, aber ja vielleicht ändert ich es noch.

Ich habe übrigens mehr Info zu den Fehlern:


```
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (malloc) failed to allocate 519536 bytes for Chunk::new
# Possible reasons:
#   The system is out of physical RAM or swap space
#   In 32 bit mode, the process size limit was hit
# Possible solutions:
#   Reduce memory load on the system
#   Increase physical memory or swap space
#   Check if swap backing store is full
#   Use 64 bit Java on a 64 bit OS
#   Decrease Java heap size (-Xmx/-Xms)
#   Decrease number of Java threads
#   Decrease Java thread stack sizes (-Xss)
#   Set larger code cache with -XX:ReservedCodeCacheSize=
# This output file may be truncated or incomplete.
#
#  Out of Memory Error (allocation.cpp:390), pid=8952, tid=0x000000000000227c
#
# JRE version: Java(TM) SE Runtime Environment (8.0_111-b14) (build 1.8.0_111-b14)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.111-b14 mixed mode windows-amd64 compressed oops)
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#

---------------  T H R E A D  ---------------

Current thread (0x0000000014949000):  JavaThread "C2 CompilerThread0" daemon [_thread_in_native, id=8828, stack(0x0000000015990000,0x0000000015a90000)]

Stack: [0x0000000015990000,0x0000000015a90000]
[error occurred during error reporting (printing stack bounds), id 0xc0000005]

Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)


Current CompileTask:
C2:  35042 3779       4       javax.swing.plaf.basic.BasicTextUI::installDefaults (463 bytes)


---------------  P R O C E S S  ---------------

Java Threads: ( => current thread )
  0x00000000166b8000 JavaThread "TimerQueue" daemon [_thread_blocked, id=5708, stack(0x0000000017d90000,0x0000000017e90000)]
  0x0000000002480800 JavaThread "DestroyJavaVM" [_thread_blocked, id=7644, stack(0x00000000022e0000,0x00000000023e0000)]
  0x0000000016613800 JavaThread "AWT-EventQueue-0" [_thread_in_Java, id=7428, stack(0x0000000017590000,0x0000000017690000)]
  0x0000000016594000 JavaThread "AWT-Windows" daemon [_thread_in_native, id=12684, stack(0x0000000016f90000,0x0000000017090000)]
  0x0000000016591000 JavaThread "AWT-Shutdown" [_thread_blocked, id=12012, stack(0x0000000016e90000,0x0000000016f90000)]
  0x0000000016590800 JavaThread "Java2D Disposer" daemon [_thread_blocked, id=13156, stack(0x0000000016d90000,0x0000000016e90000)]
  0x00000000149b1000 JavaThread "Service Thread" daemon [_thread_blocked, id=1980, stack(0x0000000015b90000,0x0000000015c90000)]
  0x00000000149a0800 JavaThread "C1 CompilerThread1" daemon [_thread_blocked, id=4068, stack(0x0000000015a90000,0x0000000015b90000)]
=>0x0000000014949000 JavaThread "C2 CompilerThread0" daemon [_thread_in_native, id=8828, stack(0x0000000015990000,0x0000000015a90000)]
  0x0000000014948000 JavaThread "Attach Listener" daemon [_thread_blocked, id=10088, stack(0x0000000015890000,0x0000000015990000)]
  0x0000000014944800 JavaThread "Signal Dispatcher" daemon [_thread_blocked, id=11688, stack(0x0000000015790000,0x0000000015890000)]
  0x0000000002576800 JavaThread "Finalizer" daemon [_thread_blocked, id=968, stack(0x0000000015580000,0x0000000015680000)]
  0x000000000256d000 JavaThread "Reference Handler" daemon [_thread_blocked, id=12420, stack(0x0000000015480000,0x0000000015580000)]

Other Threads:
  0x0000000014916800 VMThread [stack: 0x0000000015380000,0x0000000015480000] [id=11228]
  0x00000000149aa800 WatcherThread [stack: 0x0000000016090000,0x0000000016190000] [id=12716]

VM state:not at safepoint (normal execution)

VM Mutex/Monitor currently owned by a thread: None

Heap:
 PSYoungGen      total 76288K, used 27444K [0x00000000eb180000, 0x00000000f0e80000, 0x0000000100000000)
  eden space 63488K, 43% used [0x00000000eb180000,0x00000000ecc4d330,0x00000000eef80000)
  from space 12800K, 0% used [0x00000000efe00000,0x00000000efe00000,0x00000000f0a80000)
  to   space 14848K, 0% used [0x00000000eef80000,0x00000000eef80000,0x00000000efe00000)
 ParOldGen       total 88064K, used 78920K [0x00000000c1400000, 0x00000000c6a00000, 0x00000000eb180000)
  object space 88064K, 89% used [0x00000000c1400000,0x00000000c6112018,0x00000000c6a00000)
 Metaspace       used 17077K, capacity 17410K, committed 17792K, reserved 1064960K
  class space    used 1989K, capacity 2087K, committed 2176K, reserved 1048576K

Card table byte_map: [0x0000000011940000,0x0000000011b40000] byte_map_base: 0x0000000011336000

Marking Bits: (ParMarkBitMap*) 0x0000000059faa6c0
 Begin Bits: [0x0000000011e90000, 0x0000000012e40000)
 End Bits:   [0x0000000012e40000, 0x0000000013df0000)

Polling page: 0x0000000000890000

CodeCache: size=245760Kb used=12565Kb max_used=12578Kb free=233194Kb
 bounds [0x0000000002580000, 0x00000000031e0000, 0x0000000011580000]
 total_blobs=3999 nmethods=3474 adapters=436
 compilation: enabled

Compilation events (10 events):
Event: 34.652 Thread 0x0000000014949000 nmethod 3774 0x00000000031b9010 code [0x00000000031b9160, 0x00000000031b9378]
Event: 34.652 Thread 0x0000000014949000 3775  s    4       java.awt.MediaTracker::statusID (48 bytes)
Event: 34.753 Thread 0x0000000014949000 nmethod 3775 0x00000000031d4050 code [0x00000000031d42c0, 0x00000000031d7430]
Event: 34.754 Thread 0x0000000014949000 3776  s    4       java.awt.ImageMediaEntry::getStatus (80 bytes)
Event: 34.784 Thread 0x0000000014949000 nmethod 3776 0x00000000031c9dd0 code [0x00000000031c9fc0, 0x00000000031cb3d8]
Event: 34.784 Thread 0x0000000014949000 3777       4       javax.swing.plaf.basic.BasicHTML::getBaseline (43 bytes)
Event: 34.790 Thread 0x0000000014949000 nmethod 3777 0x00000000031ba390 code [0x00000000031ba4e0, 0x00000000031ba868]
Event: 34.790 Thread 0x0000000014949000 3778       4       java.awt.Rectangle::intersection (180 bytes)
Event: 34.794 Thread 0x0000000014949000 nmethod 3778 0x00000000031b7b90 code [0x00000000031b7ce0, 0x00000000031b7ef8]
Event: 34.816 Thread 0x0000000014949000 3779       4       javax.swing.plaf.basic.BasicTextUI::installDefaults (463 bytes)

GC Heap History (10 events):
Event: 19.542 GC heap before
{Heap before GC invocations=6 (full 0):
 PSYoungGen      total 35328K, used 35328K [0x00000000eb180000, 0x00000000f0a80000, 0x0000000100000000)
  eden space 32768K, 100% used [0x00000000eb180000,0x00000000ed180000,0x00000000ed180000)
  from space 2560K, 100% used [0x00000000ed180000,0x00000000ed400000,0x00000000ed400000)
  to   space 12800K, 0% used [0x00000000efe00000,0x00000000efe00000,0x00000000f0a80000)
 ParOldGen       total 44032K, used 26295K [0x00000000c1400000, 0x00000000c3f00000, 0x00000000eb180000)
  object space 44032K, 59% used [0x00000000c1400000,0x00000000c2dadc90,0x00000000c3f00000)
 Metaspace       used 16766K, capacity 17090K, committed 17536K, reserved 1064960K
  class space    used 1989K, capacity 2087K, committed 2176K, reserved 1048576K
Event: 19.756 GC heap after
Heap after GC invocations=6 (full 0):
 PSYoungGen      total 76288K, used 12800K [0x00000000eb180000, 0x00000000f0e80000, 0x0000000100000000)
  eden space 63488K, 0% used [0x00000000eb180000,0x00000000eb180000,0x00000000eef80000)
  from space 12800K, 100% used [0x00000000efe00000,0x00000000f0a80000,0x00000000f0a80000)
  to   space 14848K, 0% used [0x00000000eef80000,0x00000000eef80000,0x00000000efe00000)
 ParOldGen       total 44032K, used 26303K [0x00000000c1400000, 0x00000000c3f00000, 0x00000000eb180000)
  object space 44032K, 59% used [0x00000000c1400000,0x00000000c2dafc90,0x00000000c3f00000)
 Metaspace       used 16766K, capacity 17090K, committed 17536K, reserved 1064960K
  class space    used 1989K, capacity 2087K, committed 2176K, reserved 1048576K
}
Event: 25.067 GC heap before
{Heap before GC invocations=7 (full 0):
 PSYoungGen      total 76288K, used 76288K [0x00000000eb180000, 0x00000000f0e80000, 0x0000000100000000)
  eden space 63488K, 100% used [0x00000000eb180000,0x00000000eef80000,0x00000000eef80000)
  from space 12800K, 100% used [0x00000000efe00000,0x00000000f0a80000,0x00000000f0a80000)
  to   space 14848K, 0% used [0x00000000eef80000,0x00000000eef80000,0x00000000efe00000)
 ParOldGen       total 44032K, used 26303K [0x00000000c1400000, 0x00000000c3f00000, 0x00000000eb180000)
  object space 44032K, 59% used [0x00000000c1400000,0x00000000c2dafc90,0x00000000c3f00000)
 Metaspace       used 16910K, capacity 17218K, committed 17536K, reserved 1064960K
  class space    used 1989K, capacity 2087K, committed 2176K, reserved 1048576K
Event: 25.357 GC heap after
Heap after GC invocations=7 (full 0):
 PSYoungGen      total 78336K, used 14848K [0x00000000eb180000, 0x00000000f0e80000, 0x0000000100000000)
  eden space 63488K, 0% used [0x00000000eb180000,0x00000000eb180000,0x00000000eef80000)
  from space 14848K, 100% used [0x00000000eef80000,0x00000000efe00000,0x00000000efe00000)
  to   space 12800K, 0% used [0x00000000efe00000,0x00000000efe00000,0x00000000f0a80000)
 ParOldGen       total 45056K, used 44787K [0x00000000c1400000, 0x00000000c4000000, 0x00000000eb180000)
  object space 45056K, 99% used [0x00000000c1400000,0x00000000c3fbce70,0x00000000c4000000)
 Metaspace       used 16910K, capacity 17218K, committed 17536K, reserved 1064960K
  class space    used 1989K, capacity 2087K, committed 2176K, reserved 1048576K
}
Event: 25.357 GC heap before
{Heap before GC invocations=8 (full 1):
 PSYoungGen      total 78336K, used 14848K [0x00000000eb180000, 0x00000000f0e80000, 0x0000000100000000)
  eden space 63488K, 0% used [0x00000000eb180000,0x00000000eb180000,0x00000000eef80000)
  from space 14848K, 100% used [0x00000000eef80000,0x00000000efe00000,0x00000000efe00000)
  to   space 12800K, 0% used [0x00000000efe00000,0x00000000efe00000,0x00000000f0a80000)
 ParOldGen       total 45056K, used 44787K [0x00000000c1400000, 0x00000000c4000000, 0x00000000eb180000)
  object space 45056K, 99% used [0x00000000c1400000,0x00000000c3fbce70,0x00000000c4000000)
 Metaspace       used 16910K, capacity 17218K, committed 17536K, reserved 1064960K
  class space    used 1989K, capacity 2087K, committed 2176K, reserved 1048576K
Event: 27.327 GC heap after
Heap after GC invocations=8 (full 1):
 PSYoungGen      total 78336K, used 14293K [0x00000000eb180000, 0x00000000f0e80000, 0x0000000100000000)
  eden space 63488K, 0% used [0x00000000eb180000,0x00000000eb180000,0x00000000eef80000)
  from space 14848K, 96% used [0x00000000eef80000,0x00000000efd75590,0x00000000efe00000)
  to   space 12800K, 0% used [0x00000000efe00000,0x00000000efe00000,0x00000000f0a80000)
 ParOldGen       total 88064K, used 44642K [0x00000000c1400000, 0x00000000c6a00000, 0x00000000eb180000)
  object space 88064K, 50% used [0x00000000c1400000,0x00000000c3f98bf0,0x00000000c6a00000)
 Metaspace       used 16910K, capacity 17218K, committed 17536K, reserved 1064960K
  class space    used 1989K, capacity 2087K, committed 2176K, reserved 1048576K
}
Event: 31.783 GC heap before
{Heap before GC invocations=9 (full 1):
 PSYoungGen      total 78336K, used 77781K [0x00000000eb180000, 0x00000000f0e80000, 0x0000000100000000)
  eden space 63488K, 100% used [0x00000000eb180000,0x00000000eef80000,0x00000000eef80000)
  from space 14848K, 96% used [0x00000000eef80000,0x00000000efd75590,0x00000000efe00000)
  to   space 12800K, 0% used [0x00000000efe00000,0x00000000efe00000,0x00000000f0a80000)
 ParOldGen       total 88064K, used 44642K [0x00000000c1400000, 0x00000000c6a00000, 0x00000000eb180000)
  object space 88064K, 50% used [0x00000000c1400000,0x00000000c3f98bf0,0x00000000c6a00000)
 Metaspace       used 16990K, capacity 17346K, committed 17792K, reserved 1064960K
  class space    used 1989K, capacity 2087K, committed 2176K, reserved 1048576K
Event: 31.956 GC heap after
Heap after GC invocations=9 (full 1):
 PSYoungGen      total 76288K, used 12800K [0x00000000eb180000, 0x00000000f0e80000, 0x0000000100000000)
  eden space 63488K, 0% used [0x00000000eb180000,0x00000000eb180000,0x00000000eef80000)
  from space 12800K, 100% used [0x00000000efe00000,0x00000000f0a80000,0x00000000f0a80000)
  to   space 14848K, 0% used [0x00000000eef80000,0x00000000eef80000,0x00000000efe00000)
 ParOldGen       total 88064K, used 66819K [0x00000000c1400000, 0x00000000c6a00000, 0x00000000eb180000)
  object space 88064K, 75% used [0x00000000c1400000,0x00000000c5540e40,0x00000000c6a00000)
 Metaspace       used 16990K, capacity 17346K, committed 17792K, reserved 1064960K
  class space    used 1989K, capacity 2087K, committed 2176K, reserved 1048576K
}
Event: 31.956 GC heap before
{Heap before GC invocations=10 (full 2):
 PSYoungGen      total 76288K, used 12800K [0x00000000eb180000, 0x00000000f0e80000, 0x0000000100000000)
  eden space 63488K, 0% used [0x00000000eb180000,0x00000000eb180000,0x00000000eef80000)
  from space 12800K, 100% used [0x00000000efe00000,0x00000000f0a80000,0x00000000f0a80000)
  to   space 14848K, 0% used [0x00000000eef80000,0x00000000eef80000,0x00000000efe00000)
 ParOldGen       total 88064K, used 66819K [0x00000000c1400000, 0x00000000c6a00000, 0x00000000eb180000)
  object space 88064K, 75% used [0x00000000c1400000,0x00000000c5540e40,0x00000000c6a00000)
 Metaspace       used 16990K, capacity 17346K, committed 17792K, reserved 1064960K
  class space    used 1989K, capacity 2087K, committed 2176K, reserved 1048576K
Event: 32.955 GC heap after
Heap after GC invocations=10 (full 2):
 PSYoungGen      total 76288K, used 0K [0x00000000eb180000, 0x00000000f0e80000, 0x0000000100000000)
  eden space 63488K, 0% used [0x00000000eb180000,0x00000000eb180000,0x00000000eef80000)
  from space 12800K, 0% used [0x00000000efe00000,0x00000000efe00000,0x00000000f0a80000)
  to   space 14848K, 0% used [0x00000000eef80000,0x00000000eef80000,0x00000000efe00000)
 ParOldGen       total 88064K, used 78920K [0x00000000c1400000, 0x00000000c6a00000, 0x00000000eb180000)
  object space 88064K, 89% used [0x00000000c1400000,0x00000000c6112018,0x00000000c6a00000)
 Metaspace       used 16990K, capacity 17346K, committed 17792K, reserved 1064960K
  class space    used 1989K, capacity 2087K, committed 2176K, reserved 1048576K
}

Deoptimization events (10 events):
Event: 4.282 Thread 0x0000000016613800 Uncommon trap: reason=class_check action=maybe_recompile pc=0x00000000028490a4 method=java.util.HashMap.putVal(ILjava/lang/Object;Ljava/lang/Object;ZZ)Ljava/lang/Object; @ 253
Event: 4.282 Thread 0x0000000016613800 Uncommon trap: reason=class_check action=maybe_recompile pc=0x00000000028490a4 method=java.util.HashMap.putVal(ILjava/lang/Object;Ljava/lang/Object;ZZ)Ljava/lang/Object; @ 253
Event: 4.282 Thread 0x0000000016613800 Uncommon trap: reason=class_check action=maybe_recompile pc=0x0000000002812f44 method=java.util.HashMap.putVal(ILjava/lang/Object;Ljava/lang/Object;ZZ)Ljava/lang/Object; @ 253
Event: 4.634 Thread 0x0000000016613800 Uncommon trap: reason=unstable_if action=reinterpret pc=0x000000000286eb1c method=sun.nio.cs.UTF_8$Encoder.encode([CII[B)I @ 33
Event: 5.211 Thread 0x0000000016613800 Uncommon trap: reason=unstable_if action=reinterpret pc=0x0000000002955904 method=org.sqlite.SQLiteJDBCLoader.contentsEquals(Ljava/io/InputStream;Ljava/io/InputStream;)Z @ 39
Event: 5.679 Thread 0x0000000016613800 Uncommon trap: reason=unstable_if action=reinterpret pc=0x000000000282ece4 method=java.lang.ref.SoftReference.get()Ljava/lang/Object; @ 17
Event: 28.776 Thread 0x0000000016613800 Uncommon trap: reason=array_check action=maybe_recompile pc=0x000000000278b088 method=java.awt.AWTEventMulticaster.populateListenerArray([Ljava/util/EventListener;Ljava/util/EventListener;I)I @ 51
Event: 28.776 Thread 0x0000000016613800 Uncommon trap: reason=array_check action=maybe_recompile pc=0x000000000278b088 method=java.awt.AWTEventMulticaster.populateListenerArray([Ljava/util/EventListener;Ljava/util/EventListener;I)I @ 51
Event: 28.776 Thread 0x0000000016613800 Uncommon trap: reason=array_check action=maybe_recompile pc=0x000000000278b088 method=java.awt.AWTEventMulticaster.populateListenerArray([Ljava/util/EventListener;Ljava/util/EventListener;I)I @ 51
Event: 28.777 Thread 0x0000000016613800 Uncommon trap: reason=array_check action=maybe_recompile pc=0x000000000278b088 method=java.awt.AWTEventMulticaster.populateListenerArray([Ljava/util/EventListener;Ljava/util/EventListener;I)I @ 51

Internal exceptions (10 events):
Event: 0.566 Thread 0x0000000002480800 Exception <a 'java/security/PrivilegedActionException'> (0x00000000eb37c000) thrown at [C:\re\workspace\8-2-build-windows-amd64-cygwin\jdk8u111\7883\hotspot\src\share\vm\prims\jvm.cpp, line 1386]
Event: 0.566 Thread 0x0000000002480800 Exception <a 'java/security/PrivilegedActionException'> (0x00000000eb37c210) thrown at [C:\re\workspace\8-2-build-windows-amd64-cygwin\jdk8u111\7883\hotspot\src\share\vm\prims\jvm.cpp, line 1386]
Event: 0.570 Thread 0x0000000002480800 Exception <a 'java/security/PrivilegedActionException'> (0x00000000eb3800f8) thrown at [C:\re\workspace\8-2-build-windows-amd64-cygwin\jdk8u111\7883\hotspot\src\share\vm\prims\jvm.cpp, line 1386]
Event: 0.570 Thread 0x0000000002480800 Exception <a 'java/security/PrivilegedActionException'> (0x00000000eb380308) thrown at [C:\re\workspace\8-2-build-windows-amd64-cygwin\jdk8u111\7883\hotspot\src\share\vm\prims\jvm.cpp, line 1386]
Event: 0.585 Thread 0x0000000002480800 Exception <a 'java/io/FileNotFoundException'> (0x00000000eb382f48) thrown at [C:\re\workspace\8-2-build-windows-amd64-cygwin\jdk8u111\7883\hotspot\src\share\vm\prims\jni.cpp, line 709]
Event: 0.759 Thread 0x0000000002480800 Exception <a 'java/lang/NoSuchFieldError': method resolution failed> (0x00000000eb450730) thrown at [C:\re\workspace\8-2-build-windows-amd64-cygwin\jdk8u111\7883\hotspot\src\share\vm\prims\methodHandles.cpp, line 1146]
Event: 0.781 Thread 0x0000000002480800 Exception <a 'java/lang/NoSuchFieldError': method resolution failed> (0x00000000eb45da80) thrown at [C:\re\workspace\8-2-build-windows-amd64-cygwin\jdk8u111\7883\hotspot\src\share\vm\prims\methodHandles.cpp, line 1146]
Event: 1.083 Thread 0x0000000002480800 Exception <a 'java/io/FileNotFoundException'> (0x00000000eb591ee0) thrown at [C:\re\workspace\8-2-build-windows-amd64-cygwin\jdk8u111\7883\hotspot\src\share\vm\prims\jni.cpp, line 709]
Event: 3.302 Thread 0x0000000016613800 Exception <a 'java/io/FileNotFoundException'> (0x00000000ebc493c0) thrown at [C:\re\workspace\8-2-build-windows-amd64-cygwin\jdk8u111\7883\hotspot\src\share\vm\prims\jni.cpp, line 709]
Event: 3.398 Thread 0x0000000016613800 Exception <a 'java/io/FileNotFoundException'> (0x00000000ebc8fb78) thrown at [C:\re\workspace\8-2-build-windows-amd64-cygwin\jdk8u111\7883\hotspot\src\share\vm\prims\jni.cpp, line 709]

Events (10 events):
Event: 28.776 Thread 0x0000000016613800 Uncommon trap: trap_request=0xffffffd6 fr.pc=0x000000000278b088
Event: 28.776 Thread 0x0000000016613800 DEOPT PACKING pc=0x000000000278b088 sp=0x00000000176880c0
Event: 28.776 Thread 0x0000000016613800 DEOPT UNPACKING pc=0x00000000025c582a sp=0x0000000017688040 mode 2
Event: 28.777 Thread 0x0000000016613800 Uncommon trap: trap_request=0xffffffd6 fr.pc=0x000000000278b088
Event: 28.777 Thread 0x0000000016613800 DEOPT PACKING pc=0x000000000278b088 sp=0x0000000017687c50
Event: 28.777 Thread 0x0000000016613800 DEOPT UNPACKING pc=0x00000000025c582a sp=0x0000000017687bd0 mode 2
Event: 31.783 Executing VM operation: ParallelGCFailedAllocation
Event: 32.955 Executing VM operation: ParallelGCFailedAllocation done
Event: 32.956 Executing VM operation: RevokeBias
Event: 32.958 Executing VM operation: RevokeBias done


Dynamic libraries:
0x00007ff61a100000 - 0x00007ff61a137000     C:\Program Files\Java\jdk1.8.0_111\jre\bin\java.exe
0x00007ffdc68f0000 - 0x00007ffdc6ac2000     C:\WINDOWS\SYSTEM32\ntdll.dll
0x00007ffdc4510000 - 0x00007ffdc45bc000     C:\WINDOWS\System32\KERNEL32.DLL
0x00007ffdc31e0000 - 0x00007ffdc33fd000     C:\WINDOWS\System32\KERNELBASE.dll
0x00007ffdc46c0000 - 0x00007ffdc4762000     C:\WINDOWS\System32\ADVAPI32.dll
0x00007ffdc4770000 - 0x00007ffdc480e000     C:\WINDOWS\System32\msvcrt.dll
0x00007ffdc3f60000 - 0x00007ffdc3fb9000     C:\WINDOWS\System32\sechost.dll
0x00007ffdc4060000 - 0x00007ffdc4181000     C:\WINDOWS\System32\RPCRT4.dll
0x00007ffdc4880000 - 0x00007ffdc49e5000     C:\WINDOWS\System32\USER32.dll
0x00007ffdc2ff0000 - 0x00007ffdc300e000     C:\WINDOWS\System32\win32u.dll
0x00007ffdc4ab0000 - 0x00007ffdc4ae4000     C:\WINDOWS\System32\GDI32.dll
0x00007ffdc3570000 - 0x00007ffdc36f0000     C:\WINDOWS\System32\gdi32full.dll
0x00007ffdb61e0000 - 0x00007ffdb645a000     C:\WINDOWS\WinSxS\amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.14393.953_none_42151e83c686086b\COMCTL32.dll
0x00007ffdc4af0000 - 0x00007ffdc4db8000     C:\WINDOWS\System32\combase.dll
0x00007ffdc2ef0000 - 0x00007ffdc2fe5000     C:\WINDOWS\System32\ucrtbase.dll
0x00007ffdc34b0000 - 0x00007ffdc351a000     C:\WINDOWS\System32\bcryptPrimitives.dll
0x00007ffdc6480000 - 0x00007ffdc64ae000     C:\WINDOWS\System32\IMM32.DLL
0x000000005b140000 - 0x000000005b212000     C:\Program Files\Java\jdk1.8.0_111\jre\bin\msvcr100.dll
0x0000000059790000 - 0x000000005a02a000     C:\Program Files\Java\jdk1.8.0_111\jre\bin\server\jvm.dll
0x00007ffdc64b0000 - 0x00007ffdc64b8000     C:\WINDOWS\System32\PSAPI.DLL
0x00007ffdc00d0000 - 0x00007ffdc00d9000     C:\WINDOWS\SYSTEM32\WSOCK32.dll
0x00007ffdc4810000 - 0x00007ffdc487a000     C:\WINDOWS\System32\WS2_32.dll
0x00007ffdc11e0000 - 0x00007ffdc1203000     C:\WINDOWS\SYSTEM32\WINMM.dll
0x00007ffdb6560000 - 0x00007ffdb656a000     C:\WINDOWS\SYSTEM32\VERSION.dll
0x00007ffdc0f40000 - 0x00007ffdc0f6b000     C:\WINDOWS\SYSTEM32\WINMMBASE.dll
0x00007ffdc3520000 - 0x00007ffdc3562000     C:\WINDOWS\System32\cfgmgr32.dll
0x000000005b130000 - 0x000000005b13f000     C:\Program Files\Java\jdk1.8.0_111\jre\bin\verify.dll
0x000000005b100000 - 0x000000005b129000     C:\Program Files\Java\jdk1.8.0_111\jre\bin\java.dll
0x000000005b0e0000 - 0x000000005b0f6000     C:\Program Files\Java\jdk1.8.0_111\jre\bin\zip.dll
0x00007ffdc4f70000 - 0x00007ffdc6479000     C:\WINDOWS\System32\SHELL32.dll
0x00007ffdc36f0000 - 0x00007ffdc3dc9000     C:\WINDOWS\System32\windows.storage.dll
0x00007ffdc2da0000 - 0x00007ffdc2dec000     C:\WINDOWS\System32\powrprof.dll
0x00007ffdc42f0000 - 0x00007ffdc4342000     C:\WINDOWS\System32\shlwapi.dll
0x00007ffdc2d60000 - 0x00007ffdc2d6f000     C:\WINDOWS\System32\kernel.appcore.dll
0x00007ffdc3dd0000 - 0x00007ffdc3e79000     C:\WINDOWS\System32\shcore.dll
0x00007ffdc2d70000 - 0x00007ffdc2d84000     C:\WINDOWS\System32\profapi.dll
0x00000000595f0000 - 0x0000000059788000     C:\Program Files\Java\jdk1.8.0_111\jre\bin\awt.dll
0x00007ffdc49f0000 - 0x00007ffdc4aaf000     C:\WINDOWS\System32\OLEAUT32.dll
0x00007ffdc2df0000 - 0x00007ffdc2e8c000     C:\WINDOWS\System32\msvcp_win.dll
0x00007ffdc13e0000 - 0x00007ffdc145a000     C:\WINDOWS\SYSTEM32\apphelp.dll
0x00007ffdc1580000 - 0x00007ffdc1615000     C:\WINDOWS\system32\uxtheme.dll
0x00007ffdc4190000 - 0x00007ffdc42ea000     C:\WINDOWS\System32\MSCTF.dll
0x00007ffdc0c90000 - 0x00007ffdc0cb6000     C:\WINDOWS\system32\dwmapi.dll
0x00007ffdc4e30000 - 0x00007ffdc4f68000     C:\WINDOWS\System32\ole32.dll
0x000000005b050000 - 0x000000005b097000     C:\Program Files\Java\jdk1.8.0_111\jre\bin\fontmanager.dll
0x000000005b0c0000 - 0x000000005b0da000     C:\Program Files\Java\jdk1.8.0_111\jre\bin\net.dll
0x00007ffdc2630000 - 0x00007ffdc268c000     C:\WINDOWS\system32\mswsock.dll
0x000000005b0a0000 - 0x000000005b0b1000     C:\Program Files\Java\jdk1.8.0_111\jre\bin\nio.dll
0x000000005b000000 - 0x000000005b042000     C:\Program Files\Java\jdk1.8.0_111\jre\bin\t2k.dll
0x00007ffdc3fc0000 - 0x00007ffdc405f000     C:\WINDOWS\System32\clbcatq.dll
0x00007ffdc03c0000 - 0x00007ffdc0568000     C:\WINDOWS\SYSTEM32\WindowsCodecs.dll
0x00007ffda4440000 - 0x00007ffda4489000     C:\WINDOWS\system32\dataexchange.dll
0x00007ffdc0de0000 - 0x00007ffdc0f31000     C:\WINDOWS\system32\dcomp.dll
0x00007ffdbf100000 - 0x00007ffdbf3b6000     C:\WINDOWS\system32\d3d11.dll
0x00007ffdc1e00000 - 0x00007ffdc1e9f000     C:\WINDOWS\system32\dxgi.dll
0x00007ffdc1890000 - 0x00007ffdc19ac000     C:\WINDOWS\system32\twinapi.appcore.dll
0x00007ffdc2ca0000 - 0x00007ffdc2ccb000     C:\WINDOWS\system32\bcrypt.dll
0x00007ffdc27d0000 - 0x00007ffdc27e7000     C:\WINDOWS\SYSTEM32\CRYPTSP.dll
0x00007ffdc2230000 - 0x00007ffdc2263000     C:\WINDOWS\system32\rsaenh.dll
0x00007ffdc24b0000 - 0x00007ffdc24cf000     C:\WINDOWS\SYSTEM32\USERENV.dll
0x00007ffdc2c90000 - 0x00007ffdc2c9b000     C:\WINDOWS\SYSTEM32\CRYPTBASE.dll
0x00007ffdc23b0000 - 0x00007ffdc23e8000     C:\WINDOWS\SYSTEM32\IPHLPAPI.DLL
0x00007ffdc4dc0000 - 0x00007ffdc4dc8000     C:\WINDOWS\System32\NSI.dll
0x00007ffdb9840000 - 0x00007ffdb9856000     C:\WINDOWS\SYSTEM32\dhcpcsvc6.DLL
0x00007ffdb9800000 - 0x00007ffdb981a000     C:\WINDOWS\SYSTEM32\dhcpcsvc.DLL
0x000000006d080000 - 0x000000006d175000     C:\Users\david\AppData\Local\Temp\sqlite-3.21.0-6700884f-1794-4307-a7ca-45322a00aa97-sqlitejdbc.dll

VM Arguments:
jvm_args: -Dfile.encoding=UTF-8
java_command: signatur.GUI
java_class_path (initial): C:\Program Files\NetBeans 8.2\java\modules\ext\eclipselink\eclipselink.jar;C:\Program Files\NetBeans 8.2\java\modules\ext\eclipselink\javax.persistence_2.1.0.v201304241213.jar;C:\Program Files\NetBeans 8.2\java\modules\ext\eclipselink\org.eclipse.persistence.jpa.jpql_2.5.2.v20140319-9ad6abd.jar;C:\Program Files\NetBeans 8.2\java\modules\ext\AbsoluteLayout.jar;C:\Users\david\Documents\commons-io-2.4.jar;C:\Users\david\Documents\sqlite-jdbc-3.21.0.jar;C:\Users\david\Documents\rs2xml.jar;C:\Users\david\Downloads\sqlite-jdbc-3.21.0.jar;C:\Users\david\Downloads\rs2xml.jar;C:\Users\david\Downloads\commons-io-2.4.jar;C:\Users\david\Documents\velocity-engine-core-2.0.jar;C:\Users\david\Desktop\Signatur\build\classes
Launcher Type: SUN_STANDARD

Environment Variables:
PATH=C:\Program Files (x86)\Intel\TXE Components\TCS\;C:\Program Files\Intel\TXE Components\TCS\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files\Intel\TXE Components\DAL\;C:\Program Files (x86)\Intel\TXE Components\DAL\;C:\Program Files\Intel\TXE Components\IPT\;C:\Program Files (x86)\Intel\TXE Components\IPT\;C:\Program Files\Common Files\Autodesk Shared\;C:\Program Files\nodejs\;C:\Users\david\AppData\Local\Microsoft\WindowsApps;C:\Users\david\AppData\Roaming\npm;C:\Users\david\AppData\Local\Programs\MiKTeX 2.9\miktex\bin\x64\
USERNAME=david
OS=Windows_NT
PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 76 Stepping 4, GenuineIntel



---------------  S Y S T E M  ---------------

OS: Windows 10.0 , 64 bit Build 14393 (10.0.14393.2189)

CPU:total 2 (2 cores per cpu, 1 threads per core) family 6 model 76 stepping 4, cmov, cx8, fxsr, mmx, sse, sse2, sse3, ssse3, sse4.1, sse4.2, popcnt, aes, clmul, erms, 3dnowpref, tsc, tscinvbit

Memory: 4k page, physical 4104464k(345056k free), swap 9655920k(4884k free)

vm_info: Java HotSpot(TM) 64-Bit Server VM (25.111-b14) for windows-amd64 JRE (1.8.0_111-b14), built on Sep 22 2016 19:24:05 by "java_re" with MS VC++ 10.0 (VS2010)

time: Fri Apr 19 13:32:59 2019
elapsed time: 35 seconds (0d 0h 0m 35s)
```
 
Daraus werde ich jedoch auch nicht wirklich schlauer


----------



## mihe7 (19. Apr 2019)

Wie viele Daten lädst Du denn in die Anwendung?

Nachtrag: Du kannst mal versuchen, ob es läuft, wenn Du unter Project Properties -> Run -> VM Options "-Xms1024m -Xmx1024m" oder höhere Werte einträgst.


----------



## Xyz1 (19. Apr 2019)

-Xmx1g langt...

Bearbeitung oder besser 4g.


----------



## mihe7 (19. Apr 2019)

Tobias-nrw hat gesagt.:


> Bearbeitung oder besser 4g.





maGG hat gesagt.:


> Memory: 4k page, physical 4104464k(345056k free)


----------



## maGG (19. Apr 2019)

Hab beides ausprobiert. Funktioniert beides nicht. Die Daten, die in das Programm geladen werden beschtänken sich auf 5 Tabelle mit jeweils etwa 5-30 Zeileneinträgen ... das ist glaub kaum der Rede wert. Kann es nicht vielleicht sein, dass unendlich viele Objekte erzeugt werden an einer Stelle? Weil sobald ich den Instanzaufruf habe schmiert das Programm ab: "br = new BranchRepository;" sobald die Zeile auskomment ist im Konstruktor startet das Programm wieder. Vielleicht entsteht da eine Schleife aus der der Compiler nicht mehr rauskommt? Kann das sein?


----------



## Xyz1 (19. Apr 2019)

maGG hat gesagt.:


> java.lang.OutOfMemoryError: GC overhead limit exceeded


Ach so, man sollte vielleicht auch die Fehler message lesen. 
Joa, ist ein klassischer Anwendungsfehler, zu dem man so ohne weiteren Code natürlich nichts sagen kann.


----------



## mihe7 (19. Apr 2019)

maGG hat gesagt.:


> Die Daten, die in das Programm geladen werden beschtänken sich auf 5 Tabelle mit jeweils etwa 5-30 Zeileneinträgen ... das ist glaub kaum der Rede wert. Kann es nicht vielleicht sein, dass unendlich viele Objekte erzeugt werden an einer Stelle? Weil sobald ich den Instanzaufruf habe schmiert das Programm ab: "br = new BranchRepository;" sobald die Zeile auskomment ist im Konstruktor startet das Programm wieder. Vielleicht entsteht da eine Schleife aus der der Compiler nicht mehr rauskommt? Kann das sein?


Ja, das Problem ist, dass Du BranchRepository von GUI ableitest. Mach das "extends GUI" raus. 

EDIT: mach das "extends GUI" bei *allen* Klassen raus. Das ist grundsätzlicher, grober Unfug.


----------



## maGG (19. Apr 2019)

Ahja das war tatsächlich das Problem  Vielen Dank!! Druch das extends GUI wurde initComponents nochmal aufgerufen und dadurch ist so ne endlos Schleife entstanden, wo immer wieder der Instanzaufruf kahm und dann wieder initComponents, etc. XD



mihe7 hat gesagt.:


> Du versuchst, zu viel in eine Klasse zu stopfen. Vielleicht mal zum Unterschied: das BranchRepository verwaltet Filialen (Branch) und nur Filialen - keine Firmierungen, keine Companies, keine Funktionen. Das Location-enum gibt es dort nur, weil zwischen inländischen und ausländischen Filialen unterschieden wird. Somit dient die Location einfach als Query-Filter. Tatsächlich wäre die Location Teil des Domain Models und das enum müsste außerhalb der BranchRepository-Klasse definiert werden. Die EnumMap gibt es nur, weil zwei verschiedene Tabellen betroffen sind.



Achso, du meinst, dass ich den enum-Teil und die static mit sql in die Hauptklasse lieber schieben sollte? Irgendwie muss ichs halt aufteilen. Ich könnte halt entweder Klassen pro Tabelle machen, oder Klasse für Funktionen (preview, update, insert, delete), oder nochmal anders hmm ist es besser die Klassen pro Tabelle einzuführen? Die GUI Elemente habe ich jetzt aus der Klasse wieder entfernt übrigens. Was meinst du eigentlich mit "Business Entities"?


----------



## mihe7 (20. Apr 2019)

Mit Business Entity meine ich einfach Objekte der Geschäftslogik, die über konzeptionelle Identität verfügen. Das sind meist gerade die Dinge, für die Du auch eine Tabelle in der DB anlegst. Das sind solche Dinge, über die Du Dich mit Deinem Auftraggeber/Kunden bzw. mit den Anwendern unterhältst.

In Deinem Modell existiert beispielsweise die Filiale nur in der Datenbank. Im Programm taucht sie lediglich in Form von UI-Komponenten und DB-Abfragen auf. Du hast aber nirgends im Code eine Klasse, die eine Filiale darstellen würde.



maGG hat gesagt.:


> Achso, du meinst, dass ich den enum-Teil und die static mit sql in die Hauptklasse lieber schieben sollte?


Nein, ich meine damit, dass die Location entweder als separate .java-Datei existiert oder in einer Klasse Branch definiert wird. Die EnumMaps hingegen existieren höchstens im BranchRepository, weil die Filialen aus unterschiedlichen Tabellen kommen, die anhand der Location unterschieden werden. EDIT: Für Firmierungen, Companies, Funktionen gäbe es ggf. eigene Repositories, daher braucht es dort auch keine Maps. 

Bevor ich mehr dazu schreibe: gibt es eigentlich einen bestimmten Grund, warum die Filialen in zwei verschiedenen Tabellen gespeichert werden?


----------



## Xyz1 (20. Apr 2019)

Hat er womöglich die DTOs (oder VO, POJO, JavaBean...) vergessen? 

@mihe7 Schon wach?!?


----------



## mihe7 (20. Apr 2019)

Tobias-nrw hat gesagt.:


> Hat er womöglich die DTOs (oder VO, POJO, JavaBean...) vergessen?


Sozusagen. Das UI ist direkt an die DB angebunden. 



Tobias-nrw hat gesagt.:


> @mihe7 Schon wach?!?


Einer muss ja den Osterhasen fangen.


----------



## maGG (20. Apr 2019)

mihe7 hat gesagt.:


> Bevor ich mehr dazu schreibe: gibt es eigentlich einen bestimmten Grund, warum die Filialen in zwei verschiedenen Tabellen gespeichert werden?


Ich empfand es als übersichtlicher es aufzuteilen, im Ausland haben wir teilweise ganz andere Firmierungen. Für den Nutzer am Ende ist es wohlmöglich übersichtlicher es getrennt zu haben; so weiß man hoffentlich, dass man im Ausland dann nicht "GmbH" oder "AG" auswählt, sondern "Ltd." oder "B.V.".


Tobias-nrw hat gesagt.:


> Hat er womöglich die DTOs (oder VO, POJO, JavaBean...) vergessen?


Ne, ich weiß nicht mal was das ist (werde ich später mal nachschauen)


mihe7 hat gesagt.:


> In Deinem Modell existiert beispielsweise die Filiale nur in der Datenbank. Im Programm taucht sie lediglich in Form von UI-Komponenten und DB-Abfragen auf. Du hast aber nirgends im Code eine Klasse, die eine Filiale darstellen würde


Achso, du meinst dass ich dann z.B. noch die Klassen Filialen, Firmierung, Titel, etc. einführe, oder eine Klasse Person, die bestimmte Parameter in sich vereinigt. Welchen Vorteil hat man davon? Evtl. wenn man das Programm später noch erweitern möchte?


mihe7 hat gesagt.:


> EDIT: Für Firmierungen, Companies, Funktionen gäbe es ggf. eigene Repositories, daher braucht es dort auch keine Maps.


Ok, also du meinst schon Repository Klassen für Tabellen sind sinnvoller als für gleichartige Methoden? (Update, Insert, Delete, etc.)


----------



## mihe7 (20. Apr 2019)

maGG hat gesagt.:


> Für den Nutzer am Ende ist es wohlmöglich übersichtlicher es getrennt zu haben; so weiß man hoffentlich, dass man im Ausland dann nicht "GmbH" oder "AG" auswählt, sondern "Ltd." oder "B.V.".


Was Du in der DB stehen hast und was der Benutzer sieht, sind zwei paar Stiefel  



maGG hat gesagt.:


> Achso, du meinst dass ich dann z.B. noch die Klassen Filialen, Firmierung, Titel, etc. einführe, oder eine Klasse Person, die bestimmte Parameter in sich vereinigt.


Richtig. 



maGG hat gesagt.:


> Welchen Vorteil hat man davon?


Naja, zum einen findet man die Konzepte des Problembereichs im Code wieder, zum anderen hat man klare Zuständigkeiten und definierte Code-Einheiten. 

Wenn Du z. B. ein BranchRepository hast, das Branch-Objekte zurückgibt, dann brauchst Du nicht für jedes "Feld" eine Abfrage, sondern kannst die Liste für die Tabelle als auch für die ComboBox verwenden. 

Wenn das Repository dann noch über Änderungen informiert (Events), dann lässt sich vieles einfacher automatisieren. Beispiel: der Anwender steht in dem Fenster, in dem er eine Person bearbeitet und die Zugehörigkeit zu einer Filiale über eine Combobox auswählt. Jetzt stellt er fest, dass eine Filiale fehlt. Daher öffnet er die Filialverwaltung und fügt dort eine Filiale hinzu. 

Jetzt kann man z. B. folgendes machen: Das Repository führt den INSERT aus und informiert alle Beobachter darüber, dass (und ggf. welche) es eine neue Filiale gibt. Da man zuvor das Combobox-Model als Beobachter des Repositories registriert hat, wird es über diese Neuerung informiert und der Benutzer hat die neue Filiale sofort in der Auswahl stehen.

Man kann noch einen Schritt weitergehen und ComboBox und TableModel zusammenfassen, denn in beiden Fällen werden jetzt ja einfach Branch-Objekte verwaltet. Die JComboBox bekommt dann lediglich einen passenden Renderer, der nur den Namen zurückgibt.



maGG hat gesagt.:


> Ok, also du meinst schon Repository Klassen für Tabellen sind sinnvoller als für gleichartige Methoden?


Ich betrachte das nicht aus Sicht der Datenbank, aber ja, kann man grob so sagen.


----------



## maGG (24. Apr 2019)

Ah ok, ich habe jetzt ein Ordner "ObjectClasses" und ein Ordner "RepositoryClasses". Ich bin mir aber nicht sicher, wie ich meine Klasse Branch benutzen kann. Für die FillComboBox Methode bringt das Objekt nichts glaub ich, weil nur das Label gebraucht wird. Und für Update und Insert hätte ich zwar alle notwendigen Übergabeparamenter, ich weiß aber nicht wie ich die einsetzen kann. Aktuell mache ich das ja mit "pst.setString(int, parameter)" oder pst.setInt(int, parameter).  Die Getter und Setter Methoden habe ich auch alle in Branch.

Bisher sind das bei mir ja void Methoden, bei deinem Beispiel mit List<Branch> findAll() und List<Branch> findbyLocation(Location where) brauche ich irgendein return Wert, geht das damit? Also bisher werden die meine Parameter ja nur in einer abgewandelten toString Methode als Template in einem Writer geschrieben und das Programm gibt verschiede Textdateien aus. Also mir fehlen jetzt gerade die Anwendungsbeispiele, wo ich das benutzen könnte. Oder übersehe ich da was? Kann das in bisherigen Methoden benutzen?

Das mit der Eventmethode hört sich interessant an, dann könnte ich ein Panel erstellen, wo sowas angzeigt wird.


----------



## mihe7 (24. Apr 2019)

maGG hat gesagt.:


> Ordner


Ordner? Du meinst Klassen, oder?



maGG hat gesagt.:


> Für die FillComboBox Methode bringt das Objekt nichts glaub ich, weil nur das Label gebraucht wird.


Doch. Das Objekt bringt aus mehreren Gründen etwas: erstens wählst Du ein Branch-Objekt aus (d. h. Du bekommst das vollständige Objekt und nicht nur das Label zurück), zweitens brauchst Du die DB nicht extra bemühen. Du musst Dir allerdings einen Renderer schreiben.



maGG hat gesagt.:


> Und für Update und Insert hätte ich zwar alle notwendigen Übergabeparamenter, ich weiß aber nicht wie ich die einsetzen kann. Aktuell mache ich das ja mit "pst.setString(int, parameter)" oder pst.setInt(int, parameter). Die Getter und Setter Methoden habe ich auch alle in Branch.


Das machst Du weiterhin genauso, nur dass die Daten aus dem Branch-Objekt kommen.

Bei Deinem zweiten Absatz verstehe ich nicht, worauf Du hinaus willst.


----------



## maGG (24. Apr 2019)

Ne ich meinte das so:



wie meinst du das mit dem Branch Objekt? Du meinst einen Instanzaufruf/Konstruktoraufruf?

Und was meinst du mit Renderer?


----------



## maGG (24. Apr 2019)

Meinst du sowas?

```
public class BranchComboBox extends BasicComboBoxRenderer{

    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean celHasFocus){
        Branch branch = (Branch) value;
        if(index == 0){
            setText("Bitte auswählen");
        }else{
            setText(branch.getLabel());
        }
        return this;      
    }
   
}
```

Meine Branch Class sieht so jetzt aus:

```
public class Branch {

    int id, vorwahl;
    long telStartNum;
    String label, strasze, plzort, ort, telStartTxt, telEndDefault, faxStartTxt, faxEndTxt;  
   
    public Branch(){
        this.id = 0;
        this.vorwahl = 0;
        this.telStartNum = 0;
        this.label = "";
        this.strasze = "";
        this.plzort = "";
        this.ort = "";
        this.telStartTxt = "";
        this.telEndDefault = "";
        this.faxStartTxt = "";
        this.faxEndTxt = "";
    }
   
    public Branch(int id, String label, String strasze, String plzort, String ort, int vorwahl, long telStartNum, String telStartTxt,
        String telEndDefault, String faxStartTxt, String faxEndTxt){
        this.id = id;
        this.label = label;
        this.strasze = strasze;
        this.plzort = plzort;
        this.ort = ort;
        this.vorwahl = vorwahl;
        this.telStartNum = telStartNum;
        this.telStartTxt = telStartTxt;
        this.telEndDefault = telEndDefault;
        this.faxStartTxt = faxStartTxt;
        this.faxEndTxt = faxEndTxt;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getVorwahl() {
        return vorwahl;
    }

    public void setVorwahl(int vorwahl) {
        this.vorwahl = vorwahl;
    }

    public long getTelStartNum() {
        return telStartNum;
    }

    public void setTelStartNum(long telStartNum) {
        this.telStartNum = telStartNum;
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getStrasze() {
        return strasze;
    }

    public void setStrasze(String strasze) {
        this.strasze = strasze;
    }

    public String getPlzort() {
        return plzort;
    }

    public void setPlzort(String plzort) {
        this.plzort = plzort;
    }

    public String getOrt() {
        return ort;
    }

    public void setOrt(String ort) {
        this.ort = ort;
    }

    public String getTelStartTxt() {
        return telStartTxt;
    }

    public void setTelStartTxt(String telStartTxt) {
        this.telStartTxt = telStartTxt;
    }

    public String getTelEndDefault() {
        return telEndDefault;
    }

    public void setTelEndDefault(String telEndDefault) {
        this.telEndDefault = telEndDefault;
    }

    public String getFaxStartTxt() {
        return faxStartTxt;
    }

    public void setFaxStartTxt(String faxStartTxt) {
        this.faxStartTxt = faxStartTxt;
    }

    public String getFaxEndTxt() {
        return faxEndTxt;
    }

    public void setFaxEndTxt(String faxEndTxt) {
        this.faxEndTxt = faxEndTxt;
    }
   
}
```

Bei der Anwendung des Objektes Branch tue ich mich noch etwas schwer:

```
public List<Branch> findNames2(Location where) throws SQLException{
        String sql = queries.get(where);
        try(PreparedStatement pst = conn.prepareStatement(sql)){
            try(ResultSet rs = pst.executeQuery()){
                List<Branch> result = new ArrayList<>();
                Branch b = new Branch();
                String label = result.getLabel();
                while(rs.next()){
                    result.setLabel((rs.getString("Label"));
                }
                return result;            
            }
        }
    }
```
Das ist natürlich noch falsch, weiß aber grad nicht wie man das richtig macht


----------



## mihe7 (24. Apr 2019)

maGG hat gesagt.:


> Meinst du sowas?


Ja, sowas in der Richtung  

Sorry, wenn ich etwas kurz angebunden bin, hab im Moment keine Zeit.



maGG hat gesagt.:


> Bei der Anwendung des Objektes Branch tue ich mich noch etwas schwer:


Du schreibst Dir am besten eine Methode, die aus einem ResultSet ein komplettes Branch-Objekt einliest. Und die Methode rufst du dann in den entsprechenden anderen Methoden auf.

BTW: ich meine, mich zu erinnern, Eclipselink im Projekt gesehen zu haben. Damit würde die Abbildung automatisch funktionieren.


----------



## maGG (24. Apr 2019)

mihe7 hat gesagt.:


> BTW: ich meine, mich zu erinnern, Eclipselink im Projekt gesehen zu haben. Damit würde die Abbildung automatisch funktionieren.


Ne, benutze Netbeans IDE
Meinst du evtl., dass ich ich bei Propierties bei "Type Parameters" das Feld mit "<String>" rauslösche? Dann wäre die ComboBox glaub vom Typ Object.

So vielleicht?

```
public Branch findNames2(Location where) throws SQLException{
        String sql = queries.get(where);
        try(PreparedStatement pst = conn.prepareStatement(sql)){
            try(ResultSet rs = pst.executeQuery()){
                Branch result = new Branch();
                while(rs.next()){
                    result.setLabel((rs.getString("Label")));
                }
                return result;           
            }
        }
    }
```



mihe7 hat gesagt.:


> Sorry, wenn ich etwas kurz angebunden bin, hab im Moment keine Zeit.


Kein Ding, du hast mir schon so viel geholfen! Ich muss auch mal selbst was hinbekommen, ist nur echt schwer manchmal, wenn man sowas noch nie gemacht experimentiert man sich kaputt, und kommt oft auf dem Schlauch


----------



## mihe7 (24. Apr 2019)

maGG hat gesagt.:


> Ne, benutze Netbeans IDE


LOL, nein ich meinte nicht die IDE Eclipse sondern die Bibliothek "Eclipselink" der Eclipse Foundation. Eclipselink ist die Referenzimplementierung der JPA (Java Persistence API). Es handelt sich um einen Object-Relational-Mapper (ORM).



maGG hat gesagt.:


> Meinst du evtl., dass ich ich bei Propierties bei "Type Parameters" das Feld mit "<String>" rauslösche? Dann wäre die ComboBox glaub vom Typ Object.


Du kannst als Typparameter Branch angeben (JComboBox<Branch>).

Vielleicht komme ich später noch dazu...


----------



## maGG (24. Apr 2019)

So ich hab mal rumprobiert und zu mindest ist nix mehr rot :'D


```
package RepositoryClasses;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import ObjectClasses.Branch;

public class BranchRepository{
  
    public enum Location{
        DOMESTIC, FOREIGN,
        DOMESTIC_UPDATE, FOREIGN_UPDATE,
        DOMESTIC_INSERT,FOREIGN_INSERT
    }
  
    private static final EnumMap<Location, String> queries = new EnumMap<>(Location.class);
  
    static{
        queries.put(Location.DOMESTIC, "SELECT * FROM Filialen_Deutschland");
        queries.put(Location.FOREIGN, "SELECT * FROM Filialen_Ausland");
        queries.put(Location.DOMESTIC_UPDATE, "UPDATE Filialen_Deutschland SET id=?, LABEL=?, STRASZE=?, PLZ_ORT=?, ORT=?,TEL_LAND=?, " +
                "TEL_ANFANG_INT=?, TEL_ANFANG_TXT=?, TEL_ENDE_DEFAULT=?, FAX_ANFANG_TXT=?, FAX_ENDE_TXT=? WHERE id=?");
        queries.put(Location.FOREIGN_UPDATE, "UPDATE Filialen_Ausland SET id=?, LABEL=?, STRASZE=?, PLZ_ORT=?, ORT=?,TEL_LAND=?, " +
                "TEL_ANFANG_INT=?, TEL_ANFANG_TXT=?, TEL_ENDE_DEFAULT=?, FAX_ANFANG_TXT=?, FAX_ENDE_TXT=? WHERE id=?");
        queries.put(Location.DOMESTIC_INSERT, "INSERT INTO Filialen_Deutschland (id,LABEL,STRASZE,PLZ_ORT,ORT,TEL_LAND,TEL_ANFANG_INT," +
                "TEL_ANFANG_TXT,TEL_ENDE_DEFAULT,FAX_ANFANG_TXT,FAX_ENDE_TXT) VALUES(?,?,?,?,?,?,?,?,?,?,?)");
        queries.put(Location.DOMESTIC_INSERT, "INSERT INTO Filialen_Deutschland (id,LABEL,STRASZE,PLZ_ORT,ORT,TEL_LAND,TEL_ANFANG_INT," +
                "TEL_ANFANG_TXT,TEL_ENDE_DEFAULT,FAX_ANFANG_TXT,FAX_ENDE_TXT) VALUES(?,?,?,?,?,?,?,?,?,?,?)");
    }
  
    protected Branch branch;
    private Connection conn;
  
    public BranchRepository(){}
  
    public BranchRepository (Connection conn){
        this.conn = conn;
              
    }  
  
    public Branch findNames(Location where) throws SQLException{
        String sql = queries.get(where);
        try(PreparedStatement pst = conn.prepareStatement(sql)){
            try(ResultSet rs = pst.executeQuery()){
                branch = new Branch();
                while(rs.next()){
                    branch.setLabel((rs.getString("Label")));
                }
                return branch;            
            }
        }
    }  
  

    public void updateBranch(Location where, int f1, String f2, String f3, String f4, String f5, int f6, long f7, String f8,
            String f9, String f10, String f11) throws SQLException{
        String sql = queries.get(where);
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
            branch = new Branch(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11);
            pst.setInt(1, branch.getId()); //id
            pst.setString(2, branch.getLabel()); //LABEL
            pst.setString(3, branch.getStrasze()); //STRASZE
            pst.setString(4, branch.getPlzort()); //PLZ_ORT
            pst.setString(5, branch.getOrt()); //ORT
            pst.setInt(6, branch.getVorwahl()); //TEL_LAND
            pst.setLong(7, branch.getTelStartNum()); //TEL_ANFANG_INT
            pst.setString(8, branch.getFaxStartTxt()); //TEL_ANFANG_TXT
            if (branch.getTelEndDefault().equals("")) {
                pst.setObject(9, null);
            } else {
                pst.setInt(9, Integer.parseInt(branch.getTelEndDefault())); //TEL_ENDE_DEFAULT
            }
            pst.setString(10, branch.getFaxStartTxt()); //FAX_ANFANG_TXT
            if (branch.getFaxEndTxt().equals("")) {
                pst.setObject(11, null);
            } else {
                pst.setString(11, branch.getFaxEndTxt()); //FAX_ENDE_TXT
            }
            pst.setInt(12, branch.getId()); //id
            pst.executeUpdate();
        }  
    }

    public void insertBranch(Location where, int f1, String f2, String f3, String f4, String f5, int f6, long f7, String f8,
            String f9, String f10, String f11) throws SQLException{
        String sql = queries.get(where);
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
            branch = new Branch(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11);
            pst.setInt(1, branch.getId()); //id
            pst.setString(2, branch.getLabel()); //LABEL
            pst.setString(3, branch.getStrasze()); //STRASZE
            pst.setString(4, branch.getPlzort()); //PLZ_ORT
            pst.setString(5, branch.getOrt()); //ORT
            pst.setInt(6, branch.getVorwahl()); //TEL_LAND
            pst.setLong(7, branch.getTelStartNum()); //TEL_ANFANG_INT
            pst.setString(8, branch.getFaxStartTxt()); //TEL_ANFANG_TXT
            if (branch.getTelEndDefault().equals("")) {
                pst.setObject(9, null);
            } else {
                pst.setInt(9, Integer.parseInt(branch.getTelEndDefault())); //TEL_ENDE_DEFAULT
            }
            pst.setString(10, branch.getFaxStartTxt()); //FAX_ANFANG_TXT
            if (branch.getFaxEndTxt().equals("")) {
                pst.setObject(11, null);
            } else {
                pst.setString(11, branch.getFaxEndTxt()); //FAX_ENDE_TXT
            }
            pst.execute();
        }  
    }  
  
}
```

Könnte das so passen?



mihe7 hat gesagt.:


> LOL, nein ich meinte nicht die IDE Eclipse sondern die Bibliothek "Eclipselink" der Eclipse Foundation. Eclipselink ist die Referenzimplementierung der JPA (Java Persistence API). Es handelt sich um einen Object-Relational-Mapper (ORM).


Ok, das verstehe ich nicht


----------



## mrBrown (24. Apr 2019)

Du solltest den Location-Enum auf zwei Werte begrenzen (DOMESTIC & FOREIGN), sonst kann man zB sowas machen: `updateBranch(DOMESTIC_INSERT, ...)`, was offensichtlicher Unsinn ist 

Die Branch-Instanzvariable sollte noch weg, das sollte immer eine rein lokale Variable sein.

Und natürlich f1 - f11 sollten mindesten sinnvoll benannt werden, im Idealfall aber einfach mit einem Parameter vom Typ Branch ersetzt werden.


----------



## maGG (24. Apr 2019)

mrBrown hat gesagt.:


> zwei Werte begrenzen (DOMESTIC & FOREIGN), sonst kann man zB sowas machen: `updateBranch(DOMESTIC_INSERT, ...)`, was offensichtlicher Unsinn ist


Aber dann müsste ich 3 EnumMaps machen, was ich ja auch nicht machen soll  irgendwie muss ich es ja machen, oder hast du vielleicht ein konkreten Vorschlag, wie ich es besser machen kann? 

Habs in lokale Variablen angepasst:


```
package RepositoryClasses;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import ObjectClasses.Branch;

public class BranchRepository{
    
    public enum Location{
        DOMESTIC, FOREIGN,
        DOMESTIC_UPDATE, FOREIGN_UPDATE,
        DOMESTIC_INSERT,FOREIGN_INSERT
    }
    
    private static final EnumMap<Location, String> queries = new EnumMap<>(Location.class);
    
    static{
        queries.put(Location.DOMESTIC, "SELECT * FROM Filialen_Deutschland");
        queries.put(Location.FOREIGN, "SELECT * FROM Filialen_Ausland");
        queries.put(Location.DOMESTIC_UPDATE, "UPDATE Filialen_Deutschland SET id=?, LABEL=?, STRASZE=?, PLZ_ORT=?, ORT=?,TEL_LAND=?, " +
                "TEL_ANFANG_INT=?, TEL_ANFANG_TXT=?, TEL_ENDE_DEFAULT=?, FAX_ANFANG_TXT=?, FAX_ENDE_TXT=? WHERE id=?");
        queries.put(Location.FOREIGN_UPDATE, "UPDATE Filialen_Ausland SET id=?, LABEL=?, STRASZE=?, PLZ_ORT=?, ORT=?,TEL_LAND=?, " +
                "TEL_ANFANG_INT=?, TEL_ANFANG_TXT=?, TEL_ENDE_DEFAULT=?, FAX_ANFANG_TXT=?, FAX_ENDE_TXT=? WHERE id=?");
        queries.put(Location.DOMESTIC_INSERT, "INSERT INTO Filialen_Deutschland (id,LABEL,STRASZE,PLZ_ORT,ORT,TEL_LAND,TEL_ANFANG_INT," +
                "TEL_ANFANG_TXT,TEL_ENDE_DEFAULT,FAX_ANFANG_TXT,FAX_ENDE_TXT) VALUES(?,?,?,?,?,?,?,?,?,?,?)");
        queries.put(Location.DOMESTIC_INSERT, "INSERT INTO Filialen_Deutschland (id,LABEL,STRASZE,PLZ_ORT,ORT,TEL_LAND,TEL_ANFANG_INT," +
                "TEL_ANFANG_TXT,TEL_ENDE_DEFAULT,FAX_ANFANG_TXT,FAX_ENDE_TXT) VALUES(?,?,?,?,?,?,?,?,?,?,?)");
    }
    
    private final Connection conn;
    
    public BranchRepository(){
        this.conn = null;
    }
    
    public BranchRepository (Connection conn){
        this.conn = conn;
                
    }
    
    public Branch findNames(Location where) throws SQLException{
        String sql = queries.get(where);
        try(PreparedStatement pst = conn.prepareStatement(sql)){
            try(ResultSet rs = pst.executeQuery()){
                Branch b = new Branch();
                while(rs.next()){
                    b.setLabel((rs.getString("Label")));
                }
                return b;             
            }
        }
    }   
    

    public void updateBranch(Location where, int f1, String f2, String f3, String f4, String f5, int f6, long f7, String f8,
            String f9, String f10, String f11) throws SQLException{
        String sql = queries.get(where);
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
            Branch b = new Branch(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11); 
            pst.setInt(1, b.getId()); //id
            pst.setString(2, b.getLabel()); //LABEL
            pst.setString(3, b.getStrasze()); //STRASZE
            pst.setString(4, b.getPlzort()); //PLZ_ORT
            pst.setString(5, b.getOrt()); //ORT
            pst.setInt(6, b.getVorwahl()); //TEL_LAND
            pst.setLong(7, b.getTelStartNum()); //TEL_ANFANG_INT
            pst.setString(8, b.getFaxStartTxt()); //TEL_ANFANG_TXT
            if (b.getTelEndDefault().equals("")) {
                pst.setObject(9, null);
            } else {
                pst.setInt(9, Integer.parseInt(b.getTelEndDefault())); //TEL_ENDE_DEFAULT
            }
            pst.setString(10, b.getFaxStartTxt()); //FAX_ANFANG_TXT
            if (b.getFaxEndTxt().equals("")) {
                pst.setObject(11, null);
            } else {
                pst.setString(11, b.getFaxEndTxt()); //FAX_ENDE_TXT
            }
            pst.setInt(12, b.getId()); //id
            pst.executeUpdate(); 
        }   
    }

    public void insertBranch(Location where, int f1, String f2, String f3, String f4, String f5, int f6, long f7, String f8,
            String f9, String f10, String f11) throws SQLException{
        String sql = queries.get(where);
        try (PreparedStatement pst = conn.prepareStatement(sql)) {
            Branch b = new Branch(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11);
            pst.setInt(1, b.getId()); //id
            pst.setString(2, b.getLabel()); //LABEL
            pst.setString(3, b.getStrasze()); //STRASZE
            pst.setString(4, b.getPlzort()); //PLZ_ORT
            pst.setString(5, b.getOrt()); //ORT
            pst.setInt(6, b.getVorwahl()); //TEL_LAND
            pst.setLong(7, b.getTelStartNum()); //TEL_ANFANG_INT
            pst.setString(8, b.getFaxStartTxt()); //TEL_ANFANG_TXT
            if (b.getTelEndDefault().equals("")) {
                pst.setObject(9, null);
            } else {
                pst.setInt(9, Integer.parseInt(b.getTelEndDefault())); //TEL_ENDE_DEFAULT
            }
            pst.setString(10, b.getFaxStartTxt()); //FAX_ANFANG_TXT
            if (b.getFaxEndTxt().equals("")) {
                pst.setObject(11, null);
            } else {
                pst.setString(11, b.getFaxEndTxt()); //FAX_ENDE_TXT
            }
            pst.execute();
        }   
    }   
    
}
```



mrBrown hat gesagt.:


> Und natürlich f1 - f11 sollten mindesten sinnvoll benannt werden, im Idealfall aber einfach mit einem Parameter vom Typ Branch ersetzt werden.


Das verstehe ich nicht. Also meinst du die Übergabeparameter?


----------



## mihe7 (24. Apr 2019)

Nicht

```
public void updateBranch(Location where, int f1, String f2, String f3, String f4, String f5, int f6, long f7, String f8,
            String f9, String f10, String f11) throws SQLException{
```
sondern eher

```
public void updateBranch(Location where, Branch branch) throws SQLException{
```
bzw. ggf. noch besser (das hängt jetzt ein wenig von den Anforderungen ab), wenn Du die Location in den Branch reinnimmst und nur noch

```
public void updateBranch(Branch branch) throws SQLException{
```
hast. Analog dazu natürlich insert etc.


----------



## mihe7 (25. Apr 2019)

Jetzt habe ich mal was zusammengeschustert. Vielleicht kannst Du damit was anfangen.

In Main musst Du natürlich die DB korrekt einstellen und zum Projekt den passenden JDBC-Treiber hinzufügen.


----------



## maGG (26. Apr 2019)

Ah cool, danke für die Mühe!  ich verstehe einige Dinge noch nicht, muss mir das noch etwas länger anschauen


----------



## mihe7 (26. Apr 2019)

Frag einfach, wenn was unklar ist (es sind Teile vorhanden, die gar nicht verwendet werden).


----------



## maGG (27. Apr 2019)

mihe7 hat gesagt.:


> Frag einfach, wenn was unklar ist (es sind Teile vorhanden, die gar nicht verwendet werden).


Ok, haha ich habe einige Frage, fangen wir mal mit der Repository Klasse an:

1. 
PersonRepository -> getGeneratedId, wieso eine eigene Methode dafür?
stmt.setInt(1, branch.getId()); hätte das nicht auch dafür ausgereicht?

2.
PersonRepository -> fireEvent(RepositoryEvent<Branch> event) -> was macht die Methode? 

Ich habe mal eine Testklasse erstellt, diese sieht gerade so aus:

```
package RepositoryClasses;

import Listener.RepositoryEvent;
import Listener.RepositoryListener;
import ObjectClasses.Branch;
import java.lang.ref.WeakReference;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.Iterator;
import java.util.List;

public class BranchRepository2 {

    public enum Location{ DOMESTIC, FOREIGN }
    
    public enum Update{ DOMESTIC, FOREIGN }
    
    public enum Insert{ DOMESTIC, FOREIGN }
    
    private static final EnumMap<Location, String> LOCATION = new EnumMap<>(Location.class);
    
    private static final EnumMap<Update, String> UPDATE = new EnumMap<>(Update.class);
    
    private static final EnumMap<Insert, String> INSERT = new EnumMap<>(Insert.class);
    
    static{
        LOCATION.put(Location.DOMESTIC, "SELECT * FROM Filialen_Deutschland");
        LOCATION.put(Location.FOREIGN, "SELECT * FROM Filialen_Ausland");
        UPDATE.put(Update.DOMESTIC, "UPDATE Filialen_Deutschland SET id=?, LABEL=?, STRASZE=?, PLZ_ORT=?, ORT=?,TEL_LAND=?, " +
                "TEL_ANFANG_INT=?, TEL_ANFANG_TXT=?, TEL_ENDE_DEFAULT=?, FAX_ANFANG_TXT=?, FAX_ENDE_TXT=? WHERE id=?");
        UPDATE.put(Update.FOREIGN, "UPDATE Filialen_Ausland SET id=?, LABEL=?, STRASZE=?, PLZ_ORT=?, ORT=?,TEL_LAND=?, " +
                "TEL_ANFANG_INT=?, TEL_ANFANG_TXT=?, TEL_ENDE_DEFAULT=?, FAX_ANFANG_TXT=?, FAX_ENDE_TXT=? WHERE id=?");
        INSERT.put(Insert.DOMESTIC, "INSERT INTO Filialen_Deutschland (id,LABEL,STRASZE,PLZ_ORT,ORT,TEL_LAND,TEL_ANFANG_INT," +
                "TEL_ANFANG_TXT,TEL_ENDE_DEFAULT,FAX_ANFANG_TXT,FAX_ENDE_TXT) VALUES(?,?,?,?,?,?,?,?,?,?,?)");
        INSERT.put(Insert.FOREIGN, "INSERT INTO Filialen_Deutschland (id,LABEL,STRASZE,PLZ_ORT,ORT,TEL_LAND,TEL_ANFANG_INT," +
                "TEL_ANFANG_TXT,TEL_ENDE_DEFAULT,FAX_ANFANG_TXT,FAX_ENDE_TXT) VALUES(?,?,?,?,?,?,?,?,?,?,?)");
    }

    private final Connection conn;
    
    public BranchRepository2(){
        this.conn = null;
    }
    
    public BranchRepository2(Connection conn){
        this.conn = conn;
    }

    private final List<WeakReference<RepositoryListener<Branch>>> listeners = new ArrayList<>();
    
    public void addListener(RepositoryListener<Branch> listener) {
        listeners.add(new WeakReference(listener));
    }   
    
    public void removeListener(RepositoryListener<Branch> listener) {
        listeners.remove(new WeakReference(listener));
    }
    
    public List<Branch> findAll(Location where) throws SQLException{
        String sql = LOCATION.get(where);
        try(PreparedStatement pst = conn.prepareStatement(sql)){
            try(ResultSet rs = pst.executeQuery()){
                List<Branch> result = new ArrayList<>();
                while(rs.next()){
                    result.add(readBranch(rs));
                }
                return result;             
            }
        }
    }
    
        public List<String> findLabel(Location where) throws SQLException{
        String sql = LOCATION.get(where);
        try(PreparedStatement pst = conn.prepareStatement(sql)){
            try(ResultSet rs = pst.executeQuery()){
                List<String> result = new ArrayList<>();
                while(rs.next()){
                    result.add(readLabel(rs));
                }
                return result;             
            }
        }
    }
        
    public void updateBranch2(Update where, Branch branch) throws SQLException{
        try(PreparedStatement pst = conn.prepareStatement(UPDATE.get(where))){ 
            pst.setInt(1, branch.getId()); //id
            pst.setString(2, branch.getLabel()); //LABEL
            pst.setString(3, branch.getStrasze()); //STRASZE
            pst.setString(4, branch.getPlzort()); //PLZ_ORT
            pst.setString(5, branch.getOrt()); //ORT
            pst.setInt(6, branch.getVorwahl()); //TEL_LAND
            pst.setLong(7, branch.getTelStartNum()); //TEL_ANFANG_INT
            pst.setString(8, branch.getFaxStartTxt()); //TEL_ANFANG_TXT
            if(branch.getTelEndDefault().equals("")){
                pst.setObject(9, null);
            }else{
                pst.setInt(9, Integer.parseInt(branch.getTelEndDefault())); //TEL_ENDE_DEFAULT
            }
            pst.setString(10, branch.getFaxStartTxt()); //FAX_ANFANG_TXT
            if(branch.getFaxEndTxt().equals("")){
                pst.setObject(11, null);
            }else{
                pst.setString(11, branch.getFaxEndTxt()); //FAX_ENDE_TXT
            }
            pst.setInt(12, branch.getId()); //id
            pst.executeUpdate();
            fireUpdated(branch);
        }   
    }         
    
    public void insertBranch2(Insert where, Branch branch) throws SQLException{
        try (PreparedStatement pst = conn.prepareStatement(INSERT.get(where))) {
            pst.setInt(1, branch.getId()); //id
            pst.setString(2, branch.getLabel()); //LABEL
            pst.setString(3, branch.getStrasze()); //STRASZE
            pst.setString(4, branch.getPlzort()); //PLZ_ORT
            pst.setString(5, branch.getOrt()); //ORT
            pst.setInt(6, branch.getVorwahl()); //TEL_LAND
            pst.setLong(7, branch.getTelStartNum()); //TEL_ANFANG_INT
            pst.setString(8, branch.getFaxStartTxt()); //TEL_ANFANG_TXT
            if (branch.getTelEndDefault().equals("")) {
                pst.setObject(9, null);
            } else {
                pst.setInt(9, Integer.parseInt(branch.getTelEndDefault())); //TEL_ENDE_DEFAULT
            }
            pst.setString(10, branch.getFaxStartTxt()); //FAX_ANFANG_TXT
            if (branch.getFaxEndTxt().equals("")) {
                pst.setObject(11, null);
            } else {
                pst.setString(11, branch.getFaxEndTxt()); //FAX_ENDE_TXT
            }
            pst.execute();
            fireAdded(branch);
        }   
    }     
    
    private void fireAdded(Branch branch) {
        fireEvent(new RepositoryEvent<>(RepositoryEvent.Type.ADDED, branch));
    }

    private void fireUpdated(Branch branch) {
        fireEvent(new RepositoryEvent<>(RepositoryEvent.Type.UPDATED, branch));
    }
    
    private void fireEvent(RepositoryEvent<Branch> event) {
        Iterator<WeakReference<RepositoryListener<Branch>>> it = listeners.iterator();
        while (it.hasNext()) {
            WeakReference<RepositoryListener<Branch>> ref = it.next();
            RepositoryListener<Branch> listener = ref.get();
            if (listener != null) {
                listener.eventOccured(event);           
            } else {
                it.remove();
            }
        }
    }       
        
    private Branch readBranch(ResultSet rs) throws SQLException {
        return new Branch(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getString(4), rs.getString(5), rs.getInt(6), rs.getLong(7), rs.getString(8), rs.getString(9), rs.getString(10), rs.getString(11));
    }
        
    private String readLabel(ResultSet rs) throws SQLException{
        return rs.getString(2);
    }
    
}
```

Könnte das so evtl. passen?

Ich weiß noch auch noch nicht wie ich mein Renderer anwende auf meine "jComboBoxFilialenDeutschland" anwende und ob ich alle Parameter beim Renderer brauche, aber bleiben wir vielleicht erst mal bei der Repository Klasse :O


----------



## mihe7 (27. Apr 2019)

maGG hat gesagt.:


> PersonRepository -> getGeneratedId, wieso eine eigene Methode dafür?
> stmt.setInt(1, branch.getId()); hätte das nicht auch dafür ausgereicht?


Das kommt darauf an, wie Du die ID erzeugst. In meinem Beispiel gehe ich von einem auto_increment Feld in MySQL aus. Bei einem `INSERT INTO person (name, vorname) VALUES (...)` erzeugt die Datenbank automatisch die ID. 

Das Person-Objekt hat zum Zeitpunkt des Aufrufs von add() noch keine ID. Die Methode getGeneratedId() lässt sich die von der DB beim INSERT vergebene ID zurückgeben. Anschließend wird diese verwendet ,um die ID des Person-Objekts zu setzen. D. h. nach dem Aufruf von add() hat das Person-Objekt eine ID und zwar diejenige, die von der DB vergeben wurde.

Wenn der Benutzer die ID selbst eingibt oder die Anwendung die ID irgendwie vergibt (z. B. in Form eines Zählers oder einer UUID), dann kannst Du natürlich der Person die ID auch vor dem add mitgeben und dann z. B. stmt.setInt(1, person.getId()) verwenden.



maGG hat gesagt.:


> PersonRepository -> fireEvent(RepositoryEvent<Branch> event) -> was macht die Methode?



Die Methode benachrichtigt registrierte Listener über ein eingetretenes Ereignis. Sie wird intern von fireAdded bzw. fireUpdated (und wenn es programmiert wäre, dann auch von fireRemoved etc.) verwendet. Allerdings existiert in dem Beispiel (wenn ich mich recht entsinne) kein Listener, der darauf reagieren würde. 

Wozu kann man das brauchen? Das Person-Repository ist die zentrale Anlaufstelle, wenn es um Personen geht. Es bekommt jede persistente Änderung mit. Das Observer-Pattern (nichts anderes ist das) kann verwendet werden, um völlig voneinander unabhängige Programmteile miteinander kommunizieren zu lassen. 

Aktuell läuft das ausschließlich über das UI-Model (PersonsModel), weil dieses sowohl TableModel, ListModel und ComboBoxModel implementiert und somit für alles verwendet wird und ich die Sache nicht weiter verkomplizieren wollte.

Aber stell Dir mal vor, Du baust einen Editor für eine Person. Der bekommt das Person-Objekt und das Repository mit. Der Benutzer bearbeitet die Daten und klickt auf "Speichern". Der Editor ruft dann die udpate-Methode des Repositories auf. Das Repository benachrichtigt nun alle Observer über die Änderung. So kann z. B. ein TableModel (das sich als Listener beim Repository registriert hat) feststellen, dass sich eine Zeile geändert hat und die Daten sofort aktualisieren.

Oder geht es Dir darum, wie die programmiert ist? Hier verwende ich WeakReferences zur Vermeidung von Memory Leaks. Hintergrund ist, dass der Speicher eines Objekts erst freigegeben werden kann, wenn es keine Referenz mehr auf dieses Objekt gibt. 

Stell Dir mal folgende Situation vor: Du erstellst ein Fenster, das die Personen anzeigt. Dort wird eine JTable verwendet, ein entsprechendes TableModel, das die Daten cached usw. Jetzt gehen wir mal davon aus, dass sich dieses Fenster als Listener beim Repository registriert. Damit gibt es eine Referenz vom Repository zum Fenster. Du musst also sicherstellen, dass die Registrierung unter allen Umständen aufgehoben wird, wenn das Fenster nicht mehr benötigt wird. Ansonsten bleibt die Referenz auf das Fenster bestehen und der Speicher kann nicht freigegeben werden.

Aus diesem Grund verwende ich eine WeakReference. Ist ein Objekt nur noch über schwache Referenzen erreichbar, werden diese automatisch entfernt. In dem Fall liefert WeakReference#get() null zurück. Die fireEvent-Methode entfernt die betreffenden Listener automatisch aus der Liste.

Das Gegenbeispiel findest Du in PersonsModel. Dort werden normale Referenzen verwendet. Da sich alle Fenster das gleiche Model teilen, hast Du hier einen Memory-Leak. Der Speicher wird erst freigegeben, wenn alle Referenzen auf das PersonsModel aufgehoben wurden. Dies ist aber erst der Fall, wenn das letzte Fenster geschlossen wurde. Bis dahin werden alle jemals geöffneten Fenster (bzw. die JTables und JComboBoxes) weiterhin als Listener geführt (und natürlich auch benachrichtigt); selbst wenn sie längst geschlossen wurden. Um das zu beheben müsste man einen WindowListener einbauen, der für z.B. JTable setModel(null) aufruft (dann hebt die JTable die Registrierung auf).



maGG hat gesagt.:


> Könnte das so evtl. passen?


Du brauchst nur die Location und die verwendest Du mehrfach, z. B.

```
private static final EnumMap<Location, String> SELECT = new EnumMap<>(Location.class);
    private static final EnumMap<Location, String> UPDATE = new EnumMap<>(Location.class);    
    private static final EnumMap<Location, String> INSERT = new EnumMap<>(Location.class);
```
Es geht ja nur darum, die passenden SQL-Strings zu finden. Du kannst Dir überlegen, ob Du die Location nicht in die Branch aufnimmst. Dann kannst Du in den Methoden insert und update auf den Location-Parameter verzichten. Apropos Update: dort brauchst Du die ID nicht zweimal (UPDATE ... SET id = ? ... WHERE id=?). Andererseits: wenn bei Dir die stmt.setXXXX-Methoden dann identisch sind, kannst Du die in eine Methode auslagern, dann hast Du den Code nicht doppelt.


----------



## maGG (28. Apr 2019)

mihe7 hat gesagt.:


> Das Person-Objekt hat zum Zeitpunkt des Aufrufs von add() noch keine ID. Die Methode getGeneratedId() lässt sich die von der DB beim INSERT vergebene ID zurückgeben. Anschließend wird diese verwendet ,um die ID des Person-Objekts zu setzen. D. h. nach dem Aufruf von add() hat das Person-Objekt eine ID und zwar diejenige, die von der DB vergeben wurde.


Das Problem habe ich glaube ich nicht, weil man im Editierfenster auch ein Textfield für die id hat und dieses muss gesetzet werden, zudem würde man das Problem einer fehlenden id nicht damit beheben, dass man setId() in die erste Zeile schreibt, wie bei mir?



mihe7 hat gesagt.:


> Du brauchst nur die Location und die verwendest Du mehrfach, z. B.


Ah cool, auf die Idee kam ich nicht 



mihe7 hat gesagt.:


> Apropos Update: dort brauchst Du die ID nicht zweimal (UPDATE ... SET id = ? ... WHERE id=?)


Hmm brauch ich die in meinem Fall nicht dennoch? Weil ich ein Textfeld habe, in der der Nutzer die id setzen kann.



mihe7 hat gesagt.:


> Aktuell läuft das ausschließlich über das UI-Model (PersonsModel), weil dieses sowohl TableModel, ListModel und ComboBoxModel implementiert und somit für alles verwendet wird und ich die Sache nicht weiter verkomplizieren wollte.


Die abstrakte Klasse Person Model verstehe ich noch nicht; hätte man das nicht noch in PersonRepository machen können? Oder geht es darum, dass man so immer ein Refresh bei der Ansicht der Tabelle bekommt? Das hatte ich bisher so gelöst, dass ich in dem Button die entsprechende Methode erst aufgerufen habe und die Methode für das Anzeige der Datenbank in der Tabelle einfach wiederhole.



mihe7 hat gesagt.:


> Registrierung unter allen Umständen aufgehoben wird, wenn das Fenster nicht mehr benötigt wird. Ansonsten bleibt die Referenz auf das Fenster bestehen und der Speicher kann nicht freigegeben werden.


Das verstehe ich nicht :O ist das noch was anderes, warum ich die try-ressources Methode verwende, und nich mehr pst.close() und rs.close()?


mihe7 hat gesagt.:


> WeakReference. Ist ein Objekt nur noch über schwache Referenzen


Ok, das heißt ich brauche den unbedingt für eine Event Funktion oder sollte es zu mindest benutzen?


mihe7 hat gesagt.:


> selbst wenn sie längst geschlossen wurden. Um das zu beheben müsste man einen WindowListener einbauen, der für z.B. JTable setModel(null) aufruft (dann hebt die JTable die Registrierung auf).


Puh, das verstehe ich auch noch nicht. Also ich muss alle Referenzen löschen? Ich höre davon zum ersten Mal; gilt das überall oder nur bei jTable?

Nochmal vielen Dank für die Hilfe!


----------



## mihe7 (28. Apr 2019)

maGG hat gesagt.:


> zudem würde man das Problem einer fehlenden id nicht damit beheben, dass man setId() in die erste Zeile schreibt, wie bei mir?


Wenn Du die ID setzen kannst: ja. Meist hat man das Problem, dass man die ID nicht kennt also irgendwie ermitteln muss. In meinem Beispiel wird die Ermittlung der ID der DB überlassen.



maGG hat gesagt.:


> Hmm brauch ich die in meinem Fall nicht dennoch? Weil ich ein Textfeld habe, in der der Nutzer die id setzen kann.


Dann natürlich schon. Es ist allerdings ungewöhnlich, dass a) der Benutzer die ID bestimmt und b) der Primary Key im Nachhinein geändert wird (das führt zu Problemen, wenn es Beziehungen gibt).

Auf den Rest gehe ich mal durch Beantwortung der folgenden Frage ein.


maGG hat gesagt.:


> Puh, das verstehe ich auch noch nicht. Also ich muss alle Referenzen löschen? Ich höre davon zum ersten Mal; gilt das überall oder nur bei jTable?


Der Grundsatz gilt immer: so lange ein Objekt über den Objektbaum erreichbar ist, kann der Speicher nicht freigegeben werden. Allerdings muss man meist nicht alle Referenzen löschen, die Wurzel eines Teilbaums reicht.

Dein Objektbaum wird ja durch die Ausführung Deines Programms aufgebaut. Machen wir mal ein einfaches Beispiel:

```
public static void main(String[] args) {
    List<Element> liste = new ArrayList<>();
}
```
Hier passieren mehrere Dinge. 
1. Die JVM ruft die main-Methode auf. Damit wird im Stack des main-Threads ein Stackframe für diesen Methodenaufruf angelegt
2. Es wird ein ArrayList-Objekt im Heap erzeugt.
3. Die Referenz auf dieses Objekt wird in einer lokalen Variable gespeichert, die im Stackframe der Methode abgelegt wird.
4. Beim Beenden den Methode wird der Stackframe (und damit die lokale Variable/Referenz auf das ArrayList-Objekt) entfernt.

Damit ist klar: ab 3. existiert eine Referenz auf das ArrayList-Objekt. Und so lange diese existiert, kann der Speicher nicht freigegeben werden. Nach Beendigung der Methode ist die Referenz weg (weil lokale Variable), womit der für die Liste benötigte Speicher freigegeben werden kann.

Es spielt in dem Fall auch keine Rolle, ob in der Liste noch Objekte stecken, die die Liste referenzieren: von der Wurzel des Objektbaums aus ist die Liste nicht mehr erreichbar, folglich kann die Liste freigegeben werden. 

Beispiel:

```
public static void main(String[] args) {
    List<Element> liste = new ArrayList<>();
    liste.add(new Element(liste));
}
```

Würden wir das ganze aber umschreiben:

```
public void add(Element e) {
    List<Element> liste = new ArrayList<>();
    e.setList(liste);
    liste.add(e);
}
```

Dann haben wir das Problem, dass die Liste nicht freigegeben werden kann, so lange das übergebene Element existiert.


----------



## maGG (28. Apr 2019)

Achso, und mit liste.add(new Element(liste)); würde ich eine neue Referenz haben und dadurch würde ich nicht diesselbe Referenz benutzen, die ja erst nach Beendigung der Methode wieder verfügbar wäre? Und wenn ich die add-Methode in einer neuen Methode aufrufe ginge es ja auch, hast du deswegen extra noch eine PersonModel Klasse eingeführt?
Oder ist der Grund für PersonModel und PersonView eher die Visualisierung des Listeners geschuldet?

Wo verwendest du denn den Renderer bzw. wie benutzen ich den? Wenn ich jetzt List<Branch>findAll(Location where) oder List<String>findLabel(Location where) aufrufe und damit die ComboBox befüllen möchte. Wie würde ich den Renderer bei findAll benutzen?
Wenn ich dich richtig verstande haben war die Idee ja folgende: Anstatt mir nur Label als String aus der Db zu holen, hole ich mir doch lieber alle Werte und mit einem Renderer dann nur das Label hinzu. So könnte ich evtl. in der ActionPerformed Methode des Buttons nicht noch zusätzlich eine set Methode für die Übergabe Parameter für die Writer Methode später, oder habe ich das falsch verstanden?


----------



## mihe7 (28. Apr 2019)

maGG hat gesagt.:


> Achso, und mit liste.add(new Element(liste)); würde ich eine neue Referenz haben und dadurch würde ich nicht diesselbe Referenz benutzen, die ja erst nach Beendigung der Methode wieder verfügbar wäre?


Ja, mit liste.add(new Element(liste)); würdest Du ein neues Element-Objekt erzeugen, das intern die lokale Liste referenziert  (davon gehen wir zumindest aus) und die Referenz auf das Element würdest Du in der Liste speichern (Element und Liste referenzieren sich also gegenseitig). Nach Beendigung der Methode kann in dem Fall liste jedoch freigegeben werden, da die Referenz auf die liste nur in der Liste existiert (das Element, das die Liste referenziert, wird ja nur über die Liste referenziert).

In der zweiten Methode kommt das Element "von außerhalb" (via Parameter) und es wird eine Referenz auf die lokal erstellte Liste gesetzt. Wenn also das Element außerhalb weiterhin referenziert wird, dann kann die lokal erstellte Liste nicht freigegeben werden. 

Deutlich wird es z. B. so:


```
class TestObjekt {
    private Element element = new Element();

    public void add() {
        List<Element> liste = new ArrayList<>();
        liste.add(element);
        element.setList(liste);
    }

    public void show() {
        List<Element> imElementReferenzierteListe = element.getList();
        // ...
    }

    public void clear() {
        element = null;
    }
}
```

Nehmen wir jetzt folgenden Code an:

```
TestObjekt a = new TestObjekt();
a.add();
```

Dann hast Du folgenden Objektbaum: 

```
-->a ---> element <--> liste
```
So lange also eine Referenz auf element existiert, kann die Liste nicht freigegeben werden, denn Du könntest ja irgendwann aufrufen:

```
a.show();
```
Und dort wird die Liste wieder benötigt. 

Würdest Du dagegen a.clear(); aufrufen, würde die Referenz auf das Element entfernt:

```
-->a       element <--> liste
```
Damit ist im Objektbaum nur noch a erreichbar, element und liste jedoch nicht mehr. Und erst jetzt kann auch die liste freigegeben werden.



maGG hat gesagt.:


> hast du deswegen extra noch eine PersonModel Klasse eingeführt?
> Oder ist der Grund für PersonModel und PersonView eher die Visualisierung des Listeners geschuldet?


Das PersonModel implementiert einfach TableModel und ComboBoxModel. Damit werden die JTable/JComboBox nicht mehr aktiv gefüllt. Das PersonModel dient einfach als Adapter, um das Repository an eine JTable/JComboBox anbinden zu können, ohne irgendwelche fill-Methoden aufrufen zu müssen. 



maGG hat gesagt.:


> Wo verwendest du denn den Renderer bzw. wie benutzen ich den? Wenn ich jetzt List<Branch>findAll(Location where) oder List<String>findLabel(Location where) aufrufe und damit die ComboBox befüllen möchte. Wie würde ich den Renderer bei findAll benutzen?


Man gibt der JComboBox einfach an, mit welchem Renderer sie die Elemente darstellen solll (s. JComboBox#setRenderer). Sprich:

```
BranchRenderer renderer = new BranchRenderer();
JComboBox filialenInland = new JComboBox(...);
JComboBox filialenAusland = new JComboBox(...);
filialenInland.setRenderer(renderer);
filialenAusland.setRenderer(renderer);
```



maGG hat gesagt.:


> Wenn ich dich richtig verstande haben war die Idee ja folgende: Anstatt mir nur Label als String aus der Db zu holen, hole ich mir doch lieber alle Werte und mit einem Renderer dann nur das Label hinzu. So könnte ich evtl. in der ActionPerformed Methode des Buttons nicht noch zusätzlich eine set Methode für die Übergabe Parameter für die Writer Methode später, oder habe ich das falsch verstanden?


Genau, die Idee ist, mit den Objekten des Problembereichs zu arbeiten (wofür haben wir OO), statt auf ggf. primitive Einzelteile zurückzugreifen. Du lädst also nicht Labels aus der DB, sondern Filialen. Und die Filialen kannst Du dann sowohl für die JComboBox verwenden (der Renderer sorgt dafür, dass die Anzeige passt), als auch z. B. für die Tabelle.

Genauso wie im Beispiel  

Ein Vorteil ist: der Benutzer wählt jetzt nicht nur ein Label aus, sondern eben eine Filiale. Damit hast Du unmittelbar nach der Auswahl ein Branch-Objekt und damit sämtliche Eigenschaften/Methoden des selben zur Verfügung.


----------



## maGG (29. Apr 2019)

Ok, danke! Ich habe die Klasse PersonObjekt jetzt mal für Branch angelegt, jedoch tue ich mich grad bei der Logik der zu übergehenden Parameter in den Methoden add und update schwer:

Hier verstehe ich noch nicht ganz, warum wir List<Branch> data verwenden und wie ich ein Objekt der Klasse Branch nun diesem hinzufügen können.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package ModelClasses;

import ObjectClasses.Branch;
import RepositoryClasses.BranchRepository;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.ComboBoxModel;
import javax.swing.ListModel;
import javax.swing.event.EventListenerList;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;

/**
 *
 * @author david
 */
public class BranchModel extends AbstractTableModel implements TableModel, ListModel<Branch>, ComboBoxModel<Branch>{
 
    private EventListenerList listeners = new EventListenerList();

    private static final String[] COLUMNS =
    {"Label", "Strasze", "PLZ Ort", "Ort", "Vorwahl", "Tel.Htm.Anfang", "Tel.Txt.Anfang", "Tel.Ende", "Fax.Txt.Anfang", "Fax.Txt.Ende"};

    private BranchRepository branches;
    private BranchRepository.Location location;
    private List<Branch> data;
    private Object selected;
    
    public BranchModel(BranchRepository branches, BranchRepository.Location location) {
        this.branches = branches;
        this.location = location;
        try {
            data = branches.findAll(location);
        } catch (SQLException e) {
            data = new ArrayList<>();
            e.printStackTrace();
        }
    }   
    
    public void add(BranchRepository.Location where, Branch branch) {
        try {
            branches.insertBranch(where, branch);
            int ix = getSize();
            data.insertBranch(where, branch); //hier wird ein Fehler angezeigt: cannot find symbol
            fireTableRowsInserted(ix, ix);
            fireIntervalAdded(ix, ix);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    } 
    
    public void update(BranchRepository.Location where, Branch branch) {
        try {
            branches.updateBranch(where, branch);
            int ix = data.indexOf(branch);
            fireContentsChanged(ix, ix);
            fireTableRowsUpdated(ix, ix);
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    }   

    protected void fireIntervalAdded(int index0, int index1){
        ListDataEvent event = new ListDataEvent(this, ListDataEvent.INTERVAL_ADDED, index0, index1);
        for (ListDataListener l : listeners.getListeners(ListDataListener.class)) {
            l.intervalAdded(event);
        }
    }

    protected void fireIntervalRemoved(int index0, int index1){
        ListDataEvent event = new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, index0, index1);
        for (ListDataListener l : listeners.getListeners(ListDataListener.class)) {
            l.intervalRemoved(event);
        }
    }

    protected void fireContentsChanged(int index0, int index1){
        ListDataEvent event = new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, index0, index1);
        for (ListDataListener l : listeners.getListeners(ListDataListener.class)) {
            l.contentsChanged(event);
        }
    }       
    
    //implemented abstract methodes
    
    @Override
    public int getSize(){
            return data.size();
    }

    @Override
    public Branch getElementAt(int index){
            return data.get(index);
    }

    @Override
    public void addListDataListener(ListDataListener l){
            listeners.add(ListDataListener.class, l);
    }

    @Override
    public void removeListDataListener(ListDataListener l){
            listeners.remove(ListDataListener.class, l);
        }
    
    @Override
    public int getRowCount(){
            return getSize();
    }

    @Override
    public int getColumnCount(){
            return COLUMNS.length;
        }
    
        @Override
        public String getColumnName(int column){
            return COLUMNS[column];
        }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex){
        Branch branch = getElementAt(rowIndex);
            switch (columnIndex){
                case 0: return branch.getId();
                case 1: return branch.getVorwahl();
                case 2: return branch.getTelStartNum();
                case 3: return branch.getLabel();
                case 4: return branch.getStrasze();
                case 5: return branch.getPlzort();       
                case 6: return branch.getOrt();       
                case 7: return branch.getTelStartTxt();
                case 8: return branch.getTelEndDefault();       
                case 9: return branch.getFaxStartTxt() ;       
                case 10: return branch.getFaxEndTxt() ;       
                default: return null;
            }
    }
        
    @Override
    public void setSelectedItem(Object anItem){
            selected = anItem;
    }

    @Override
    public Object getSelectedItem(){
            return selected;
    }
        
}
```

Bei PersonView hast du mit einer setCurrent Methode direkt in dem Element gearbeitet, bei mir in Netbeans funktioniert das alles ein bisschen anders. Daher weiß ich leider immer noch nicht wie ich z.B: bei jComboBoxFilialenDeutschland den Renderer anwende. 

Meine ComboBox sieht gerade so aus:

```
jComboBoxFilialenDeutschland = new javax.swing.JComboBox<>();

jComboBoxFilialenDeutschland.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Bitte auswählen" }));

jComboBoxFilialenDeutschland.addActionListener(new java.awt.event.ActionListener() {
    public void actionPerformed(java.awt.event.ActionEvent evt) {
        jComboBoxFilialenDeutschlandActionPerformed(evt);
    }
});

// Code adding the component to the parent container - not shown here
```

Könnte ich da vielleichet auch den Teil von PersonView reinpacken und Methoden wie setCurrent in meiner Hauptklasse lassen?


----------



## mihe7 (29. Apr 2019)

Arbeitest Du mit der UI-Builder von NetBeans? Es ist schon ewig her, dass ich den benutzt habe aber ich weiß, dass man das dem UI-Editor beibrignen kann/konnte. Du kannst in den Properties Code eingeben. Und es gibt auch Pre-/Post-Init-Code. Genaueres kann ich Dir gerade nicht sagen, dazu müsste ich den Editor erst anschmeißen. Falls Du da Hilfe brauchst, sag Bescheid, dann werfe ich den Editor an.



maGG hat gesagt.:


> Hier verstehe ich noch nicht ganz, warum wir List<Branch> data verwenden


Als Cache, damit wir nicht ständig auf der DB rumfuhrwerken.

Vereinfachen wir das ganze mal, indem wir uns auf die JComboBox konzentrieren (=> ComboBoxModel) und Scrolling ignorieren.

Du musst Dir das so vorstellen: die JComboBox will die Liste mit den Einträgen anzeigen. Dazu fragt sie beim ComboBoxModel nach, wie viele Einträge es gibt, indem es die getSize()-Methode des ComboBoxModels aufruft. Anschließend ruft sie die betreffenden Elemente vom ComboBoxModel ab (getElementAt(index)), gibt das jeweilige Element an den gesetzten Renderer weiter, der dann eine UI-Komponente zurückliefert. Die JComboBox ruft dann die paint-Methode der UI-Komponente auf, um das Teil zu zeichnen.

Der wichtige Punkt dabei ist: der JComboBox ist es völlig egal, wo die Daten herkommen und welche Daten es sind. Einzige Voraussetzung ist, dass das Model-Objekt der JComboBox das ComboBoxModel-Interface implementiert. Den Punkt muss man verstanden haben, wenn man mit Swing (und anderen UI-Frameworks) Freude haben will (Google mal nach MVC Pattern).

Aus Sicht der JComboBox könnte also auch das Repository das ComboBoxModel direkt implementieren (getSize() könnte z. B. das Ergebnis von "SELECT count(*) ..." zurückgeben) . Das Repository würde dabei aber ständig auf die DB zugreifen, was die Sache sehr langsam machen würde. Daher verwenden wir eine List als Cache und eine separate Klasse weil die Bereitstellung von Daten im "JComboBox-Format" nichts mit der DB zu tun und somit nichts im Repository verloren hat.



maGG hat gesagt.:


> data.insertBranch(where, branch); //hier wird ein Fehler angezeigt: cannot find symbol


Ja, die insertBranch-Methode des Repositories gibt es in der List nicht  Änder einfach data zu repository und füg eine zweite Zeile `data.add(branch);` hinzu. Die insertBranch-Methode sorgt dafür, dass das Repository (also die DB) aktualisiert wird, die data.add()-Methode, dass der Cache aktualisiert wird.


----------



## maGG (30. Apr 2019)

Vielen Dank für die Hilfe! Ich habe gestern länger darüber nachgedacht und glaube ich bin jetzt grad an nem Punkt, wo es mehr um Logik geht bzw. wie ich es aufbaue. Dein sehr schönes Beispiel, wie ich es machen könnte lässt sich nicht ganz auf meinen Anwendungsfall anwenden, zu mindest nicht 1:1. 

Ich habe insgesamt 5 Tabellen in der Datenbank:
Filialen_Deutschland(11 Parameter), Filialen_Ausland(11 Parameter), Firmierungen(2 Parameter), Titel(3 Parameter), Funktionen (2 Parameter)

Dementsprechend habe ich auch 5 Tabellen bzw. 5 Seiten bei einem TabbedPane.

Auf den Tabellen habe ich jeweils zwei Methoden:

```
private void jTableTitelKeyReleased(java.awt.event.KeyEvent evt) {
    if (evt.getKeyCode() == KeyEvent.VK_UP || evt.getKeyCode() == KeyEvent.VK_DOWN) {
    ...
    }
}
```


```
private void jTableTitelMouseClicked(java.awt.event.MouseEvent evt) {
    ...
}
```

Dann habe ich noch 5 ComboBox(en), für die jeweiligen Tabellen, wo nur das Label angezeigt wird. Von der Logik her müsste ich doch dann in meiner setCurrent Methode die Parameter mit Werten belegen, die ich später in der Writer Methode brauche und die in der Vorschau Methode angezeigt werden.

Zum Beispiel ich setzen bei der ComboBox GermanBranches jetzt das Label Frankfurt am Main, dann müsste die setCurrent Methode alle Parameter setzen und danach wird die Preview Methode aufgerufen und das Vorschaufenster aktualisiert.

Wenn ich jetzt in einer der Tabellen etwas editieren möchte, dann setzt er bei einem Action Event die entsprechenden Textfelder mit Werten. Bei Speicher und Update holt er sich die Werte. Das heißt ich hab doch quasi eine Trennng zwischen ComboBox und Table, oder sehe ich das falsch?

Ich kriegs gedanklich nicht hin, deine wohlüberlegte Struktur darauf anzuwenden. Für die Repository Klassen habe ich das Ganze noch hinbekommen, aber wie ich die Model und dann die View Klassen darauf anwenden kann sprengt gerade etwas meine Vorstellungskraft. Leider kann ich nicht mit Parameter wie JComboBoxFilialenDeutschland nicht außerhalb der initComponent Methode arbeiten, sobald ich sie außerhalb benutze habe ich kein Zugriff darauf, was hieße ich müsste für jede Action Event Methode quasie densleben Code nochmal reinschreiben. Ein Teil erspare ich mir durch eine Repository Klasse, aber so schön wie das bei dir ist krieg ich das irgendwie nicht hin, oder mir fällt zu mindest keine Lösung ein.

Ich grüble mal weiter, vielleicht fällt mir ja doch noch was ein


----------



## mihe7 (30. Apr 2019)

Kannst Du mir mal Deinen aktuellen Code schicken, damit ich das nachvollziehen kann, was Du meinst? 

Ein Punkt ist völlig klar: 1:1 kannst Du das nicht übernehmen, da der Code bei mir Quick & Dirty davon ausgeht, dass es ein einziges PersonsModel gibt. Das kannst Du so nicht machen, weil Du mehrere JComboBoxen hast, und jede JComboBox "ihr eigenes" setCurrent/getCurrent braucht.

Zwei Dinge verstehe ich nicht:


maGG hat gesagt.:


> Von der Logik her müsste ich doch dann in meiner setCurrent Methode die Parameter mit Werten belegen, die ich später in der Writer Methode brauche und die in der Vorschau Methode angezeigt werden.


und


maGG hat gesagt.:


> bei der ComboBox GermanBranches jetzt das Label Frankfurt am Main, dann müsste die setCurrent Methode alle Parameter setzen und danach wird die Preview Methode aufgerufen und das Vorschaufenster aktualisiert.


Wenn Du mit Objekten arbeitest, wird nicht ein "Label" Frankfurst am Main gesetzt sondern die komplette Filiale. Aber, lass mal den Code sehen.


----------



## maGG (1. Mai 2019)

mihe7 hat gesagt.:


> Kannst Du mir mal Deinen aktuellen Code schicken, damit ich das nachvollziehen kann, was Du meinst?


Klar, ich weiß jedoch nicht ob du dich zurecht finden wirst; ich habe vieles wieder auskommentiert und Code noch drin, der am Ende raus soll ... es ist ein Chaos im Moment 

Ich habe gestern noch rumprobiert; ich habe in meine BranchView Klasse die Hauptklasse importiert (k.A. ob man das machen sollte) und dann die einzelnen Textfelder im GUI Builder auf public gesetzt. Jetzt habe ich zwar Zugriff darauf, jedoch hab ich das static Problem; "Non-static variable cannot be referenced from a static context" Könnte das an der Run Methode liegen? (public static void main (String args[]))


----------



## maGG (1. Mai 2019)

update: habe das static Problem gelöst; habe folgendes gemacht:
import Interface.Main
private Main main;
...
main.jTextfield.setText(...);


----------



## maGG (1. Mai 2019)

Meine Klasse sieht jetzt so aus:

```
package ModelClasses;

import ObjectClasses.Branch;
import RepositoryClasses.BranchRepository;
import UserInterface.Main;

public class BranchViewDomestic {
    
    private BranchModel branches;
    private Branch current;
    private Main main;
    
    public BranchViewDomestic(BranchModel branches) {
        this.branches = branches;
    }   

    private void setCurrent(Branch branch) {
        this.current = branch;
        boolean valid = current != null;
        if (valid) {
            main.jTextFieldF1.setText("" + current.getId());
            main.jTextFieldF2.setText(current.getLabel());
            main.jTextFieldF3.setText(current.getStrasze());
            main.jTextFieldF4.setText(current.getPlzort());       
            main.jTextFieldF5.setText(current.getOrt());
            main.jTextFieldF6.setText("" + current.getVorwahl());
            main.jTextFieldF7.setText("" + current.getTelStartNum());
            main.jTextFieldF8.setText(current.getTelStartTxt());
            main.jTextFieldF9.setText("" + current.getTelEndDefault());
            main.jTextFieldF10.setText(current.getFaxStartTxt());
            main.jTextFieldF11.setText(current.getFaxEndTxt());
        } else {
            main.jTextFieldF1.setText("");
            main.jTextFieldF2.setText("");
            main.jTextFieldF3.setText("");
            main.jTextFieldF4.setText("");       
            main.jTextFieldF5.setText("");
            main.jTextFieldF6.setText("");
            main.jTextFieldF7.setText("");
            main.jTextFieldF8.setText("");
            main.jTextFieldF9.setText("");
            main.jTextFieldF10.setText("");
            main.jTextFieldF11.setText("");
        }
        main.jTextFieldF1.setEnabled(valid);
        main.jTextFieldF2.setEnabled(valid);
        main.jTextFieldF3.setEnabled(valid);
        main.jTextFieldF4.setEnabled(valid);     
        main.jTextFieldF5.setEnabled(valid);
        main.jTextFieldF6.setEnabled(valid);
        main.jTextFieldF7.setEnabled(valid);
        main.jTextFieldF8.setEnabled(valid);
        main.jTextFieldF9.setEnabled(valid);
        main.jTextFieldF10.setEnabled(valid);
        main.jTextFieldF11.setEnabled(valid);
        main.jButtonUpdateA1.setEnabled(valid);
        main.jButtonSaveA1.setEnabled(valid);
        main.jButtonDeleteA1.setEnabled(valid);   
    }

    private void save() {
        if (this.current == null) {
            return;
        }
        current.setId(Integer.parseInt(main.jTextFieldF1.getText()));
        current.setLabel(main.jTextFieldF2.getText());
        current.setStrasze(main.jTextFieldF3.getText());
        current.setPlzort(main.jTextFieldF4.getText());
        current.setOrt(main.jTextFieldF5.getText());
        current.setVorwahl(Integer.parseInt(main.jTextFieldF6.getText()));
        current.setTelStartNum(Long.parseLong(main.jTextFieldF7.getText()));
        current.setTelStartTxt(main.jTextFieldF8.getText());
        current.setTelEndDefault(Integer.parseInt(main.jTextFieldF9.getText()));
        current.setFaxEndTxt(main.jTextFieldF10.getText());
        current.setFaxEndTxt(main.jTextFieldF11.getText());
        if (current.getId() == 0L) {
            branches.add(BranchRepository.INSERT<Location.DOMESTIC>, current); //<- falsch
            //BranchRepository.DOMESTIC geht aber ebenfalls nicht, da ich sonst keine Unterscheidung habe, ob es sich um LOCATION, UPDATE, oder INSERT als EnumMap handelt
        } else {
            branches.update(BranchRepository.INSERT<Location.DOMESTIC>, current); // falsch
        }
    }   
}
```


----------



## mrBrown (1. Mai 2019)

Sind die Kommentare im Code die Fragen?

Falls ja:

Die Aufrufe sollten `branches.add(Location.DOMESTIC, current);[icode] bzw [icode]branches.update(Location.DOMESTIC, current);` sein. Ob es SELECT, UPDATE, oder INSERT ist, ist durch den Methodenaufruf klar - die Location soll nicht zur Unterscheidung dessen dienen, sondern eben nur als Location.





Und ein paar kleine Tipps am Rande: 
Zieh die Location mit in den Branch rein. Auch wenn das nicht als Wert in der Tabelle steht, sondern nur durch die Tabelle vorgegeben wird, kannst du das trotzdem explizit in deinem Model abbilden.
Die Aufrufe sähen dann nur noch so aus: `branches.add(current);`, und keinerlei Code außer des Repoitorys muss wissen, dass die aus zwei verschiedenen Tabellen kommen.

Wenn du die ID zum Unterscheiden zwischen neuen und bearbeiteten Einträgen nutzt, bietet es sich an, `null` für neue Einträge zu nutzen, das entspräche dann keiner ID, und nicht einer Magic Number - kann durchaus Fehler unwahrscheinlicher machen.
Und wenn die spezielle ID nur für zwei unterschiedliche Methodenaufrufe des Repos nutzt, kannst du die Unterscheidung dort reinziehen, die ist ja nur für die unterschiedlichen Queres relevant, also nur für Repository-Interna.

Das würde im Client-Code zu diesem: `branches.save(current);` führen, statt diesem: 
	
	
	
	





```
if (current.getId() == 0L) {
            branches.add(BranchRepository.INSERT<Location.DOMESTIC>, current); //<- falsch
            //BranchRepository.DOMESTIC geht aber ebenfalls nicht, da ich sonst keine Unterscheidung habe, ob es sich um LOCATION, UPDATE, oder INSERT als EnumMap handelt
        } else {
            branches.update(BranchRepository.INSERT<Location.DOMESTIC>, current); // falsch
        }
```


----------



## maGG (1. Mai 2019)

Danke!

Meinst du, dass ich nur das Enum in Branch ziehen soll, oder das Enum, die EnumMaps und static {}?


mrBrown hat gesagt.:


> Wenn du die ID zum Unterscheiden zwischen neuen und bearbeiteten Einträgen nutzt, bietet es sich an, `null` für neue Einträge zu nutzen, das entspräche dann keiner ID, und nicht einer Magic Number - kann durchaus Fehler unwahrscheinlicher machen.


Wie kann ich mir das vorstellen? Würdest du mir raten, das Texfeld zum setzen einer Id zu entfernen? Ich dachte halt in der Tabell ist es als Visualisierung gut, da man so direkt weiß, um was es sich handelt.


----------



## mrBrown (1. Mai 2019)

maGG hat gesagt.:


> mrBrown hat gesagt.:
> 
> 
> > Ob es SELECT, UPDATE, oder INSERT ist, ist durch den Methodenaufruf klar
> ...



Es gibt aktuell drei Methoden, und jede Methode nutzt nur genau eine der drei Maps:
In add musst du immer INSERT nutzen, in update immer UPDATE und in find (falls die noch so heißt) immer LOCATION (welche man vom Namen her den anderen anpassen sollte).

Du bist niemals an dem Punkt, dass zb add aufgerufen wurde, und du dann noch wissen musst, ob dazu INSERT oder UPDATE gehört - das ist ja schon durch das add klar.



maGG hat gesagt.:


> Wie kann ich mir das vorstellen? Würdest du mir raten, das Texfeld zum setzen einer Id zu entfernen? Ich dachte halt in der Tabell ist es als Visualisierung gut, da man so direkt weiß, um was es sich handelt.


Das sichtbare Textfeld hat nur am Rande mit dem Inhalt deines Modells zu tun 
Das Textfeld kann ruhig so bleiben, ob es für den Nutzer allerdings klar ist, dass '0' als ID bedeutet, dass ein neuer Eintrag eingefügt wird?

Im Model ist es aber of sinnvoll, nicht 0 als Zeichen für "keine ID" zu nutzen, sondern eben null. 
0 ist aus Datenbanksicht in den meisten Fällen eine valide ID, wenn du sehr viel Pech hast, steht in der Datenbank irgendwann ein Datensatz mit ID 0, und deine Anwendung könnte den niemals korrekt behandeln.
null dagegen macht das im Model eindeutiger (null=kein Wert => neuer Eintrag, irgendeine Zahl => bestehender Eintrag), und in der Datenbank uU auch, da NULL keine ID.


----------



## maGG (1. Mai 2019)

Achso, also das ich dann anstatt findAll(Location where) habe, dann findAllDomestic() und findAllForeign()?

```
public List<Branch> findAll(Location where) throws SQLException{
        String sql = LOCATION.get(where);
        try(PreparedStatement pst = conn.prepareStatement(sql)){
            try(ResultSet rs = pst.executeQuery()){
                List<Branch> result = new ArrayList<>();
                while(rs.next()){
                    result.add(readBranch(rs));
                }
                return result;            
            }
        }
    }
   
    public List<Branch> findAllDomestic() throws SQLException{
        String sql = LOCATION.get(Branch.Location.DOMESTIC);
        try(PreparedStatement pst = conn.prepareStatement(sql)){
            try(ResultSet rs = pst.executeQuery()){
                List<Branch> result = new ArrayList<>();
                while(rs.next()){
                    result.add(readBranch(rs));
                }
                return result;            
            }
        }
    }
   
    public List<Branch> findAllForeign() throws SQLException{
        String sql = LOCATION.get(Branch.Location.FOREIGN);
        try(PreparedStatement pst = conn.prepareStatement(sql)){
            try(ResultSet rs = pst.executeQuery()){
                List<Branch> result = new ArrayList<>();
                while(rs.next()){
                    result.add(readBranch(rs));
                }
                return result;            
            }
        }
    }
```
So? Gut kann ich auch machen, führt halt dann zu einer Verdoppelung an Methoden in meiner Repository Klasse und ich brauche auch zwei BranchModel Klassen dann, hmm ...


----------



## mrBrown (1. Mai 2019)

maGG hat gesagt.:


> Achso, also das ich dann anstatt findAll(Location where) habe, dann findAllDomestic() und findAllForeign()?


Nein, wie kommst du darauf? 

Ah, dein Beitrag auf den ich geantwortet hab ist mittlerweile völlig anders...

Wenn du dich "aufs Location in Branch ziehen" beziehst:

Branch wird einfach um ein Feld `Location location` mit Get/Settern ergänzt, die EnumMaps bleiben davon unberührt der Enum auch, außer dass man ihn in das package ziehen kann (sodass er keine "nested class" mehr ist).

findAll bleibt genau so wie bisher: `List<Branch> findAll(Location where)`, du willst ja weiterhin alle zu einer Location.


Die anderen müsstest du in der aktuellen Version mal zeigen, damit man dazu was sagen könnte.
Aber angenommen update sah bisher so aus: `void update(Location where, Branch branch) {...}`, dann wäre es danach so: 
	
	
	
	





```
void update(Branch branch) {
   Location where = branch.getLocation();
   //und hier der Rest, der bisher in der Methode stand
}
```


Mehr Methoden brauchst du damit keinesfalls, es wird nur an anderen Stellen etwas weniger Code.


----------



## maGG (1. Mai 2019)

Achso, danke ... das macht jetzt etwas mehr Sinn, bei mir bleibt jetzt aber dennoch eine Frage offen:

```
public void updateBranch(Branch branch) throws SQLException{
        Location where = branch.getLocation();
        String sql = UPDATE.get(where);
        try(PreparedStatement pst = conn.prepareStatement(sql)){ 
            pst.setInt(1, branch.getId()); //id
            pst.setString(2, branch.getLabel()); //LABEL
            pst.setString(3, branch.getStrasze()); //STRASZE
            pst.setString(4, branch.getPlzort()); //PLZ_ORT
            pst.setString(5, branch.getOrt()); //ORT
            pst.setInt(6, branch.getVorwahl()); //TEL_LAND
            pst.setLong(7, branch.getTelStartNum()); //TEL_ANFANG_INT
            pst.setString(8, branch.getFaxStartTxt()); //TEL_ANFANG_TXT
            pst.setInt(9, branch.getTelEndDefault()); //TEL_ENDE_DEFAULT
            pst.setString(10, branch.getFaxStartTxt()); //FAX_ANFANG_TXT
            if(branch.getFaxEndTxt().equals("")){
                pst.setObject(11, null);
            }else{
                pst.setString(11, branch.getFaxEndTxt()); //FAX_ENDE_TXT
            }
            pst.setInt(12, branch.getId()); //id
            pst.executeUpdate();
            fireUpdated(branch);
        }   
    }
```
Mit "Location where = branch.getLocation()" bekomme ich doch jetzt DOMESTIC und FOREIGN zurück oder? Aber ich müsste ja eigentlich einen der beiden Werte setzen, oder?


----------



## mihe7 (1. Mai 2019)

maGG hat gesagt.:


> Aber ich müsste ja eigentlich einen der beiden Werte setzen, oder?


Ja, in findAll()


----------



## mrBrown (1. Mai 2019)

maGG hat gesagt.:


> Mit "Location where = branch.getLocation()" bekomme ich doch jetzt DOMESTIC und FOREIGN zurück oder?


Ja.



maGG hat gesagt.:


> Aber ich müsste ja eigentlich einen der beiden Werte setzen, oder?


Ja. In den find-Methoden und überall da, wo du neue Brasch-Objekte erstellst


----------



## mihe7 (1. Mai 2019)

Vielleicht noch kurz zur Erklärung: die Location ergibt sich implizit daraus, in welcher Tabelle der Satz steht. Umgekehrt dient die Location eben genau dafür, die Tabelle zu bestimmen, in die eingefügt wird bzw. wo aktualisiert werden muss.


----------



## maGG (1. Mai 2019)

Achso ok, das heißt ich müsste mit einer Füllmethode dann die Methode findAll Methode aufrufen, aber ich habe ja ein TabbedPane, wo ich zwischen den Tabellen wechsle... ne ich kapiers noch nicht ganz  gut das ihr mein Programm besser versteht als ich 

Du hattest mal angesprochen, das es sinn macht, die grafischen Sachen zu trennen. Bei deinem Beispiel hast du in der Main Klasse fast nix drin. Meine Main ist ja rieseig. Jetzt benutze ich den GUI Builder, aber es würde sinn machen zu mindest das zweite Fenster auszulagern, oder? Im Moment lädt das Programm beim Start immer das Hauptfenster und das zweite Fenster zum Editieren. Falls man die DB aber gar nicht editieren möchte, macht es das Programm doch ziemlich langsam, oder? Wäre es dann nicht schlauer, das Ganze auf 2 JFrame Klassen aufzuteilen? Also das Hauptfenster "Main" und meinetwegen "EditData"


----------



## mihe7 (1. Mai 2019)

maGG hat gesagt.:


> Achso ok, das heißt ich müsste mit einer Füllmethode dann die Methode findAll Methode aufrufen, aber ich habe ja ein TabbedPane, wo ich zwischen den Tabellen wechsle... ne ich kapiers noch nicht ganz  gut das ihr mein Programm besser versteht als ich


Naja, im Inlandspane rufst Du halt (EDIT: ggf. indirekt) findAll(Location.DOMESTIC) und im Auslandspane findAll(Location.FOREIGN) auf 



maGG hat gesagt.:


> Du hattest mal angesprochen, das es sinn macht, die grafischen Sachen zu trennen.


Ja. Wir trennen ja schon die ganze Zeit 



maGG hat gesagt.:


> Jetzt benutze ich den GUI Builder, aber es würde sinn machen zu mindest das zweite Fenster auszulagern, oder?


Ich hab es schon mal angesprochen: Du kannst den GUI-Builder auch dazu verwenden, um einzelne Panels zu designen. Dann landen die Panels in separaten .java-Files.

Beispiel: Du willst einen JFrame mit einem TabbedPane mit zwei Tabs haben. Dann entwirfst Du jeden Tab separat als JPanel (zwei .java-Files) und außerdem den JFrame mit einem TabbedPane. Dan kannst Du, wenn ich mich richtig entsinne, die Panels in das TabbedPane ziehen.


----------



## maGG (2. Mai 2019)

Ok, habe mein zweites Fenster jetzt ausgelagert in die Klasse "EditData" und dort jeweils 5 JPanels eingefügt. Ich wollte jetzt mal versuchen eine der Tabellen zu füllen und hab es natürlich erst mal verkackt 

Ich bekomme eine NullPointer als Meldung. Hmm was habe ich falsch gemacht? Ich finde das echt mega schwer, diese ganze Verschachtelung ...  stimmt wenigstens der Ansatz/Idee in etwa? 

Oder brauch ich dafür ein Renderer?


```
public EditData() {
        initComponents();
        setLocationRelativeTo(null);
        setResizable(false);
        try {
            br.findAll(Location.DOMESTIC);
        } catch (SQLException ex) {
            ex.printStackTrace();
            JOptionPane.showMessageDialog(null, ex);
        }
        bvd.show();     
    }

    BranchRepository br;
    BranchViewDomestic bvd;
//...
```


```
public class BranchViewDomestic {
  
    private final BranchModel branches;
    private Branch current;
    private PanelBranchesDomestic panel;
  
    public BranchViewDomestic(BranchModel branches) {
        this.branches = branches;
    } 

    public void show() {
        branches.show();
        panel.jTableFilialen_Deutschland.setModel(branches);
    }
//...
```


```
public class BranchModel extends AbstractTableModel implements TableModel, ListModel<Branch>, ComboBoxModel<Branch>{

    private BranchRepository branches;
    private Branch.Location location;
    private List<Branch> data;
    private Object selected;
  
    public BranchModel(BranchRepository branches, Branch.Location location) {
        this.branches = branches;
        this.location = location;
        try {
            data = branches.findAll(location);
        } catch (SQLException e) {
            data = new ArrayList<>();
            e.printStackTrace();
        }
    }
  
    public BranchModel show() {
        BranchModel bm = new BranchModel(branches, location);
        return bm;
    }
//...
```


```
public class BranchRepository {
  
//...
    private final Connection conn;
  
    public BranchRepository() {
        this.conn = null;
    }
  
    public BranchRepository(Connection conn) {
        this.conn = conn;
    }
//... 
    public List<Branch> findAll(Location where) throws SQLException {
        String sql = LOCATION.get(where);
        try(PreparedStatement pst = conn.prepareStatement(sql)) {
            try(ResultSet rs = pst.executeQuery()){
                List<Branch> result = new ArrayList<>();
                while(rs.next()) {
                    result.add(readBranch(rs));
                }
                return result;           
            }
        }
    }
//...
```


----------



## mihe7 (2. Mai 2019)

maGG hat gesagt.:


> Ich bekomme eine NullPointer als Meldung.


In der Meldung steht genau, an welcher Stelle...


----------



## mihe7 (2. Mai 2019)

Ich sehe gerade, dass Du es etwas zu kompliziert machst. Meine View-Klassen entsprechen Deinen NetBeans-UI-Builder-Klassen. Das, was Du aktuell in BranchViewDomestic hast, kannst Du direkt in PanelBranchDomestic umsetzen; auf die View-Klasse kannst Du dann verzichten.


----------



## maGG (2. Mai 2019)

mihe7 hat gesagt.:


> Ich sehe gerade, dass Du es etwas zu kompliziert machst. Meine View-Klassen entsprechen Deinen NetBeans-UI-Builder-Klassen. Das, was Du aktuell in BranchViewDomestic hast, kannst Du direkt in PanelBranchDomestic umsetzen; auf die View-Klasse kannst Du dann verzichten.


Kannst du das etwas genauer beschreiben?


```
import ModelClasses.BranchModel;

public class PanelBranchesDomestic extends javax.swing.JPanel {

    public PanelBranchesDomestic() {
        initComponents();
        branches.show(); // hier weiß ich nicht wann überhaupt Location eingesetzt wird, glaube nämlich gar nicht
        jTableFilialen_Deutschland.setModel(branches); 
    }
  
    BranchModel branches;

}
```

hier bekomme ich wieder eine NullPointerException bei der Zeile, wo mein Kommentar ist -> branches.show()


----------



## mihe7 (2. Mai 2019)

Deine View-Klasse mappt zwischen Model und Panel. Das kannst Du Dir sparen, wenn Du den relevanten Code einfach direkt in das Panel reinnimmst.


----------



## mihe7 (2. Mai 2019)

maGG hat gesagt.:


> branches.show(); // hier weiß ich nicht wann überhaupt Location eingesetzt wird, glaube nämlich gar nicht


Hier weiß ich gar nicht, was die Zeile überhaupt soll


----------



## maGG (2. Mai 2019)

Ah hab gerade gesehen, dass ich BranchModelView anstatt BranchModel hatte, ja naja ich muss doch Werte einsetzen in meine FindAll Methode, daher dachte ich rufe ich den Konstruktor in BranchModel einfach auf, weil da wird ja die Methode aufgerufen. Mit jTableFilialen_Deutschland.setModel(branches); kommt wieder nix raus (NullPointer). Ich muss an irgendeiner stelle die findAll Methode aufrufen und dort muss ich etwas einsetzen, oder?


----------



## mihe7 (2. Mai 2019)

Das BranchModel ruft die findAll-Methode auf. Im Panel musst Du dann nur das Model setzen und beim Erzeugen des Models gibst Du ja an, für welche Location.


----------



## maGG (2. Mai 2019)

meinst du das so?

```
public PanelBranchesDomestic() {
    initComponents
    branches = new BranchModel(branch, Location.DOMESTIC) // was setze ich für branch ein?
    jTableFilialen_Deutschland.setModel(branches)
}

BranchModel branches;
BranchRepository branch;
```

was setze ich dann ein? :O


----------



## mihe7 (2. Mai 2019)

Ein übergebenes BranchRepository


----------



## maGG (2. Mai 2019)

ah ich hatte vergessen bei dem Konstruktor den Übergabeparameter Branch zu löschen. Allerdings habe ich immer noch eine Nullpointer Exception, bei der Zeile "data = branches.findAll(location);


```
//Panel
public PanelBranchesDomestic() {
    branches = new BranchModel(Location.DOMESTIC);
    jTableFilialen_Deutschland.setModel(branches);
}
//BranchModel
public BranchModel(Branch.Location location) {
    this.location = location;
    try {
        data = branches.findAll(location)
    }catch (SQLException e) {
        data = new ArrayList<>();
        e.printStackTrace();
    }
}
//BranchRepository
public List<Branch> findAll(Location where) throws SQLException {
    String sql = LOCATION.get(where);
    try(PreparedStatement pst = conn.preparedStatement(sql)) {
        try (RestultSet rs = pst.executeQuery()) {
            List<Branch> result = new ArrayList<>();
            while (rs.next()) {
                result.add(readBranch(rs));
            }
            return result;
        }
    }
}
```

Ich glaube es liegt vielleicht an meiner Datenbank Anbindung, ich habe nirgendwo mein Parameter "conn" aus der Hauptklasse angegeben hmm


----------



## maGG (2. Mai 2019)

mihe7 hat gesagt.:


> Ein übergebenes BranchRepository


k.A. was das heißen soll, hab jetzt schon eniges ausprobiert, kommt jedes mall Null zurück. Ich brauch einen Wert, den ich dort einsetze.

Wenn du sowas meinst:
Main main;
BranchRepository branch = new BranchRepository(main.conn)

=> geht auch nicht.


----------



## mihe7 (2. Mai 2019)

Du baust das irgendwie in die falsche Richtung auf. 

Ganz "unten" hast Du ein BranchRepository, das muss bei seiner Erzeugung eine Connection bekommen. 

Eine Ebene höher liegt das BranchModel, das muss dem entsprechend ein BranchRepository erhalten. Außerdem gibst Du dort noch an, für welche Location das Model gelten soll.

Das BranchModel verwendest Du in Deinem JPanel, also gibst Du es diesem bei seiner Erzeugung mit. 

Hier mal ein Schichtenmodell, wobei das UI detailliert, die Anwendung dagegen verkürzt darstellt ist:

```
+---------------------------------+
| UI-Komponente (z. B. JComboBox) |
+---------------------------------+
                ^
                |
                v
 +---------------------------------+
 |   UI-Model (z. B. BranchModel)  |
 +---------------------------------+
                ^
                |
                v
+------------------------------------+
| Anwendung (z. B. BranchRepository) |
+------------------------------------+
                ^
                |
                v
       +----------------+
       |   Datenbank    |
       +----------------+
```


----------



## maGG (2. Mai 2019)

Ja das sieht schematisch toll aus, vielen Dank, aber ich komme damit nicht weiter leider. Ich habe jetzt 2 Klasse die ein Run Methode haben. Bisher ist meine Methode ConnectDb(); nur in meiner Main Klasse vorhanden. Jetzt habe ich ja mein zweites Fenster ausgelagert, ebenfalls mit einer Run Methode. Bei dieser fehlt jetzt ConnectDb(). Also muss ich irgendwas in dieses bescheuerte Repository Objekt einsetzen und ich habe keine Ahnung was. Das ist wie ein Ratespiel für mich: Schreib 100 verschiedene Sachen rein und schaue obs klappt.

Ich habe jetzt die ConnectDb() Methode auch nochmal in EditData reingeschrieben und in meinem PanelBranchesDomestic habe ich jetzt für das Repository Objekt ein EditData Objekt eingesetzt und darauf eine Getter Methode, um mir Connection conn zu holen. Bringt wieder nix, NullPointer .... gibt bestimmt ne bessere Option, wo ich ConnectDb reinschreiben kann? So schöne diese Verschachtelung auch ist, mittlerweile bin ich echt entnervt davon, es ist mega kompliziert diese Struktur einzuhalten und den Überblick zu behalten wo was eingesetzt wird


----------



## mihe7 (2. Mai 2019)

Niemand hat gesagt, dass es einfach wird - wobei das Prinzip an sich sogar sehr einfach ist:

Wenn Du den Auftrag bekommst, ein Loch zu bohren, dann fängst Du nicht an, eine Bohrmaschine zu bauen. Du lässt Dir auch nicht die Einzelteile geben, um sie dann zu einer Bohrmaschine zusammenzusetzen. Alles, was Du brauchst, ist eine Bohrmaschine, die und nur die musst Du bekommen. Job done.

Wenn ein Objekt ein anderes erzeugt, hat dieses Objekt dafür zu sorgen, dass das erzeugte Objekt mit allem ausgestattet wird, was es braucht. 

Genau diesem Prinzip versucht man, in der Objektorientierung zu folgen. Wenn Du z. B. Swing ansiehst, dann hast Du eine ganz klare Trennung zwischen der UI-Komponente (JComboBox) und dem UI-Model (ComboBoxModel). Wenn Du in Deiner Panel-Klasse ein JComboBox-Objekt erzeugst, dann hat Dein Panel-Objekt dafür zu sorgen, dass das JComboBox-Objekt das benötigte ComboBoxModel erhält. Daher gibst Du der JComboBox das Model mit - was in dem Model steckt, interessiert die JComboBox nicht. Das ist keine Erfindung von mir, um Dich zu ärgern, sondern vom Framework so vorgegeben 

Das gleiche machen wir nun einfach weiter: wir haben eine ComboBoxModel-Implementierung (BranchModel). Das BranchModel braucht ein BranchRepository. Woraus das BranchRepository besteht, interessiert das BranchModel nicht. Wichtig ist für das BranchModel in erster Linie findAll() und ggf. noch die Möglichkeit, das Repository zu beobachten (addRepositoryListener).


Bis dahin ist die Sache recht einfach. Wenn Du ein BranchRepository-Objekt hast, kannst Du ganz einfach ein BranchModel erzeugen. Dieses kannst Du verwenden, um es an eine JComboBox zu übergeben. Die JComboBox kannst Du verwenden, um es in ein JPanel einzufügen. An der Stelle hast Du einen kleinen Bruch, der durch den UI-Builder entsteht: um ein Panel zu erzeugen, fügt der UI-Builder nicht einfach Komponenten einem JPanel hinzu (das wäre das Prinzip wie oben beschrieben), sondern der UI-Builder erstellt eine eigene Klasse für das Panel und in dieser Klasse wird jetzt selbst die JComboBox erstellt (das wäre wie Bohrmaschine selber bauen). 

Daher gibt es nun zwei Möglichkeiten:
a) Du übergibst das BranchModel an das JPanel
b) Du erzeugst auch das BranchModel im JPanel

Da die JComboBox und das BranchModel im UI sehr eng zusammenhängen (die von JPanel abgeleitete Klasse ist ja gerade dazu da, z. B. die Inlands-Filialen anzuzeigen), wäre b) durchaus ein gangbarer Weg. Um das BranchModel im JPanel zu erzeugen, brauchst Du aber ein BranchRepository. Dieses gibst Du dem JPanel mit (Konstruktor).

Bleibt die Frage: wo kommt das BranchRepository her? Ganz einfach: von der Klasse, die das JPanel erzeugt. Das setzt sich ggf. bis zur main-Methode durch.

Im Idealfall gibt es genau eine einzige Instanz von BranchRepository. Dann nämlich kann jedes x-beliebige Objekt (Fenster, Panel, ComboBox, Tabelle, ...) über Änderungen informiert werden, unabhängig davon, wer die Änderung durchführt.

Du kannst also beim Start der Anwendung die DB-Verbindung herstellen, ein BranchRepository-Objekt erzeugen (erhält dann die gerade erstellte Connection) und dieses gibst Du Deinem Fenster/JPanel mit.


----------



## maGG (2. Mai 2019)

Vielen Dank für die ausführliche Erklärung! Mein Code müsste dann eigenlich schon funktionierten, im Moment kommt aber immer noch NullPointer raus:

```
public PanelBranchesDomestic() {
    intiComponents();
    branches = new BranchModel(branch, Location.DOMESTIC)
    jTableFilialen_Deutschland.setModel(branches);
}

EditData conn;
BranchModel branches;
BranchRepository branch = new BranchRepository (conn.getConn());
```
Könntet ihr mir wenigstens sagen, ob der Code soweit stimmt?


----------



## mrBrown (2. Mai 2019)

maGG hat gesagt.:


> Könntet ihr mir wenigstens sagen, ob der Code soweit stimmt?


Naja, er funktioniert nicht, oder? 



Lösch aus der Klasse das Feld conn, und ergänz den Konstruktor um einen Parameter BranchRepository und übergib das dort, wo du das Panel erstellst, so wie @mihe7 das auch schon sagte: 


mihe7 hat gesagt.:


> Du kannst also beim Start der Anwendung die DB-Verbindung herstellen, ein BranchRepository-Objekt erzeugen (erhält dann die gerade erstellte Connection) und dieses gibst Du Deinem Fenster/JPanel mit.


----------



## maGG (2. Mai 2019)

mrBrown hat gesagt.:


> Lösch aus der Klasse das Feld conn


aus der Klasse PanelBranchDomestic?


mrBrown hat gesagt.:


> ergänz den Konstruktor um einen Parameter BranchRepository


so?

```
public PanelBranchesDomestic (BranchRepository branchRepo) {
    initComponents();
    this.branchRepo = branchRepo;   
}
```



mrBrown hat gesagt.:


> übergib das dort, wo du das Panel erstellst


also in meiner Klasse EditData? Was meinst du mit übergeben? was auf was?
Mit den Hinweisen von euch verstehe ich es leider nicht, wie ich es bei mir umsetzen kann.

Im Moment verstehe ich euch so (so ist es aber falsch):

```
public EditData() {
    initComponents();
    setLocationRelativeTo(null);
    setResizable(false);
    ConnectDb();
    panel = new PanelBranchesDomestic (branch);
    branch = new BranchRepository (conn)
    branches = new BranchModel (branch, Location.DOMESTIC);
    panel.jTableFilialen_Deutschland.setModel(branches);
}

Connection conn;
BranchRepository branch;
BranchModel branches;
PanelBranchesDomestic panel;
```


----------



## mrBrown (2. Mai 2019)

maGG hat gesagt.:


> ```
> panel = new PanelBranchesDomestic (branch);
> branch = new BranchRepository (conn)
> ```


Du solltest dir die Reihenfolge noch mal angucken...


----------



## maGG (2. Mai 2019)

Passt denn der Rest?  Also es kommt jetzt zwar kein Fehler mehr, aber die Db wird leider nicht angezeigt


----------



## mihe7 (2. Mai 2019)

Das sieht aktuell nicht verkehrt aus. Du solltest aber nicht von außen auf die Felder des Panels zugreifen.

D. h. panel.jTableFilialen_Deutschland.setModel(branches); aus EditData() rausnehmen und in

```
public PanelBranchesDomestic (BranchRepository branchRepo) {
    this.branchRepo = branchRepo;   
    initComponents();
    jTableFilialen_Deutschland.setModel(new BranchModel(branchRepo, Location.DOMESTIC));
}
```
reinnehmen (man müsste im UI-Builder auch das Model der ComboBox einstellen und dort Code angeben können, dann wäre der Code in initComponents()).

Warum Du jetzt nichts siehst, weiß ich so nicht. Dafür bräuchte man den aktuellen Code bzgl. BranchRepository, BranchModel, usw. inkl. der Stelle, wo Du die DB-Connection "öffnest".


----------



## maGG (3. Mai 2019)

Ok, super .. wenigstens etwas mal richtig 

öffnen tue ich die Datenbank hier:

```
public EditData() {
        initComponents();
        ConnectDb();
        branchRepos = new BranchRepository(conn);
    }
    Connection conn = null;
    BranchRepository branchRepos = null;
   
    private Connection ConnectDb() {
        try {
            conn = DriverManager.getConnection("jdbc:sqlite:" + System.getProperty("user.home") + "/Desktop/Standorte_Adressen.db");
            return conn;
        } catch (SQLException ex) {
            JOptionPane.showMessageDialog(null, ex);
            return null;
        }
    }
```
Konstruktor Aufruf im BranchModel:

```
public BranchModel(BranchRepository branches, Branch.Location location) {
        this.branches = branches;
        this.location = location;
        try {
            data = branches.findAll(location);
        } catch (SQLException e) {
            data = new ArrayList<>();
            e.printStackTrace();
        }
    }
```
Konstruktor Aufruf in BranchRepository:

```
public BranchRepository(Connection conn) {
        this.conn = conn;
    }
```
findAll Methode in BranchRepository:

```
public List<Branch> findAll(Location where) throws SQLException {
        String sql = LOCATION.get(where);
        try(PreparedStatement pst = conn.prepareStatement(sql)) {
            try(ResultSet rs = pst.executeQuery()){
                List<Branch> result = new ArrayList<>();
                while(rs.next()) {
                    result.add(readBranch(rs));
                }
                return result;            
            }
        }
    }
```
readBranchMethode in BranchRepository:

```
private Branch readBranch(ResultSet rs) throws SQLException {
        return new Branch(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getString(4), rs.getString(5), rs.getInt(6), rs.getLong(7), rs.getString(8), rs.getInt(9), rs.getString(10), rs.getString(11));
    }
```
Sieht doch richtig aus oder? :O


----------



## mihe7 (3. Mai 2019)

Ja, das sieht alles schon mal sehr gut aus. Ich sehe nur nicht, dass Du branchRepos irgendwo verwenden würdest (in EditData). Wenn Du das in initComponents() machst, musst Du initComponents() ans Ende des Konstruktors schieben, sonst ist branchRepos noch nicht gesetzt.


----------



## maGG (3. Mai 2019)

Ok, habe ich geändert. Leider keine Veränderug. Gibt es noch etwas was ich checken könnte? Also die Datei befindet sich tatsächlich noch auf dem Desktop


----------



## mihe7 (3. Mai 2019)

Wo initialisierst Du denn mit dem branchRepos ein BranchModel und wo setzt du das Model der JComboBox?


----------



## maGG (3. Mai 2019)

branchRepos habe ich zwei Mal:
In der Klasse EditData, mit "BranchRepository branchRepos = null;"
in der Klasse PanelBranchDomestic, ebenfalls mit BranchRepository branchRepos = null; <- hier als Übergabeparameter

Die JCombBox habe ich nirgens initialisiert, da sie jetzt im neuen Fenster EditData nicht vorhanden ist. Die ComboBox ist in dem Hauptfenster Main und soll dort Parameter mit Werten belegen, die dann in einer späteren Writer Methode zum erstellen von Textdateien benutzt wird. Die ComboBox ist also nicht dazu da, um die Textfelder in EditData mit Werte zu belegen, diese Werte sollen nur mit einem MouseClicked und KeyReleased Event mit setText() belegt werden. Zu mindest habe ich mir das jetzt so überlegt. So wird beim Start des Programms nur noch das Hauptfenster geladen, was dann hoffentlich schneller geht, und das Fenster EditData kann man öffnen, wenn man Einträge in der Datenbank editieren möchte. Man könnte dann noch ein Passwort setzen, welches man in einem simplen JOptionPane eingeben muss, um ins zweite Fenster zu gelangen. Also ich würde am liebsten erst mal mich ums EditData Fenster kümmern und dort alles zu Ende schreiben, bevor ich mich dann um die JComboBox kümmere.

Die Connection conn habe ich jetzt provisorisch in EditData drin, damit ich testen kann. Später ist es bestimmt sinvoller, wie du ja schon geschrieben hast, die conn einmal aufzurufen und dann zu übergeben.


----------



## mihe7 (3. Mai 2019)

maGG hat gesagt.:


> So wird beim Start des Programms nur noch das Hauptfenster geladen, was dann hoffentlich schneller geht, und das Fenster EditData kann man öffnen, wenn man Einträge in der Datenbank editieren möchte.


Ach so, dann schreiben wir die ganze Zeit aneinander vorbei   Was soll in dem Fenster denn alles sein?


----------



## maGG (3. Mai 2019)

Also im Fenster Main sind ...

Und das EditData Fenster sieht so aus:


----------



## mihe7 (3. Mai 2019)

Ok, dann musst Du die JTable mit dem BranchModel irgendwo initialisieren.


----------



## maGG (3. Mai 2019)

mihe7 hat gesagt.:


> Ok, dann musst Du die JTable mit dem BranchModel irgendwo initialisieren.


Mache ich das nicht schon im Panel hiermit? 
	
	
	
	





```
jTableFilialen_Deutschland.setModel(new BranchModel(branchRepos, Location.DOMESTIC));
```
Oder meinst du sowas?

```
public PanelBranchesDomestic(BranchRepository branchRepos, BranchModel branchModel) {
        this.branchRepos = branchRepos;
        this.branchModel = new BranchModel(branchRepos, Location.DOMESTIC);
        initComponents();
        jTableFilialen_Deutschland.setModel(branchModel);
    }
   
    BranchRepository branchRepos = null;
    BranchModel branchModel = null;
```


----------



## mihe7 (3. Mai 2019)

Du hast also ein EditData-Frame, das z. B. ein PanelBranchesDomestic enthält, richtig? In Kommentar #156 fehlt mir die Stelle, an der Du das PanelBranchesDomestic erzeugst.

Im Konstruktor von PanelBranchesDomestic brauchst Du das branchModel nicht nochmal erstellen - Du bekommst es ja schon als Parameter.


----------



## maGG (3. Mai 2019)

Ah auf so eine ähnliche Idee kam ich auch gerade; ich habe die Panels als Datei erstellt, dann einmal auf Compile File geklickt und dann mit Drag and Drop in das TabbedPane von EditData gezogen - mehr habe ich nicht gemacht. Ich sehe gerade in der initComponent Methode wird nur der Standardkonstruktor aufgerufen. Kann es sein, dass ich den Allgemeinen aufrufen muss, mit unseren Anpassungen?

Sowas? 
	
	
	
	





```
panelBranchesDomestic = new UserInterface.PanelBranchesDomestic(branchRepos);
```

initComponents:

```
@SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                        
    private void initComponents() {

        jButton5 = new javax.swing.JButton();
        jTabbedPane = new javax.swing.JTabbedPane();
        panelBranchesDomestic = new UserInterface.PanelBranchesDomestic();
        panelBranchesForeign = new UserInterface.PanelBranchesForeign();
        panelCompanyNames = new UserInterface.PanelCompanyNames();
        panelTitels = new UserInterface.PanelTitels();
        panelRoles1 = new UserInterface.PanelRoles();
        jButton8 = new javax.swing.JButton();
        jLabelDBPath = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setPreferredSize(new java.awt.Dimension(1082, 587));
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
        });

        jButton5.setIcon(new javax.swing.ImageIcon("C:\\Users\\david\\Documents\\back-icon.png")); // NOI18N
        jButton5.setText("Zurück zum Hauptfenster");
        jButton5.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton5ActionPerformed(evt);
            }
        });

        jTabbedPane.addTab("Filialen_Deutschland", panelBranchesDomestic);
        jTabbedPane.addTab("Filialen_Ausland", panelBranchesForeign);
        jTabbedPane.addTab("Firmierungen", panelCompanyNames);
        jTabbedPane.addTab("Titel", panelTitels);
        jTabbedPane.addTab("Funktionen", panelRoles1);

        jButton8.setIcon(new javax.swing.ImageIcon("C:\\Users\\david\\Documents\\My-Documents-icon.png")); // NOI18N
        jButton8.setText("Datenbank laden");
        jButton8.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton8ActionPerformed(evt);
            }
        });

        jLabelDBPath.setText(" ");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jButton5)
                .addGap(18, 18, 18)
                .addComponent(jButton8)
                .addGap(121, 121, 121)
                .addComponent(jLabelDBPath)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
            .addComponent(jTabbedPane)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jButton5)
                    .addComponent(jButton8)
                    .addComponent(jLabelDBPath))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jTabbedPane, javax.swing.GroupLayout.PREFERRED_SIZE, 534, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>  

    // Variables declaration - do not modify                    
    private javax.swing.JButton jButton5;
    private javax.swing.JButton jButton8;
    private javax.swing.JLabel jLabelDBPath;
    private javax.swing.JTabbedPane jTabbedPane;
    private UserInterface.PanelBranchesDomestic panelBranchesDomestic;
    private UserInterface.PanelBranchesForeign panelBranchesForeign;
    private UserInterface.PanelCompanyNames panelCompanyNames;
    private UserInterface.PanelRoles panelRoles1;
    private UserInterface.PanelTitels panelTitels;
    // End of variables declaration
```


----------



## mihe7 (3. Mai 2019)

maGG hat gesagt.:


> Kann es sein, dass ich den Allgemeinen aufrufen muss, mit unseren Anpassungen?


Jawoll. Du kannst im UI Builder in den Properties bei "custom creation" (oder so ähnlich) angeben, wie die Komponente erstellt werden solll.  Dort schreibst Du "new UserInterface.PanelBranchesDomestic(branch, branches);" rein (hoffe, die Namen stimmen noch). Dann sollte das so in initComponents übernommen werden.


----------



## maGG (3. Mai 2019)

Ah cool!

Moment, im Allgemeinen Konstruktor von PanelBranchDomestic habe ich aktuell nur (BranchRepository branchRepos) als Übergabeparamenter. Das mit dem Model war nur geraten  Oder müssen da wirklich zwei Parameter rein?

Also:

```
//Klasse des Panels
public class PanelBranchesDomestic extends javax.swing.JPanel {

    public PanelBranchesDomestic() {
        initComponents();
    }
    
    public PanelBranchesDomestic(BranchRepository branchRepos) {
        this.branchRepos = branchRepos;
        initComponents();
        jTableFilialen_Deutschland.setModel(new BranchModel(branchRepos, Location.DOMESTIC));       
    }
    
    BranchRepository branchRepos = null;
    
    private void initComponents() {
        //...
    }
    
    //Variable declaration ...
}

//Klasse des EditData Fensters
public class EditData extends javax.swing.JFrame {

    public EditData() {
        branchRepos = new BranchRepository(conn);
        initComponents();
        setLocationRelativeTo(null);
        setResizable(false);
        ConnectDb();
    }
    
    Connection conn = null;
    BranchRepository branchRepos = null;
    
    private Connection ConnectDb() {
        try {
            conn = DriverManager.getConnection("jdbc:sqlite:" + System.getProperty("user.home") + "/Desktop/KERN_Standorte_Adressen.db");
            return conn;
        } catch (SQLException ex) {
            JOptionPane.showMessageDialog(null, ex);
            return null;
        }
    }
    
    private void initComponents() {
        panelBranchesDomestic = new UserInterface.PanelBranchesDomestic(branchRepos);
        //...
    }
    
    private UserInterface.PanelCompanyNames panelCompanyNames;
    //..

}
```
Bekomme so aber leider wieder eine Nullpointer:

```
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at RepositoryClasses.BranchRepository.findAll(BranchRepository.java:75)
    at ModelClasses.BranchModel.<init>(BranchModel.java:42)
    at UserInterface.PanelBranchesDomestic.<init>(PanelBranchesDomestic.java:29)
    at UserInterface.EditData.initComponents(EditData.java:70)
    at UserInterface.EditData.<init>(EditData.java:25)
    at UserInterface.EditData$4.run(EditData.java:188)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.awt.EventQueue$3.run(EventQueue.java:703)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
```


```
//at RepositoryClasses.BranchRepository.findAll(BranchRepository.java:75)
try(PreparedStatement pst = conn.prepareStatement(sql)) {

//at ModelClasses.BranchModel.<init>(BranchModel.java:42)
data = branches.findAll(location);

//at UserInterface.PanelBranchesDomestic.<init>(PanelBranchesDomestic.java:29)
jTableFilialen_Deutschland.setModel(new BranchModel(branchRepos, Location.DOMESTIC));   

//at UserInterface.EditData.initComponents(EditData.java:70)
panelBranchesDomestic = new UserInterface.PanelBranchesDomestic(branchRepos);

//at UserInterface.EditData.<init>(EditData.java:25)
initComponents();

//at UserInterface.EditData$4.run(EditData.java:188)
new EditData().setVisible(true);
```


----------



## mihe7 (3. Mai 2019)

maGG hat gesagt.:


> Oder müssen da wirklich zwei Parameter rein?


Nein, nein, passt.


----------



## mihe7 (3. Mai 2019)

maGG hat gesagt.:


> Bekomme so aber leider wieder eine Nullpointer:


Du musst in EditData ConnectDB schon aufrufen, bevor Du die conn verwendest.


----------



## maGG (4. Mai 2019)

Cool, es funktioniert inzwischnen 

Wie kann ich denn in der Struktur jetzt mit der setCurrent Methode das entsprechend handhaben? Also in der Klasse BranchViewDomestic habe ich die setCurent drin, wo die entsprechenden Texfelder gesetzt werden. Die Methode benötigt aos Übergabe ein Objekt vom Typ Branch. Von der Struktur her bräuchte ich jetzt eine Methode in BranchModel oder? Kann ich mir da was aus den abstrakten Methoden des BranchModels was basteln?
Habe gesehen, dass du bei PersonView das beim erstellen der Tabelle geschrieben hast, davon versehe ich aber nicht alles:


```
private JTable createTable() {
        JTable table = new JTable(persons);
        table.getSelectionModel().addListSelectionListener(e -> {
            if (e.getValueIsAdjusting()) {
                return;
            }
            int row = table.getSelectedRow();
            setCurrent(row != -1 ? persons.getElementAt(row) : null);
        });       
        setCurrent(null);
        return table;
    }
```
Den ListSelectionListener brauche ich ja nicht mehr oder? Also, weil ich jetzt EventMouseOver und EventKeyReleased benutze


----------



## mihe7 (4. Mai 2019)

maGG hat gesagt.:


> Also in der Klasse BranchViewDomestic habe ich die setCurent drin, wo die entsprechenden Texfelder gesetzt werden


Mal eine doofe Frage: unterscheiden sich BranchViewDomestic und BranchViewForeign irgendwie (mal von der Location abgesehen)? Wenn nein, wäre es Zeit für ein Refactoring: BranchView mit Location als Parameter 

Ansonsten: setCurrent bekommt ein Branch-Objekt und setzt die Textfelder. Was willst Du da noch basteln?

Nachtrag: habe gerade BranchView mit den Panels verwechselt. Wenn ich mich recht entsinne, war das die Klasse, deren Methode Du ins Panel übernehmen kannst.


----------



## maGG (4. Mai 2019)

mihe7 hat gesagt.:


> Mal eine doofe Frage: unterscheiden sich BranchViewDomestic und BranchViewForeign irgendwie (mal von der Location abgesehen)? Wenn nein, wäre es Zeit für ein Refactoring: BranchView mit Location als Parameter


Im Prinzip nicht, allerdings gibt es für DOMESTIC und FOREIGN jeweils eine andere JTabel (jTableFilialen_Deutschland und jTableFilialen_Ausland). Das heißt in der setCurrent  Methode setze ich ja String Werte für die die 11 JTextFields. In den jTableFilialen_Deutschland heißen die Felder jTextFieldD1 bis jTextFieldD11 und bei jTableFilialen_Ausland dann jTextFieldA1 bis jTextFieldA11. Alternativ könnte ich mir die setCurrent Methode auch sparen und mir in die GUI Methode schreiben, aber dann kann hab ich ja nicht mehr diese schöne Verschachtelung ^^


mihe7 hat gesagt.:


> Ansonsten: setCurrent bekommt ein Branch-Objekt und setzt die Textfelder. Was willst Du da noch basteln?
> 
> Nachtrag: habe gerade BranchView mit den Panels verwechselt. Wenn ich mich recht entsinne, war das die Klasse, deren Methode Du ins Panel übernehmen kannst.


Ja genau, aber wie bekomme ich jetzt an das Branch Objekt ran? Mache ich das mit SelectedRow von der JTable?


----------



## mihe7 (4. Mai 2019)

Wenn Deine Panels identisch sind, kannst Du eine Klasse draus machen. Du musst ja nur zwei Instanzen davon erstellen, einmal mit Location.DOMESTIC und einmal mit Location.FOREIGN. In dem Panel heißen die Felder dann neutral. Die View-Klasse kannst Du Dir sparen, weil setCurrent ja gerade dazu dient, die Textfelder zu setzen. Das Branch-Objekt bekommst Du vom BranchModel über die ausgewählte Zeile der Tabelle.


----------



## maGG (4. Mai 2019)

mihe7 hat gesagt.:


> In dem Panel heißen die Felder dann neutral


Das versteht ich nicht, geht sowas?

```
private void jTableFilialen_DeutschlandKeyReleased(java.awt.event.KeyEvent evt) {                                                       
        if(evt.getKeyCode() == KeyEvent.VK_UP || evt.getKeyCode() == KeyEvent.VK_DOWN) {
            int row = jTableFilialen_Deutschland.getSelectedRow();
            Branch branch = branchModel.getElementAt(row);
            branchView.setCurrent(branch);
        }
    }
```
So in etwa?


----------



## mihe7 (4. Mai 2019)

Das, was in Deinem if-Zweig ist: genau.

Zwei Dinge: 
1. warum willst Du einen KeyListener verwenden?
2. zum Thema neutrale Bezeichnung. Mal in mehreren Absätzen:

Aktuell hast Du ja ein "Deutschland-Panel" und ein "Auslands-Panel". Du kannst innerhalb der beiden Panels die Felder neutral bezeichnen, weil ja durch die Klasse schon klar ist, ob Deutschland oder Ausland. 

Sprich: statt jTableFilialen_Deutschland nennst Du das Feld einfach jTableFilialen. 

Wenn dann die Panels auch noch identisch sind, kannst Du eines in die Tonne treten. Dann machst Du z. B. aus PanelBranchesDomestic einfach nur ein PanelBranches und gibst im Konstruktor die Location mit. In EditData erzeugst Du dann einfach zwei PanelBranches - jeweils mit der betreffenden Location.


----------



## maGG (4. Mai 2019)

mihe7 hat gesagt.:


> 1. warum willst Du einen KeyListener verwenden?


Weil ich keine anderen Möglichkeit kenne, um die Texfelder mit den Werten zu belegen, wenn man mit den Pfeiltasten hoch und runter geht. Es ist auch noch ein MouseClicked vorgesehen, oder hast du eine andere Idee?

zu 2. das würde ich später noch machen, jeztzt hab ich schon wieder ne NullPointer bei dem Code oben


----------



## mihe7 (4. Mai 2019)

```
import javax.swing.*;
import javax.swing.table.*;

public class Test {
    public void run() {
        TableModel dataModel = new AbstractTableModel() {
            public int getColumnCount() { return 10; }
            public int getRowCount() { return 10;}
            public Object getValueAt(int row, int col) { return new Integer(row*col); }
        };
        JTable table = new JTable(dataModel);
        table.setRowSelectionAllowed(true);
        table.getSelectionModel().addListSelectionListener(e -> {
            if (e.getValueIsAdjusting()) {
                return;
            }
            System.out.println(table.getSelectedRow());
        });
        JScrollPane scrollpane = new JScrollPane(table);

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(scrollpane);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Test().run());
    }
}
```


----------



## maGG (5. Mai 2019)

Ah cool, das ist natürlich was Feines 


```
private void tableListener() {
        table.setRowSelectionAllowed(true);
        table.getSelectionModel().addListSelectionListener(e -> {
            if (e.getValueIsAdjusting()) {
                return;
            }
            //System.out.println(table.getSelectedRow() + 1);
            int row = table.getSelectedRow() + 1;
            branch = branchModel.getElementAt(row);
            branchView.setCurrent(branch);
        });   
    }
```
 komischerweise ist die Zahl immer um 1 zu klein gewesen. 

In der Zeile branchView.setCurrent(branch); bekomme ich aber wieder eine NullPointer Ex. Ich vermute mal das an der Methode was falsch ist;

```
public void setCurrent(Branch branch) {
        this.current = branch;
        boolean valid = current != null;
        if (valid) {
            panel.jTextFieldD1.setText("" + current.getId());
            panel.jTextFieldD2.setText(current.getLabel());
            panel.jTextFieldD3.setText(current.getStrasze());
            panel.jTextFieldD4.setText(current.getPlzort());       
            panel.jTextFieldD5.setText(current.getOrt());
            panel.jTextFieldD8.setText("" + current.getVorwahl());
            panel.jTextFieldD6.setText("" + current.getTelStartNum());
            panel.jTextFieldD7.setText(current.getTelStartTxt());
            panel.jTextFieldD9.setText("" + current.getTelEndDefault());
            panel.jTextFieldD10.setText(current.getFaxStartTxt());
            panel.jTextFieldD11.setText(current.getFaxEndTxt());
        } else {
            panel.jTextFieldD1.setText("");
            panel.jTextFieldD2.setText("");
            panel.jTextFieldD3.setText("");
            panel.jTextFieldD4.setText("");       
            panel.jTextFieldD5.setText("");
            panel.jTextFieldD8.setText("");
            panel.jTextFieldD6.setText("");
            panel.jTextFieldD9.setText("");
            panel.jTextFieldD10.setText("");
            panel.jTextFieldD11.setText("");
        }
        panel.jTextFieldD1.setEnabled(valid);
        panel.jTextFieldD2.setEnabled(valid);
        panel.jTextFieldD3.setEnabled(valid);
        panel.jTextFieldD4.setEnabled(valid);     
        panel.jTextFieldD5.setEnabled(valid);
        panel.jTextFieldD8.setEnabled(valid);
        panel.jTextFieldD6.setEnabled(valid);
        panel.jTextFieldD7.setEnabled(valid);
        panel.jTextFieldD9.setEnabled(valid);
        panel.jTextFieldD10.setEnabled(valid);
        panel.jTextFieldD11.setEnabled(valid);
        panel.jButtonUpdateD.setEnabled(valid);
        panel.jButtonSaveD.setEnabled(valid);
        panel.jButtonDeleteD.setEnabled(valid);   
    }
```
Es wird halt kein Fehler angezeigt :O


----------



## mihe7 (5. Mai 2019)

maGG hat gesagt.:


> komischerweise ist die Zahl immer um 1 zu klein gewesen.


Wie meinst Du das? Die Indizes beginnen wie üblich mit 0, erste Zeile ist also 0. 



maGG hat gesagt.:


> In der Zeile branchView.setCurrent(branch); bekomme ich aber wieder eine NullPointer Ex.


Wenn Du in der Zeile eine NPE bekommst, muss branchView == null gelten.


----------



## maGG (5. Mai 2019)

mihe7 hat gesagt.:


> Wie meinst Du das? Die Indizes beginnen wie üblich mit 0, erste Zeile ist also 0.


naja bei dem System.out.printLine.. kommt immer eine Zahl die sonst um 1 zu klein ist.


mihe7 hat gesagt.:


> Wenn Du in der Zeile eine NPE bekommst, muss branchView == null gelten.


Jo, das geht aber gar nicht eigentlich. Sonst müsste auch ja schon das Anzeigen der Werte in der Tabelle nicht klappen. Gut, dort habe ich BranchViewDomestic gar nicht benutzt. Aber auch durch setzen von 
	
	
	
	





```
BranchViewDomestic branchView = new BranchViewDomestic(branchModel);
```
 kommt noch NullPointer


----------



## mihe7 (5. Mai 2019)

Schau Dir den Stacktrace nochmal genau an. Wenn die NPE wirklich originär in der Zeile entsteht, muss branchView == null gelten -> das geht nicht anders. Evtl. passiert es aber innerhalb der BranchViewDomestic, das würdest Du dann aber im Stacktrace sehen.


----------



## maGG (5. Mai 2019)

ja, da steht das hier:

```
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at ModelClasses.BranchViewDomestic.setCurrent(BranchViewDomestic.java:40)
    at UserInterface.PanelBranchesDomestic.lambda$tableListener$0(PanelBranchesDomestic.java:61)
```
Ich vermute auch, dass es an der setCurrent Methode liegt, aber dann dürfe es eigentlich keine NullPointer sein, sondern irgendeine andere Exception. Zeile 40 macht das hier: 
	
	
	
	





```
panel.jTextFieldD1.setText("" + current.getId());
```
 sehe ich kein Fehler drin.


----------



## mihe7 (5. Mai 2019)

Naja, panel, jTextFieldD1 und/oder current muss/müssen null sein, damit hier eine NPE fliegt. Du kannst ja mal einen Haltepunkt setzen und den Debugger starten. Dann kannst Du Dir live ansehen, wo das Problem liegt.


----------



## maGG (6. Mai 2019)

ja danke, werde ich mal testen. Wobei ich inzwischen am überlegen bin mein alten Code wieder zu nehmen. Der ist zwar viel schlechter, aber er funktioniert wenigstens und ich habe nicht ständig NPEs. Es ist extrem frustrierend, wenn man für so ne Kleinigkeit Stunden oder gar Tage benötigt und ich muss das Ding auch mal fertig bekommen.


----------



## mrBrown (6. Mai 2019)

Ich würde an deiner Stelle mit dem Coder weiter machen 
NPEs sind mit die einfachsten Fehler, dein alter Code würde dich vermutlich vor deutlich komplexere Probleme stellen


----------



## mihe7 (6. Mai 2019)

maGG hat gesagt.:


> Wobei ich inzwischen am überlegen bin mein alten Code wieder zu nehmen. Der ist zwar viel schlechter, aber er funktioniert wenigstens und ich habe nicht ständig NPEs.


Eine NPE entsteht immer dadurch, dass eine Referenz null ist. Du weißt also in welcher Zeile und meist auch sofort welche Referenz. Insofern ist das sehr einfach zu beheben.

Ich kenne jetzt den Rest des aktuellen Codes nicht, sonst hätte ich da mal reingeschaut. Meine Vermutung wäre: Du nimmst immer noch diese seltsamen View-Klassen her, instantiierst diese, bevor das betreffende Panel-Objekt (vollständig) initialisiert ist. Ist aber nur eine Vermutung.


----------



## maGG (9. Mai 2019)

mihe7 hat gesagt.:


> seltsamen View-Klassen


naja das ist eigentlich nach deinem Modell erstell. Nur das du halt kein GUI Builder benutzt hast und die Tabelle in der selben Klasse erstellst hast, wo du auch die Textfelder belegt hast. Ich habe jetzt halt ein View Klasse und eine Klasse wo die Texfelder deklariert sind. Um Zugriff auf diese zu haben muss ich also die Felder public machen und eine Object aus der GUI Klasse in meiner View Klasse erzeugen und dann habe ich erst Zugriff.

Ich bin mal die Verschachtelunge durchgegangen und geschaut wo was initialisiert wird. Es sieht alles richtig aus (auch dei Reihenfolge). Jetzt Könnte vielleicht das Erzeugene des Object.

Das habe ich so gemacht:

```
import UserInterface.PanelBranchesDomestic;

public class BranchViewDomestic {
    private PanelBranchesDomestic panel;

    //...
    
    public void setCurrent(Branch branch) {
        //...
        panel.jTextFieldD1.setText("" + current.getId());
        //..
    }
}
```
Ist das etwas falsch?


----------



## mihe7 (9. Mai 2019)

maGG hat gesagt.:


> naja das ist eigentlich nach deinem Modell erstell.


Die Namen machen nicht das Modell 


maGG hat gesagt.:


> Ich habe jetzt halt ein View Klasse und eine Klasse wo die Texfelder deklariert sind. Um Zugriff auf diese zu haben muss ich also die Felder public machen und eine Object aus der GUI Klasse in meiner View Klasse erzeugen und dann habe ich erst Zugriff.


Und genau das ist falsch. Du hebst jegliche Kapselung auf, weil Du Dinge trennst, die zusammengehören (Google mal nach Kopplung und Kohäsion).



maGG hat gesagt.:


> Ist das etwas falsch?


Das lässt sich auf Grundlage des Schnipsels nicht sagen. Das Einzige, was sicher ist: wenn in einer Zeile (originär) eine NPE auftritt, dann wird auf eine null-Referenz zugegriffen. In der Zeile panel.jTextFieldD1.setText("" + current.getId()); kann eine NPE nur auftreten, wenn panel, jTextFieldD1 oder current null ist. Du brauchst Dir ja nur mal die Werte ausgeben zu lassen (System.out.println(panel); ...)


----------



## maGG (9. Mai 2019)

mihe7 hat gesagt.:


> Du hebst jegliche Kapselung auf, weil Du Dinge trennst, die zusammengehören


Das heißt die Klasse ViewBranchDomestic würdest du löschen und den Code lieber direkt in mein Panel schreiben? Das kann ich auch machen, dann habe ich halt keine View Klasse mehr und die Methode setCurrent, add, update, delete etc. müsste ich dann alle in mein Panel, also dann für jedes Panel die gleichen Methoden.

Ich habe es mal ausprobiert und die setCurrent Methode in mein Panel geschriebe; funktioniert jetzt so. Aber dachte ich sollte solche Sachen immer trennen und copy & paste vermeiden :O


----------



## mihe7 (10. Mai 2019)

maGG hat gesagt.:


> Das heißt die Klasse ViewBranchDomestic würdest du löschen und den Code lieber direkt in mein Panel schreiben?


Ja, das schreibe ich schon die ganze Zeit  



maGG hat gesagt.:


> Das kann ich auch machen, dann habe ich halt keine View Klasse mehr und die Methode setCurrent, add, update, delete etc. müsste ich dann alle in mein Panel, also dann für jedes Panel die gleichen Methoden.


Nein, wenn Du's richtig machst, hast Du wahrscheinlich nur noch eine Panel-Klasse (s. Kommentar #169).



maGG hat gesagt.:


> Aber dachte ich sollte solche Sachen immer trennen und copy & paste vermeiden :O


Jein. Es geht darum, halbwegs vernünftige Abstraktionen zu finden, dadurch vermeidet man Copy & Paste. Wenn unterschiedliche Konzepte gleiche Eigenschaften besitzen, dann ist das eben so. Wenn ich Deine zwei Panels nehme (s. #169), dann schaut es so aus, als wären die identisch. Dann schreibt man eine Panel-Klasse und verwendet diese öfter (in Form mehrerer Objekte).


----------



## maGG (10. Mai 2019)

wie kann ich mit meiner save/insert Methode eine neues Branch Object in meiner Liste erzeugen? Im Moment funktioniert meine save Methode ungewollt wie eine update Methode 


```
public void save() {
        if (this.current == null) {
            return;
        }
        current.setId(Integer.parseInt(jTextFieldBranch1.getText()));
        current.setLabel(jTextFieldBranch2.getText());
        current.setStrasze(jTextFieldBranch3.getText());
        current.setPlzort(jTextFieldBranch4.getText());
        current.setOrt(jTextFieldBranch5.getText());
        current.setVorwahl(Integer.parseInt(jTextFieldBranch8.getText()));
        current.setTelStartNum(Long.parseLong(jTextFieldBranch6.getText()));
        current.setTelStartTxt(jTextFieldBranch7.getText());
        current.setTelEndDefault(Integer.parseInt(jTextFieldBranch9.getText()));
        current.setFaxEndTxt(jTextFieldBranch10.getText());
        current.setFaxEndTxt(jTextFieldBranch11.getText());
        if (current.getId() == 0L) {
            branchModel.add(current); 
        } else {
            //branchModel.update(current); // was wolltest du hier eigentlich machen? Wieso die Verzweigung hier?
        }
        jTableBranch.setModel(branchModel);  // hiermit wollte ich die JTable nach eine Änderunge aktualiseren, geht noch nicht
    }
```


----------



## mihe7 (10. Mai 2019)

maGG hat gesagt.:


> wie kann ich mit meiner save/insert Methode eine neues Branch Object in meiner Liste erzeugen? Im Moment funktioniert meine save Methode ungewollt wie eine update Methode


Indem Du die if-Abfrage unten rausnimmst und immer branchModel.add() aufrufst. Die letzte Zeile (jTableBranch.setModel) ebenfalls entfernen. Das JTable wird jetzt automatisch aktualisiert


----------



## maGG (10. Mai 2019)

Ne leider funktioniert das noch nicht so toll bei mir  

Mein Panel Klasse sieht jetzt so aus (habe einige Sachen geändert, z.B. habe ich jetzt nur ein Panel für die Branches)


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package UserInterface;

import java.awt.Color;
import ObjectClasses.Branch;
import ObjectClasses.Branch.Location;
import ModelClasses.BranchModel;
import RepositoryClasses.BranchRepository;

/**
 *
 * @author david
 */
public class PanelBranches extends javax.swing.JPanel {

    /**
     * Creates new form BranchesDomestic
     */
    
    public PanelBranches() {
        initComponents();
    }
    
    public PanelBranches(BranchRepository branchRepos, Location location) {
        this.branchRepos = branchRepos;
        branchModel = new BranchModel(branchRepos, location);
        initComponents();       
        adjustAppearance();
        tableListener();
        /*
            Model wird in initComponents gesetzt
            branchModel = new BranchModel(branchRepos, location);
            jTableBranch.setModel(branchModel);
        */
    }

    BranchModel branchModel = null;
    BranchRepository branchRepos = null;
    Branch branch, current = null;
 
    private void adjustAppearance() {
        setBackground(new Color(0,0,0,0));
        jTableBranch.getTableHeader().setOpaque(false);
        jTableBranch.getTableHeader().setBackground(new Color(128, 128, 128));
        jTableBranch.getTableHeader().setForeground(Color.WHITE);
    }

    private void setCaretPositions() {
        jTextFieldBranch1.setCaretPosition(0);
        jTextFieldBranch2.setCaretPosition(0);
        jTextFieldBranch3.setCaretPosition(0);
        jTextFieldBranch4.setCaretPosition(0);     
        jTextFieldBranch5.setCaretPosition(0);
        jTextFieldBranch8.setCaretPosition(0);
        jTextFieldBranch6.setCaretPosition(0);
        jTextFieldBranch9.setCaretPosition(0);
        jTextFieldBranch10.setCaretPosition(0);
        jTextFieldBranch11.setCaretPosition(0);
    }
    
    private void tableListener() {
        jTableBranch.setRowSelectionAllowed(true);
        jTableBranch.getSelectionModel().addListSelectionListener(e -> {
            if (e.getValueIsAdjusting()) {
                return;
            }
            int row = jTableBranch.getSelectedRow();
            branch = branchModel.getElementAt(row);
            setCurrent(branch);
            setCaretPositions();
        });   
    }
    
    private void setCurrent(Branch branch) {
        this.current = branch;
        boolean valid = current != null;
        if (valid) {
            jTextFieldBranch1.setText("" + current.getId());
            jTextFieldBranch2.setText(current.getLabel());
            jTextFieldBranch3.setText(current.getStrasze());
            jTextFieldBranch4.setText(current.getPlzort());       
            jTextFieldBranch5.setText(current.getOrt());
            jTextFieldBranch8.setText("" + current.getVorwahl());
            jTextFieldBranch6.setText("" + current.getTelStartNum());
            jTextFieldBranch7.setText(current.getTelStartTxt());
            jTextFieldBranch9.setText("" + current.getTelEndDefault());
            jTextFieldBranch10.setText(current.getFaxStartTxt());
            jTextFieldBranch11.setText(current.getFaxEndTxt());
        } else {
            jTextFieldBranch1.setText("");
            jTextFieldBranch2.setText("");
            jTextFieldBranch3.setText("");
            jTextFieldBranch4.setText("");       
            jTextFieldBranch5.setText("");
            jTextFieldBranch8.setText("");
            jTextFieldBranch6.setText("");
            jTextFieldBranch9.setText("");
            jTextFieldBranch10.setText("");
            jTextFieldBranch11.setText("");
        }
        jTextFieldBranch1.setEnabled(valid);
        jTextFieldBranch2.setEnabled(valid);
        jTextFieldBranch3.setEnabled(valid);
        jTextFieldBranch4.setEnabled(valid);     
        jTextFieldBranch5.setEnabled(valid);
        jTextFieldBranch8.setEnabled(valid);
        jTextFieldBranch6.setEnabled(valid);
        jTextFieldBranch7.setEnabled(valid);
        jTextFieldBranch9.setEnabled(valid);
        jTextFieldBranch10.setEnabled(valid);
        jTextFieldBranch11.setEnabled(valid);
        jButtonUpdateBranch.setEnabled(valid);
        jButtonSaveBranch.setEnabled(valid);
        jButtonDeleteBranch.setEnabled(valid);   
    }
    
    private void update(Branch branch) {
        this.current = branch;
        if(this.current == null) {
            return;
        }
        current.setId(Integer.parseInt(jTextFieldBranch1.getText()));
        current.setLabel(jTextFieldBranch2.getText());
        current.setStrasze(jTextFieldBranch3.getText());
        current.setPlzort(jTextFieldBranch4.getText());
        current.setOrt(jTextFieldBranch5.getText());
        current.setVorwahl(Integer.parseInt(jTextFieldBranch8.getText()));
        current.setTelStartNum(Long.parseLong(jTextFieldBranch6.getText()));
        current.setTelStartTxt(jTextFieldBranch7.getText());
        current.setTelEndDefault(Integer.parseInt(jTextFieldBranch9.getText()));
        current.setFaxEndTxt(jTextFieldBranch10.getText());
        current.setFaxEndTxt(jTextFieldBranch11.getText());
        branchModel.update(current);
    }
    
    private void save() {
        this.current = branch;       
        if (this.current == null) {
            return;
        }
        current.setId(Integer.parseInt(jTextFieldBranch1.getText()));
        current.setLabel(jTextFieldBranch2.getText());
        current.setStrasze(jTextFieldBranch3.getText());
        current.setPlzort(jTextFieldBranch4.getText());
        current.setOrt(jTextFieldBranch5.getText());
        current.setVorwahl(Integer.parseInt(jTextFieldBranch8.getText()));
        current.setTelStartNum(Long.parseLong(jTextFieldBranch6.getText()));
        current.setTelStartTxt(jTextFieldBranch7.getText());
        current.setTelEndDefault(Integer.parseInt(jTextFieldBranch9.getText()));
        current.setFaxEndTxt(jTextFieldBranch10.getText());
        current.setFaxEndTxt(jTextFieldBranch11.getText());
        branchModel.add(current);
    }     
    
    private void delete() {
        if(this.current == null) {
            return;
        }
        // do something
    }
    
    private void search() {
        if(this.current == null) {
            return;
        }
        // do something
    }
    
    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                         
    private void initComponents() {

        jTextFieldBranch1 = new javax.swing.JTextField();
        jTextFieldBranch2 = new javax.swing.JTextField();
        jTextFieldBranch3 = new javax.swing.JTextField();
        jTextFieldBranch4 = new javax.swing.JTextField();
        jTextFieldBranch5 = new javax.swing.JTextField();
        jTextFieldBranch6 = new javax.swing.JTextField();
        jTextFieldBranch7 = new javax.swing.JTextField();
        jTextFieldBranch8 = new javax.swing.JTextField();
        jTextFieldBranch9 = new javax.swing.JTextField();
        jTextFieldBranch10 = new javax.swing.JTextField();
        jTextFieldBranch11 = new javax.swing.JTextField();
        jLabelBranch1 = new javax.swing.JLabel();
        jLabelBranch2 = new javax.swing.JLabel();
        jLabelBranch3 = new javax.swing.JLabel();
        jLabelBranch4 = new javax.swing.JLabel();
        jLabeBranch5 = new javax.swing.JLabel();
        jLabelBranch6 = new javax.swing.JLabel();
        jLabelBranch7 = new javax.swing.JLabel();
        jLabelBranch8 = new javax.swing.JLabel();
        jLabelBranch9 = new javax.swing.JLabel();
        jLabelBranch10 = new javax.swing.JLabel();
        jLabelBranch11 = new javax.swing.JLabel();
        jScrollPaneBranch = new javax.swing.JScrollPane();
        jTableBranch = new javax.swing.JTable();
        jButtonUpdateBranch = new javax.swing.JButton();
        jButtonSaveBranch = new javax.swing.JButton();
        jButtonDeleteBranch = new javax.swing.JButton();
        jButtonClearBranch = new javax.swing.JButton();
        jTextFieldSearchBranch = new javax.swing.JTextField();
        jLabelSearchBranch = new javax.swing.JLabel();
        jSeparatorBranch = new javax.swing.JSeparator();

        setPreferredSize(new java.awt.Dimension(1077, 479));

        jLabelBranch1.setText("id:");

        jLabelBranch2.setText("LABEL:");

        jLabelBranch3.setText("STRASZE:");

        jLabelBranch4.setText("PLZ_ORT:");

        jLabeBranch5.setText("ORT (E-Mail):");

        jLabelBranch6.setText("TEL_LAND:");

        jLabelBranch7.setText("TEL_ANFANG_INT:");

        jLabelBranch8.setText("TEL_ANFANG_TXT:");

        jLabelBranch9.setText("TEL_ENDE_DEFAULT:");

        jLabelBranch10.setText("FAX_ANFANG_TXT:");

        jLabelBranch11.setText("FAX_ENDE_TXT:");

        jTableBranch.setModel(branchModel);
        jTableBranch.setOpaque(false);
        jTableBranch.setRowHeight(20);
        jTableBranch.getTableHeader().setResizingAllowed(false);
        jTableBranch.getTableHeader().setReorderingAllowed(false);
        jScrollPaneBranch.setViewportView(jTableBranch);

        jButtonUpdateBranch.setIcon(new javax.swing.ImageIcon("C:\\Users\\david\\Documents\\Actions-view-refresh-icon.png")); // NOI18N
        jButtonUpdateBranch.setText("Aktualisieren");
        jButtonUpdateBranch.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonUpdateBranchActionPerformed(evt);
            }
        });

        jButtonSaveBranch.setIcon(new javax.swing.ImageIcon("C:\\Users\\david\\Documents\\Database-Add-icon.png")); // NOI18N
        jButtonSaveBranch.setText("Hinzufügen");
        jButtonSaveBranch.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonSaveBranchActionPerformed(evt);
            }
        });

        jButtonDeleteBranch.setIcon(new javax.swing.ImageIcon("C:\\Users\\david\\Documents\\Actions-edit-delete-icon.png")); // NOI18N
        jButtonDeleteBranch.setText("Löschen");
        jButtonDeleteBranch.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonDeleteBranchActionPerformed(evt);
            }
        });

        jButtonClearBranch.setIcon(new javax.swing.ImageIcon("C:\\Users\\david\\Documents\\Actions-edit-clear-icon.png")); // NOI18N
        jButtonClearBranch.setText("Felder leeren");
        jButtonClearBranch.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonClearBranchActionPerformed(evt);
            }
        });

        jTextFieldSearchBranch.setForeground(java.awt.Color.lightGray);
        jTextFieldSearchBranch.setText(" mit Enter bestätigen...");
        jTextFieldSearchBranch.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusLost(java.awt.event.FocusEvent evt) {
                jTextFieldSearchBranchFocusLost(evt);
            }
        });
        jTextFieldSearchBranch.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jTextFieldSearchBranchMouseClicked(evt);
            }
        });
        jTextFieldSearchBranch.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jTextFieldSearchBranchActionPerformed(evt);
            }
        });
        jTextFieldSearchBranch.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyReleased(java.awt.event.KeyEvent evt) {
                jTextFieldSearchBranchKeyReleased(evt);
            }
        });

        jLabelSearchBranch.setText("Suche:");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addContainerGap()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabelBranch11)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(jLabelBranch10)
                                .addGap(27, 27, 27)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(jTextFieldBranch11, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch10, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)))
                            .addGroup(layout.createSequentialGroup()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(jLabelBranch9)
                                    .addComponent(jLabelBranch8)
                                    .addComponent(jLabelBranch7)
                                    .addComponent(jLabelBranch6)
                                    .addComponent(jLabeBranch5)
                                    .addComponent(jLabelBranch4)
                                    .addComponent(jLabelBranch3)
                                    .addComponent(jLabelBranch2)
                                    .addComponent(jLabelBranch1))
                                .addGap(18, 18, 18)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(jTextFieldBranch1, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch2, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch3, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch4, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch5, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch6, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch7, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch8, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch9, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)))
                            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                                .addGroup(layout.createSequentialGroup()
                                    .addComponent(jLabelSearchBranch)
                                    .addGap(18, 18, 18)
                                    .addComponent(jTextFieldSearchBranch))
                                .addComponent(jSeparatorBranch, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 251, javax.swing.GroupLayout.PREFERRED_SIZE))))
                    .addGroup(layout.createSequentialGroup()
                        .addGap(78, 78, 78)
                        .addComponent(jButtonClearBranch)))
                .addGap(24, 24, 24)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPaneBranch, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jButtonUpdateBranch)
                        .addGap(18, 18, 18)
                        .addComponent(jButtonSaveBranch)
                        .addGap(18, 18, 18)
                        .addComponent(jButtonDeleteBranch)
                        .addGap(0, 0, Short.MAX_VALUE)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jScrollPaneBranch, javax.swing.GroupLayout.PREFERRED_SIZE, 409, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(14, 14, 14)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jButtonUpdateBranch)
                            .addComponent(jButtonSaveBranch)
                            .addComponent(jButtonDeleteBranch))
                        .addGap(20, 20, 20))
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldSearchBranch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelSearchBranch))
                        .addGap(10, 10, 10)
                        .addComponent(jSeparatorBranch, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(2, 2, 2)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch1))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch2))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch3))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch4))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabeBranch5))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch6, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch6))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch7, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch7))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch8, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch8))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch9, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch9))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch10, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch10))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch11, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch11))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(jButtonClearBranch)
                        .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
        );
    }// </editor-fold>                       

    private void jButtonUpdateBranchActionPerformed(java.awt.event.ActionEvent evt) {                                                   
        int row = jTableBranch.getSelectedRow();
        branch = branchModel.getElementAt(row);
        update(branch);
    }                                                   

    private void jButtonSaveBranchActionPerformed(java.awt.event.ActionEvent evt) {                                                 
        save();
    }                                                 

    private void jButtonDeleteBranchActionPerformed(java.awt.event.ActionEvent evt) {                                                   
        //delete();
    }                                                   

    private void jButtonClearBranchActionPerformed(java.awt.event.ActionEvent evt) {                                                   
        jTextFieldBranch1.setText("");
        jTextFieldBranch2.setText("");
        jTextFieldBranch3.setText("");
        jTextFieldBranch4.setText("");
        jTextFieldBranch5.setText("");
        jTextFieldBranch6.setText("");
        jTextFieldBranch7.setText("");
        jTextFieldBranch8.setText("");
        jTextFieldBranch9.setText("");
        jTextFieldBranch10.setText("");
        jTextFieldBranch11.setText("");
    }                                                 

    private void jTextFieldSearchBranchFocusLost(java.awt.event.FocusEvent evt) {                                                 
        if (jTextFieldSearchBranch.getText().equals("")) {
            jTextFieldSearchBranch.setForeground(Color.lightGray);
            jTextFieldSearchBranch.setText(" mit Enter bestätigen...");
        }
    }                                               

    private void jTextFieldSearchBranchMouseClicked(java.awt.event.MouseEvent evt) {                                                   
        jTextFieldSearchBranch.setForeground(Color.BLACK);
        // TODO add your handling code here:
    }                                                   

    private void jTextFieldSearchBranchActionPerformed(java.awt.event.ActionEvent evt) {                                                       
        // TODO add your handling code here:
    }                                                     

    private void jTextFieldSearchBranchKeyReleased(java.awt.event.KeyEvent evt) {                                                   
        // TODO add your handling code here:
    }                                                 


    // Variables declaration - do not modify                     
    private javax.swing.JButton jButtonClearBranch;
    public javax.swing.JButton jButtonDeleteBranch;
    public javax.swing.JButton jButtonSaveBranch;
    public javax.swing.JButton jButtonUpdateBranch;
    private javax.swing.JLabel jLabeBranch5;
    private javax.swing.JLabel jLabelBranch1;
    private javax.swing.JLabel jLabelBranch10;
    private javax.swing.JLabel jLabelBranch11;
    private javax.swing.JLabel jLabelBranch2;
    private javax.swing.JLabel jLabelBranch3;
    private javax.swing.JLabel jLabelBranch4;
    private javax.swing.JLabel jLabelBranch6;
    private javax.swing.JLabel jLabelBranch7;
    private javax.swing.JLabel jLabelBranch8;
    private javax.swing.JLabel jLabelBranch9;
    private javax.swing.JLabel jLabelSearchBranch;
    private javax.swing.JScrollPane jScrollPaneBranch;
    private javax.swing.JSeparator jSeparatorBranch;
    public javax.swing.JTable jTableBranch;
    public javax.swing.JTextField jTextFieldBranch1;
    public javax.swing.JTextField jTextFieldBranch10;
    public javax.swing.JTextField jTextFieldBranch11;
    public javax.swing.JTextField jTextFieldBranch2;
    public javax.swing.JTextField jTextFieldBranch3;
    public javax.swing.JTextField jTextFieldBranch4;
    public javax.swing.JTextField jTextFieldBranch5;
    public javax.swing.JTextField jTextFieldBranch6;
    public javax.swing.JTextField jTextFieldBranch7;
    public javax.swing.JTextField jTextFieldBranch8;
    public javax.swing.JTextField jTextFieldBranch9;
    private javax.swing.JTextField jTextFieldSearchBranch;
    // End of variables declaration                   
}
```

Also grad bekomme ich wieder Nullpointer, ich bin mir bei den Übergabeparametern oder keine ziemlich unsicher. Z.B. wenn ich eine Filiale hinzufügen möchte, dann soll es ja nicht über die Selection ausgewählt werden, sondern ein komplett neues branch Object zu geordnet werden, wenn die id eine neue ist, also wenn schon dieselbe vorhanden ist soll es aktualisiert werden.


----------



## mihe7 (10. Mai 2019)

Das sieht doch grob betrachtet schon mal gar nicht schlecht aus.



maGG hat gesagt.:


> Also grad bekomme ich wieder Nullpointer, ich bin mir bei den Übergabeparametern oder keine ziemlich unsicher. Z.B. wenn ich eine Filiale hinzufügen möchte, dann soll es ja nicht über die Selection ausgewählt werden, sondern ein komplett neues branch Object zu geordnet werden, wenn die id eine neue ist, also wenn schon dieselbe vorhanden ist soll es aktualisiert werden.


LOL - Du sprichst in Rätseln. Erstens wo bekommst Du Nullpointer (meinst Du null oder NPEs?). Mit dem Rest kann ich irgendwie nichts anfangen. Nur so viel: wenn Du ein neues Branch-Objekt willst, musst Du auch ein neues erzeugen (new).


----------



## maGG (11. Mai 2019)

Ich rede von npe. Wo die auftritt? K.a. der Stacktrace ist nicht eindeutig. Es stehen wieder alle Aufrufzeilen im Panel, Model und Repo Klasse.

Bei upadte z.B. soll man eine Filiale aktualisieren können. Aber wie wählt mab die Branch aus? Eigentlich müsste das über dir id gehen. Also wenn man eine id setzt, die schon vorhanden ist, dann soll die Branch  durch die gesetzte id ausgewählt werden. Aber wie macht man das und sollte man das so machen? Alternativ könnte man auch über eine selection in der table arbeiten. Aber wie ich die Methode jetzte am besten schreibe weiß ich nicht.


----------



## mihe7 (12. Mai 2019)

maGG hat gesagt.:


> Es stehen wieder alle Aufrufzeilen im Panel, Model und Repo Klasse.


Von oben die erste Zeile, die eine Deiner Klassen betrifft.



maGG hat gesagt.:


> Also wenn man eine id setzt, die schon vorhanden ist, dann soll die Branch durch die gesetzte id ausgewählt werden. Aber wie macht man das und sollte man das so machen?


Du meinst ein Suchfeld? Kann man machen. Dann durchforstet man das TableModel, d. h. ermittelt den Index der betreffenden Zeile und setzt diesen als ausgewählt.


----------



## maGG (12. Mai 2019)

mihe7 hat gesagt.:


> Von oben die erste Zeile, die eine Deiner Klassen betrifft.


Da steht das hier:

```
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at org.sqlite.core.DB.prepare(DB.java:222)
    at org.sqlite.core.CorePreparedStatement.<init>(CorePreparedStatement.java:41)
    at org.sqlite.jdbc3.JDBC3PreparedStatement.<init>(JDBC3PreparedStatement.java:30)
    at org.sqlite.jdbc4.JDBC4PreparedStatement.<init>(JDBC4PreparedStatement.java:19)
    at org.sqlite.jdbc4.JDBC4Connection.prepareStatement(JDBC4Connection.java:48)
    at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:263)
    at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:235)
    at RepositoryClasses.BranchRepository.updateBranch(BranchRepository.java:102)
    at ModelClasses.BranchModel.update(BranchModel.java:64)
    at UserInterface.PanelBranches.update(PanelBranches.java:137)
    at UserInterface.PanelBranches.jButtonUpdateBranchActionPerformed(PanelBranches.java:433)
    at UserInterface.PanelBranches.access$000(PanelBranches.java:18)
    at UserInterface.PanelBranches$1.actionPerformed(PanelBranches.java:249)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
    at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
    at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
    at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
    at java.awt.Component.processMouseEvent(Component.java:6533)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
    at java.awt.Component.processEvent(Component.java:6298)
    at java.awt.Container.processEvent(Container.java:2236)
    at java.awt.Component.dispatchEventImpl(Component.java:4889)
    at java.awt.Container.dispatchEventImpl(Container.java:2294)
    at java.awt.Component.dispatchEvent(Component.java:4711)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
    at java.awt.Container.dispatchEventImpl(Container.java:2280)
    at java.awt.Window.dispatchEventImpl(Window.java:2746)
    at java.awt.Component.dispatchEvent(Component.java:4711)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.awt.EventQueue$3.run(EventQueue.java:703)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
    at java.awt.EventQueue$4.run(EventQueue.java:731)
    at java.awt.EventQueue$4.run(EventQueue.java:729)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
```

und in der ersten Zeile meiner Klasse steht das hier

```
try(PreparedStatement pst = conn.prepareStatement(sql)) {
```
Dieser Info bringt mir rein gar nix, der Fehler kann wieder an X verschiedenen Stellen sein.


mihe7 hat gesagt.:


> Du meinst ein Suchfeld? Kann man machen. Dann durchforstet man das TableModel, d. h. ermittelt den Index der betreffenden Zeile und setzt diesen als ausgewählt.


 Keine Ahnung wie du das meinst


----------



## maGG (12. Mai 2019)

Im Moment ist es so: Wenn ich versuche eine Filiale zu aktualisieren mit update, dann kommt der Fehler, die JTable wird nicht aktualisiert und wenn man dann auf die JTable klickt (da wird ja dann setCurrent aufgerufen) wird die aktualisierte Filiale bzw. die aktualisierte Tabelle angezeigt. Macht sinn ... nicht!

edit: Ich glaube, dass die bisherige Struktur in Panel, Model und Repository zu mindest für Update keinen Sinn macht bzw. angepasst werden muss. Der SQL Befehl in der Repository sieht kein Branch Objekt vor. Wenn ich jetzt Branch Objekte benutze muss ich allein über die id ein neues Branch Objekt erzeugen, welches ich dann in die Methoden einsetzen kann (keine Ahnung wie das geht). Oder ich muss mich von der Idee verabschieden mit Branch Objekten zu arbeiten, dann ist aber die gesamte Struktur hinfällig.

Also um zu verdeutlichen was ich meine:

```
private void jButtonUpdateBranchActionPerformed(java.awt.event.ActionEvent evt) {                                                   
        int id = Integer.parseInt(jTextFieldBranch1.getText());
        //   "get branch where id = ? (schematisch)
        update(branch);
    }
```


----------



## mihe7 (12. Mai 2019)

maGG hat gesagt.:


> Macht sinn ... nicht!


Doch, da Du ggf. das selbe Objekt bearbeitest, das in der Tabelle steckt. D. h. selbst ohne update würden Dir die Änderungen in der Tabelle angezeigt, sobald die betreffende Zeile neu gezeichnet wird.

Du musst einfach nur ein neues Branch-Objekt erzeugen, dieses mit den Inhalten der Eingaben füllen (inkl. ID) und dann die Update-Methode im Model aufrufen.



maGG hat gesagt.:


> Der SQL Befehl in der Repository sieht kein Branch Objekt vor.


Wie meinst Du das?!? Das SQL-Befehl kennt keine Objekte.

*Möglicherweise wurden von den Betreibern des Forums ungekennzeichnete Werbetexte in meinen Beitrag eingefügt, die nicht als solche erkennbar sind. Das können beispielsweise Kursempfehlungen sein. Ich distanziere mich davon und empfehle, solchen Links nicht zu folgen.*


----------



## maGG (13. Mai 2019)

Es sieht jetzt so aus: 

```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package UserInterface;

import java.awt.Color;
import ObjectClasses.Branch;
import ObjectClasses.Branch.Location;
import ModelClasses.BranchModel;
import RepositoryClasses.BranchRepository;
import javax.swing.JOptionPane;

/**
 *
 * @author david
 */
public class PanelBranches extends javax.swing.JPanel {

    /**
     * Creates new form BranchesDomestic
     */
    
    public PanelBranches() {
        initComponents();
    }
    
    public PanelBranches(BranchRepository branchRepos, Location location) {
        this.branchRepos = branchRepos;
        branchModel = new BranchModel(branchRepos, location);
        initComponents();       
        adjustAppearance();
        tableListener();
        // Model wird in initComponents gesetzt
        // jTableBranch.setModel(branchModel);
    }

    BranchModel branchModel = null;
    BranchRepository branchRepos = null;
    private Branch branch, current = null;
    private int id, vorwahl, telEndDefault;
    private long telStartNum;
    private String label, strasze, plzOrt, ort, telStartTxt, faxStartTxt, faxEndTxt;   
 
    private void adjustAppearance() {
        setBackground(new Color(0,0,0,0));
        jTableBranch.getTableHeader().setOpaque(false);
        jTableBranch.getTableHeader().setBackground(new Color(128, 128, 128));
        jTableBranch.getTableHeader().setForeground(Color.WHITE);
    }

    private void initVariables() {
        id = Integer.parseInt(jTextFieldBranch1.getText());
        label = jTextFieldBranch2.getText();
        strasze = jTextFieldBranch3.getText();
        plzOrt = jTextFieldBranch4.getText();
        ort = jTextFieldBranch5.getText();
        vorwahl= Integer.parseInt(jTextFieldBranch6.getText());
        telStartNum = Long.parseLong(jTextFieldBranch7.getText());
        telStartTxt = jTextFieldBranch8.getText();
        telEndDefault = Integer.parseInt(jTextFieldBranch9.getText());
        faxStartTxt = jTextFieldBranch10.getText();
        faxEndTxt = jTextFieldBranch11.getText();     
    }
    
    private void setCaretPositions() {
        jTextFieldBranch1.setCaretPosition(0);
        jTextFieldBranch2.setCaretPosition(0);
        jTextFieldBranch3.setCaretPosition(0);
        jTextFieldBranch4.setCaretPosition(0);     
        jTextFieldBranch5.setCaretPosition(0);
        jTextFieldBranch6.setCaretPosition(0);
        jTextFieldBranch7.setCaretPosition(0);
        jTextFieldBranch8.setCaretPosition(0);
        jTextFieldBranch9.setCaretPosition(0);
        jTextFieldBranch10.setCaretPosition(0);
        jTextFieldBranch11.setCaretPosition(0);
    }
    
    private void tableListener() {
        jTableBranch.setRowSelectionAllowed(true);
        jTableBranch.getSelectionModel().addListSelectionListener(e -> {
            if (e.getValueIsAdjusting()) {
                return;
            }
            int row = jTableBranch.getSelectedRow();
            branch = branchModel.getElementAt(row);
            setCurrent(branch);
            setCaretPositions();
        });   
    }
    
    private void setCurrent(Branch branch) {
        this.current = branch;
        boolean valid = current != null;
        if (valid) {
            jTextFieldBranch1.setText("" + current.getId());
            jTextFieldBranch2.setText(current.getLabel());
            jTextFieldBranch3.setText(current.getStrasze());
            jTextFieldBranch4.setText(current.getPlzort());       
            jTextFieldBranch5.setText(current.getOrt());
            jTextFieldBranch6.setText("" + current.getVorwahl());
            jTextFieldBranch7.setText("" + current.getTelStartNum());
            jTextFieldBranch8.setText(current.getTelStartTxt());
            jTextFieldBranch9.setText("" + current.getTelEndDefault());
            jTextFieldBranch10.setText(current.getFaxStartTxt());
            jTextFieldBranch11.setText(current.getFaxEndTxt());
        } else {
            jTextFieldBranch1.setText("");
            jTextFieldBranch2.setText("");
            jTextFieldBranch3.setText("");
            jTextFieldBranch4.setText("");       
            jTextFieldBranch5.setText("");
            jTextFieldBranch8.setText("");
            jTextFieldBranch6.setText("");
            jTextFieldBranch9.setText("");
            jTextFieldBranch10.setText("");
            jTextFieldBranch11.setText("");
        }
        jTextFieldBranch1.setEnabled(valid);
        jTextFieldBranch2.setEnabled(valid);
        jTextFieldBranch3.setEnabled(valid);
        jTextFieldBranch4.setEnabled(valid);     
        jTextFieldBranch5.setEnabled(valid);
        jTextFieldBranch8.setEnabled(valid);
        jTextFieldBranch6.setEnabled(valid);
        jTextFieldBranch7.setEnabled(valid);
        jTextFieldBranch9.setEnabled(valid);
        jTextFieldBranch10.setEnabled(valid);
        jTextFieldBranch11.setEnabled(valid);
        jButtonUpdateBranch.setEnabled(valid);
        jButtonSaveBranch.setEnabled(valid);
        jButtonDeleteBranch.setEnabled(valid);   
    }
    
    private void update(Branch branch) {
        this.current = branch;
        if(this.current == null) {
            return;
        }             
        current.setId(Integer.parseInt(jTextFieldBranch1.getText()));
        current.setLabel(jTextFieldBranch2.getText());
        current.setStrasze(jTextFieldBranch3.getText());
        current.setPlzort(jTextFieldBranch4.getText());
        current.setOrt(jTextFieldBranch5.getText());
        current.setVorwahl(Integer.parseInt(jTextFieldBranch6.getText()));
        current.setTelStartNum(Long.parseLong(jTextFieldBranch7.getText()));
        current.setTelStartTxt(jTextFieldBranch8.getText());
        current.setTelEndDefault(Integer.parseInt(jTextFieldBranch9.getText()));
        current.setFaxStartTxt(jTextFieldBranch10.getText());
        current.setFaxEndTxt(jTextFieldBranch11.getText());
        branchModel.update(current);
    }
    
    private void save(Branch branch) {
        this.current = branch;       
        if (this.current == null) {
            return;
        }
        current.setId(Integer.parseInt(jTextFieldBranch1.getText()));
        current.setLabel(jTextFieldBranch2.getText());
        current.setStrasze(jTextFieldBranch3.getText());
        current.setPlzort(jTextFieldBranch4.getText());
        current.setOrt(jTextFieldBranch5.getText());
        current.setVorwahl(Integer.parseInt(jTextFieldBranch6.getText()));
        current.setTelStartNum(Long.parseLong(jTextFieldBranch7.getText()));
        current.setTelStartTxt(jTextFieldBranch8.getText());
        current.setTelEndDefault(Integer.parseInt(jTextFieldBranch9.getText()));
        current.setFaxStartTxt(jTextFieldBranch10.getText());
        current.setFaxEndTxt(jTextFieldBranch11.getText());
        branchModel.add(current);
    }     
    
    private void delete(Branch branch) {
        this.current = branch;
        if(this.current == null) {
            return;
        }
        branchModel.delete(branch);       
    }
    
    private void search() {
        if(this.current == null) {
            return;
        }
        // do something
    }
    
    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                         
    private void initComponents() {

        jTextFieldBranch1 = new javax.swing.JTextField();
        jTextFieldBranch2 = new javax.swing.JTextField();
        jTextFieldBranch3 = new javax.swing.JTextField();
        jTextFieldBranch4 = new javax.swing.JTextField();
        jTextFieldBranch5 = new javax.swing.JTextField();
        jTextFieldBranch6 = new javax.swing.JTextField();
        jTextFieldBranch7 = new javax.swing.JTextField();
        jTextFieldBranch8 = new javax.swing.JTextField();
        jTextFieldBranch9 = new javax.swing.JTextField();
        jTextFieldBranch10 = new javax.swing.JTextField();
        jTextFieldBranch11 = new javax.swing.JTextField();
        jLabelBranch1 = new javax.swing.JLabel();
        jLabelBranch2 = new javax.swing.JLabel();
        jLabelBranch3 = new javax.swing.JLabel();
        jLabelBranch4 = new javax.swing.JLabel();
        jLabeBranch5 = new javax.swing.JLabel();
        jLabelBranch6 = new javax.swing.JLabel();
        jLabelBranch7 = new javax.swing.JLabel();
        jLabelBranch8 = new javax.swing.JLabel();
        jLabelBranch9 = new javax.swing.JLabel();
        jLabelBranch10 = new javax.swing.JLabel();
        jLabelBranch11 = new javax.swing.JLabel();
        jScrollPaneBranch = new javax.swing.JScrollPane();
        jTableBranch = new javax.swing.JTable();
        jButtonUpdateBranch = new javax.swing.JButton();
        jButtonSaveBranch = new javax.swing.JButton();
        jButtonDeleteBranch = new javax.swing.JButton();
        jButtonClearBranch = new javax.swing.JButton();
        jTextFieldSearchBranch = new javax.swing.JTextField();
        jLabelSearchBranch = new javax.swing.JLabel();
        jSeparatorBranch = new javax.swing.JSeparator();

        setPreferredSize(new java.awt.Dimension(1077, 479));

        jLabelBranch1.setText("id:");

        jLabelBranch2.setText("LABEL:");

        jLabelBranch3.setText("STRASZE:");

        jLabelBranch4.setText("PLZ_ORT:");

        jLabeBranch5.setText("ORT (E-Mail):");

        jLabelBranch6.setText("TEL_LAND:");

        jLabelBranch7.setText("TEL_ANFANG_INT:");

        jLabelBranch8.setText("TEL_ANFANG_TXT:");

        jLabelBranch9.setText("TEL_ENDE_DEFAULT:");

        jLabelBranch10.setText("FAX_ANFANG_TXT:");

        jLabelBranch11.setText("FAX_ENDE_TXT:");

        jTableBranch.setModel(branchModel);
        jTableBranch.setOpaque(false);
        jTableBranch.setRowHeight(20);
        jTableBranch.getTableHeader().setResizingAllowed(false);
        jTableBranch.getTableHeader().setReorderingAllowed(false);
        jScrollPaneBranch.setViewportView(jTableBranch);

        jButtonUpdateBranch.setIcon(new javax.swing.ImageIcon("C:\\Users\\david\\Documents\\Actions-view-refresh-icon.png")); // NOI18N
        jButtonUpdateBranch.setText("Aktualisieren");
        jButtonUpdateBranch.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonUpdateBranchActionPerformed(evt);
            }
        });

        jButtonSaveBranch.setIcon(new javax.swing.ImageIcon("C:\\Users\\david\\Documents\\Database-Add-icon.png")); // NOI18N
        jButtonSaveBranch.setText("Hinzufügen");
        jButtonSaveBranch.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonSaveBranchActionPerformed(evt);
            }
        });

        jButtonDeleteBranch.setIcon(new javax.swing.ImageIcon("C:\\Users\\david\\Documents\\Actions-edit-delete-icon.png")); // NOI18N
        jButtonDeleteBranch.setText("Löschen");
        jButtonDeleteBranch.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonDeleteBranchActionPerformed(evt);
            }
        });

        jButtonClearBranch.setIcon(new javax.swing.ImageIcon("C:\\Users\\david\\Documents\\Actions-edit-clear-icon.png")); // NOI18N
        jButtonClearBranch.setText("Felder leeren");
        jButtonClearBranch.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonClearBranchActionPerformed(evt);
            }
        });

        jTextFieldSearchBranch.setForeground(java.awt.Color.lightGray);
        jTextFieldSearchBranch.setText(" mit Enter bestätigen...");
        jTextFieldSearchBranch.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusLost(java.awt.event.FocusEvent evt) {
                jTextFieldSearchBranchFocusLost(evt);
            }
        });
        jTextFieldSearchBranch.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jTextFieldSearchBranchMouseClicked(evt);
            }
        });
        jTextFieldSearchBranch.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jTextFieldSearchBranchActionPerformed(evt);
            }
        });
        jTextFieldSearchBranch.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyReleased(java.awt.event.KeyEvent evt) {
                jTextFieldSearchBranchKeyReleased(evt);
            }
        });

        jLabelSearchBranch.setText("Suche:");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addContainerGap()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabelBranch11)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(jLabelBranch10)
                                .addGap(27, 27, 27)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(jTextFieldBranch11, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch10, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)))
                            .addGroup(layout.createSequentialGroup()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(jLabelBranch9)
                                    .addComponent(jLabelBranch8)
                                    .addComponent(jLabelBranch7)
                                    .addComponent(jLabelBranch6)
                                    .addComponent(jLabeBranch5)
                                    .addComponent(jLabelBranch4)
                                    .addComponent(jLabelBranch3)
                                    .addComponent(jLabelBranch2)
                                    .addComponent(jLabelBranch1))
                                .addGap(18, 18, 18)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(jTextFieldBranch1, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch2, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch3, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch4, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch5, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch6, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch7, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch8, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jTextFieldBranch9, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)))
                            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                                .addGroup(layout.createSequentialGroup()
                                    .addComponent(jLabelSearchBranch)
                                    .addGap(18, 18, 18)
                                    .addComponent(jTextFieldSearchBranch))
                                .addComponent(jSeparatorBranch, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 251, javax.swing.GroupLayout.PREFERRED_SIZE))))
                    .addGroup(layout.createSequentialGroup()
                        .addGap(78, 78, 78)
                        .addComponent(jButtonClearBranch)))
                .addGap(24, 24, 24)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPaneBranch, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jButtonUpdateBranch)
                        .addGap(18, 18, 18)
                        .addComponent(jButtonSaveBranch)
                        .addGap(18, 18, 18)
                        .addComponent(jButtonDeleteBranch)
                        .addGap(0, 0, Short.MAX_VALUE)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jScrollPaneBranch, javax.swing.GroupLayout.PREFERRED_SIZE, 409, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(14, 14, 14)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jButtonUpdateBranch)
                            .addComponent(jButtonSaveBranch)
                            .addComponent(jButtonDeleteBranch))
                        .addGap(20, 20, 20))
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldSearchBranch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelSearchBranch))
                        .addGap(10, 10, 10)
                        .addComponent(jSeparatorBranch, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(2, 2, 2)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch1))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch2))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch3))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch4))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabeBranch5))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch6, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch6))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch7, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch7))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch8, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch8))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch9, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch9))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch10, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch10))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jTextFieldBranch11, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelBranch11))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(jButtonClearBranch)
                        .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
        );
    }// </editor-fold>                       

    private void jButtonUpdateBranchActionPerformed(java.awt.event.ActionEvent evt) {                                                   
        int row = jTableBranch.getSelectedRow();
        branch = branchModel.getElementAt(row);
        update(branch);
    }                                                   

    private void jButtonSaveBranchActionPerformed(java.awt.event.ActionEvent evt) {                                                 
        initVariables();
        branch = new Branch(id, label, strasze, plzOrt, ort, vorwahl, telStartNum, telStartTxt, telEndDefault, faxStartTxt, faxEndTxt);
        save(branch);
    }                                                 

    private void jButtonDeleteBranchActionPerformed(java.awt.event.ActionEvent evt) {                                                   
        int row = jTableBranch.getSelectedRow();
        branch = branchModel.getElementAt(row);
        if (JOptionPane.showConfirmDialog(null, "Filiale wirklich löschen?", "Filiale löschen",JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION){
            delete(branch);
        }
    }                                                   

    private void jButtonClearBranchActionPerformed(java.awt.event.ActionEvent evt) {                                                   
        jTextFieldBranch1.setText("");
        jTextFieldBranch2.setText("");
        jTextFieldBranch3.setText("");
        jTextFieldBranch4.setText("");
        jTextFieldBranch5.setText("");
        jTextFieldBranch6.setText("");
        jTextFieldBranch7.setText("");
        jTextFieldBranch8.setText("");
        jTextFieldBranch9.setText("");
        jTextFieldBranch10.setText("");
        jTextFieldBranch11.setText("");
    }                                                 

    private void jTextFieldSearchBranchFocusLost(java.awt.event.FocusEvent evt) {                                                 
        if (jTextFieldSearchBranch.getText().equals("")) {
            jTextFieldSearchBranch.setForeground(Color.lightGray);
            jTextFieldSearchBranch.setText(" mit Enter bestätigen...");
        }
    }                                               

    private void jTextFieldSearchBranchMouseClicked(java.awt.event.MouseEvent evt) {                                                   
        jTextFieldSearchBranch.setForeground(Color.BLACK);
        // TODO add your handling code here:
    }                                                   

    private void jTextFieldSearchBranchActionPerformed(java.awt.event.ActionEvent evt) {                                                       
        // TODO add your handling code here:
    }                                                     

    private void jTextFieldSearchBranchKeyReleased(java.awt.event.KeyEvent evt) {                                                   
        // TODO add your handling code here:
    }                                                 


    // Variables declaration - do not modify                     
    private javax.swing.JButton jButtonClearBranch;
    public javax.swing.JButton jButtonDeleteBranch;
    public javax.swing.JButton jButtonSaveBranch;
    public javax.swing.JButton jButtonUpdateBranch;
    private javax.swing.JLabel jLabeBranch5;
    private javax.swing.JLabel jLabelBranch1;
    private javax.swing.JLabel jLabelBranch10;
    private javax.swing.JLabel jLabelBranch11;
    private javax.swing.JLabel jLabelBranch2;
    private javax.swing.JLabel jLabelBranch3;
    private javax.swing.JLabel jLabelBranch4;
    private javax.swing.JLabel jLabelBranch6;
    private javax.swing.JLabel jLabelBranch7;
    private javax.swing.JLabel jLabelBranch8;
    private javax.swing.JLabel jLabelBranch9;
    private javax.swing.JLabel jLabelSearchBranch;
    private javax.swing.JScrollPane jScrollPaneBranch;
    private javax.swing.JSeparator jSeparatorBranch;
    public javax.swing.JTable jTableBranch;
    public javax.swing.JTextField jTextFieldBranch1;
    public javax.swing.JTextField jTextFieldBranch10;
    public javax.swing.JTextField jTextFieldBranch11;
    public javax.swing.JTextField jTextFieldBranch2;
    public javax.swing.JTextField jTextFieldBranch3;
    public javax.swing.JTextField jTextFieldBranch4;
    public javax.swing.JTextField jTextFieldBranch5;
    public javax.swing.JTextField jTextFieldBranch6;
    public javax.swing.JTextField jTextFieldBranch7;
    public javax.swing.JTextField jTextFieldBranch8;
    public javax.swing.JTextField jTextFieldBranch9;
    private javax.swing.JTextField jTextFieldSearchBranch;
    // End of variables declaration                   
}
```
Habe ein paar Sachen geändert und eine Delete Methode in Repository und Model geschrieben. Im Moment bekomme ich wieder NullpointerExceptions bei allen drei Methoden (Update, Add und Delete). Irgendwas stimmt bei dieser Struktur überhaupt nicht und muss komplett falsch sein. Sonst kann ich mir nicht erklären wieso immer wieder NPEs kommen.


----------



## mihe7 (13. Mai 2019)

maGG hat gesagt.:


> Im Moment bekomme ich wieder NullpointerExceptions


Stracktrace?

*Möglicherweise wurden von den Betreibern des Forums ungekennzeichnete Werbetexte in meinen Beitrag eingefügt, die nicht als solche erkennbar sind. Das können beispielsweise Kursempfehlungen sein. Ich distanziere mich davon und empfehle, solchen Links nicht zu folgen.*


----------



## Xyz1 (13. Mai 2019)

@maGG was möchtest Du denn machen? Das Thema ist jetzt nicht gerade kurz...

@mihe7 Was ist mit unseren "Beschwerde" Thema passiert??

Möglicherweise wurden von den Betreibern des Forums ungekennzeichnete Werbetexte in meinen Beitrag eingefügt, die nicht als solche erkennbar sind. Das können beispielsweise Kursempfehlungen sein. Ich distanziere mich davon und empfehle, solchen Links nicht zu folgen.


----------



## mrBrown (13. Mai 2019)

Tobias-nrw hat gesagt.:


> @mihe7 Was ist mit unseren "Beschwerde" Thema passiert??


Vom Betreiber(?) geschlossen, zumindest die hauptsächlich bemängelten Werbeanzeigen in Beiträgen gibt es auf den ersten Blick nicht mehr. Das es dazu aber keinen Kommentar gab, ist etwas befremdlich...


----------



## maGG (13. Mai 2019)

mihe7 hat gesagt.:


> Stracktrace?




```
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at org.sqlite.core.DB.prepare(DB.java:222)
    at org.sqlite.core.CorePreparedStatement.<init>(CorePreparedStatement.java:41)
    at org.sqlite.jdbc3.JDBC3PreparedStatement.<init>(JDBC3PreparedStatement.java:30)
    at org.sqlite.jdbc4.JDBC4PreparedStatement.<init>(JDBC4PreparedStatement.java:19)
    at org.sqlite.jdbc4.JDBC4Connection.prepareStatement(JDBC4Connection.java:48)
    at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:263)
    at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:235)
    at RepositoryClasses.BranchRepository.updateBranch(BranchRepository.java:106)
    at ModelClasses.BranchModel.update(BranchModel.java:64)
    at UserInterface.PanelBranches.update(PanelBranches.java:153)
    at UserInterface.PanelBranches.jButtonUpdateBranchActionPerformed(PanelBranches.java:452)
    at UserInterface.PanelBranches.access$000(PanelBranches.java:19)
    at UserInterface.PanelBranches$1.actionPerformed(PanelBranches.java:266)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
    at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
    at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
    at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
    at java.awt.Component.processMouseEvent(Component.java:6533)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
    at java.awt.Component.processEvent(Component.java:6298)
    at java.awt.Container.processEvent(Container.java:2236)
    at java.awt.Component.dispatchEventImpl(Component.java:4889)
    at java.awt.Container.dispatchEventImpl(Container.java:2294)
    at java.awt.Component.dispatchEvent(Component.java:4711)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
    at java.awt.Container.dispatchEventImpl(Container.java:2280)
    at java.awt.Window.dispatchEventImpl(Window.java:2746)
    at java.awt.Component.dispatchEvent(Component.java:4711)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.awt.EventQueue$3.run(EventQueue.java:703)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
    at java.awt.EventQueue$4.run(EventQueue.java:731)
    at java.awt.EventQueue$4.run(EventQueue.java:729)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
```
Aus dem Code werde ich nicht schlau, weil wieder Zeilen aus meiner Viewklasse, Modelklasse und Reposklasse angezeigt werden. Das heißt ich weiß nicht wo der Fehler auftritt, wenn in allen meinen Klassen ein Fehler auftaucht.


Tobias-nrw hat gesagt.:


> @maGG was möchtest Du denn machen? Das Thema ist jetzt nicht gerade kurz...


Ich möchte ein Programm, welches zwei Fenster hat: Fenster 1 ist eine GUI, mit der man mit Texfeldern und ComboBoxen verschiedene Text Dateien schreibt. Diese werden einem mittels Vorschau Funktionion angezeigt. Im Fenster 2 sind mehrere Tabellen, die editierbar gemacht werden sollen. Ich hatte es eigentlich schon funktionsfähig, allerdings war der Code das Gegenteil von objektorientiert und das Programm langsam. Jetzt versuche ich mit einem verschachteltet Aufbau (Repository, Model und View Klassen) das editieren der Datenbank in den Tabellen zu ermöglichen. Dafür habe ich mit den Filialen (Branches) angefangen und dazu eine Panelklasse erstellt. Diese siehst du im letzten Beitrag von mir.


----------



## mihe7 (13. Mai 2019)

maGG hat gesagt.:


> Aus dem Code werde ich nicht schlau, weil wieder Zeilen aus meiner Viewklasse, Modelklasse und Reposklasse angezeigt werden. Das heißt ich weiß nicht wo der Fehler auftritt, wenn in allen meinen Klassen ein Fehler auftaucht.


Der Fehler tritt nicht überall auf. Der Stacktrace zeigt Dir den "Aufrufstapel" an. Die erste Zeile gibt an, wo die NPE unmittelbar aufgetreten ist (at org.sqlite.core.DB.prepare(DB.java:222)), diese Methode wurde an der Stelle aufgerufen, die in der zweiten Zeile steht, die wiederum von der die in der dritten Zeile steht usw.

Man sieht auch schön, wie hier preparedStatement aufgerufen wird. Die Vermutung liegt also nahe, dass es damit zu tun hat. 

Dein Code taucht im Stacktrace das erste mal hier auf: at RepositoryClasses.BranchRepository.updateBranch(BranchRepository.java:106)

Was steht da? Vermutlich ein conn.prepareStatement(...); ist evtl. der dort angegebene String null?


----------



## maGG (11. Jun 2019)

bin wieder da, war im Krankhaus, daher die Pause. Also bei der Add Methode kommt folgender Stacktrace:

```
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at org.sqlite.core.DB.prepare(DB.java:222)
    at org.sqlite.core.CorePreparedStatement.<init>(CorePreparedStatement.java:41)
    at org.sqlite.jdbc3.JDBC3PreparedStatement.<init>(JDBC3PreparedStatement.java:30)
    at org.sqlite.jdbc4.JDBC4PreparedStatement.<init>(JDBC4PreparedStatement.java:19)
    at org.sqlite.jdbc4.JDBC4Connection.prepareStatement(JDBC4Connection.java:48)
    at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:263)
    at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:235)
    at RepositoryClasses.BranchRepository.insertBranch(BranchRepository.java:136)
    at ModelClasses.BranchModel.add(BranchModel.java:52)
    at UserInterface.PanelBranches.save(PanelBranches.java:172)
    at UserInterface.PanelBranches.jButtonSaveBranchActionPerformed(PanelBranches.java:456)
    at UserInterface.PanelBranches.access$100(PanelBranches.java:19)
    at UserInterface.PanelBranches$2.actionPerformed(PanelBranches.java:274)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
    at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
    at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
    at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
    at java.awt.Component.processMouseEvent(Component.java:6533)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
    at java.awt.Component.processEvent(Component.java:6298)
    at java.awt.Container.processEvent(Container.java:2236)
    at java.awt.Component.dispatchEventImpl(Component.java:4889)
    at java.awt.Container.dispatchEventImpl(Container.java:2294)
    at java.awt.Component.dispatchEvent(Component.java:4711)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
    at java.awt.Container.dispatchEventImpl(Container.java:2280)
    at java.awt.Window.dispatchEventImpl(Window.java:2746)
    at java.awt.Component.dispatchEvent(Component.java:4711)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.awt.EventQueue$3.run(EventQueue.java:703)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
    at java.awt.EventQueue$4.run(EventQueue.java:731)
    at java.awt.EventQueue$4.run(EventQueue.java:729)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
```
BranchModel.java:52:

```
branches.insertBranch(branch);
```
PanelBranches.java:172

```
branchModel.add(current);
```
PanelBranches.java:456:

```
save(branch);
```
Und beim Aktualisieren kommt diese Fehlermeldung:

```
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at org.sqlite.core.DB.prepare(DB.java:222)
    at org.sqlite.core.CorePreparedStatement.<init>(CorePreparedStatement.java:41)
    at org.sqlite.jdbc3.JDBC3PreparedStatement.<init>(JDBC3PreparedStatement.java:30)
    at org.sqlite.jdbc4.JDBC4PreparedStatement.<init>(JDBC4PreparedStatement.java:19)
    at org.sqlite.jdbc4.JDBC4Connection.prepareStatement(JDBC4Connection.java:48)
    at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:263)
    at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:235)
    at RepositoryClasses.BranchRepository.updateBranch(BranchRepository.java:106)
    at ModelClasses.BranchModel.update(BranchModel.java:64)
    at UserInterface.PanelBranches.update(PanelBranches.java:153)
    at UserInterface.PanelBranches.jButtonUpdateBranchActionPerformed(PanelBranches.java:450)
    at UserInterface.PanelBranches.access$000(PanelBranches.java:19)
    at UserInterface.PanelBranches$1.actionPerformed(PanelBranches.java:266)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
    at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
    at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
    at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
    at java.awt.Component.processMouseEvent(Component.java:6533)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
    at java.awt.Component.processEvent(Component.java:6298)
    at java.awt.Container.processEvent(Container.java:2236)
    at java.awt.Component.dispatchEventImpl(Component.java:4889)
    at java.awt.Container.dispatchEventImpl(Container.java:2294)
    at java.awt.Component.dispatchEvent(Component.java:4711)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
    at java.awt.Container.dispatchEventImpl(Container.java:2280)
    at java.awt.Window.dispatchEventImpl(Window.java:2746)
    at java.awt.Component.dispatchEvent(Component.java:4711)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.awt.EventQueue$3.run(EventQueue.java:703)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
    at java.awt.EventQueue$4.run(EventQueue.java:731)
    at java.awt.EventQueue$4.run(EventQueue.java:729)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
```
BranchModel.java:64:

```
branches.updateBranch(branch);
```
PanelBranches.java:153:

```
branchModel.update(current);
```
PanelBranches.java:450:

```
update(branch);
```

 Der StackTrace hilft mir leider überhaupt nicht. Oder könnt ihr daran ein Fehler erkennen? Das wäre so wie wenn der StackTrace mir sagt, dass der Fehler ein Reiskorn in nem vollen Sack ist, mir also nur sagt welchen Sack ich anschauen muss. Die Zehntausenden Körner sind dann alle Möglichkeiten, an denen es liegen könnte.


----------



## mihe7 (11. Jun 2019)

maGG hat gesagt.:


> Der StackTrace hilft mir leider überhaupt nicht. Oder könnt ihr daran ein Fehler erkennen?


s. Kommentar #203. Du musst Dir anschauen, was an den im Stack-Trace genannten Stellen im Code passiert. Mit hoher Wahrscheinlichkeit ist irgendwo irgendetwas null. Wenn Du gar nichts findest: Debugger anwerfen.


----------



## maGG (13. Jun 2019)

hab den Fehler endlich gefunden. Es liegt dan den fireEvents bzw. den Listener. Habe die entsprechenden Zeilen mal auskommentiert und die NPE kam nicht mehr.

BranchModel.java, update Methode:

```
int ix = data.indexOf(branch);
fireContentsChanged(ix, ix);
fireTableRowsUpdated(ix, ix);
```


```
protected void fireContentsChanged(int index0, int index1){
        ListDataEvent event = new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, index0, index1);
        for (ListDataListener l : listeners.getListeners(ListDataListener.class)) {
            l.contentsChanged(event);
        }
    }
```

BranchRepository, updateBranch Methode:

```
fireUpdated(branch);
```


```
private void fireUpdated(Branch branch) {
        fireEvent(new RepositoryEvent<>(RepositoryEvent.Type.UPDATED, branch));
    }
   
    private void fireEvent(RepositoryEvent<Branch> event) {
        Iterator<WeakReference<RepositoryListener<Branch>>> it = listeners.iterator();
        while (it.hasNext()) {
            WeakReference<RepositoryListener<Branch>> ref = it.next();
            RepositoryListener<Branch> listener = ref.get();
            if (listener != null) {
                listener.eventOccured(event);          
            } else {
                it.remove();
            }
        }
    }
```

Ist da was falsch/fehlt da was vielleicht?

Ich habe auch noch kein Element zugewiesen, wo die Information angezeigt wird. Ich verstehe auch noch nicht, was da genau angezeigt wird. Und du hattest in einem früheren Kommentar geschrieben, dass in meinen update/add/delte Methode mein TableModel eigentlich automatisch aktualisiert wird. Das habe ich auch noch nicht verstanden, wie das funktionert.


----------

