# Daten aus DB -> JTable aktualisieren klappt nicht



## chrisUBER (15. Jul 2011)

Hi,

vor ein paar Wochen habe ich einen Thread eröffnet, indem es um die Anwendungs des Cardlayouts ging (Thread). Dank eurer Hilfe konnte ich das Problem lösen und es mMn auch relativ gut umsetzen 

Ok, habe das nun kurz angesprochen, da dies vielleicht auch mit meinem aktuellen Problem zu tun hat (Code dazu kommt weiter unten).

Mein aktuelles Problem: Ich habe mir aus einem JTable einen kleine Kalender gebastelt. Dieser Kalender holt sich aus einer Embedded DB (h2) die vorliegenden Termine und gibt diese an den entsprechenden Tagen wieder. Bei Programmstart funktioniert dieses einwandfrei. Ich kann zwischen den Monaten hin- und herschalten und die aktuellen Tage werden aus der Datenbank entnommen und korrekt dargestellt.

Jetzt habe ich zum Test ein kleines Formular gebastelt (noch ohne Eingabekontrollen etc) um neue Termine hinzuzufügen. Sobald ich nun einen Termin hinzufüge, wird dieser in die Datenbank eingetragen (funktioniert), jedoch wird das JTable mit dem Kalender nicht aktualisiert. Erst bei Neustart des Programmes oder beim hin- und herschalten der Monate wird der neue Termin angezeigt.

Habe versucht das Tablemodel mit fireTableDataChanged() zu aktualisieren, auch JTable.repaint(), revalidate() haben keinerlei Wirklung gezeigt.

Um nicht mein kompletten Chaoscode (bin noch Anfänger und kriege das MVC Konzept nicht sauber umgesetzt) zu posten, schreibe ich erstmal nur die Stellen, die mir wichtig erscheinen. Am Ende schreibe ich nochmal, wo ich die Fehler vermuten würde.

Klasse Navi: erstellt Buttons zur Auswahl der Cards inkl. Eventlistener

```
public Navi(Content mainPanel) {
        
        this.mainPanel = mainPanel;

        this.setBorder(javax.swing.BorderFactory.createTitledBorder("Menü"));
        this.setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.Y_AXIS));

        
        jButton1 = new javax.swing.JButton("Übersicht");
        jButton2 = new javax.swing.JButton("Veranstaltungen");


        jLabel1 = new JLabel();
        jLabel1.setVisible(false);

        jPanel1 = new JPanel();
       
        
        jButton1.addActionListener(this);
        jButton2.addActionListener(this);


        jButton1.setMaximumSize(new Dimension(150, 24));
        jButton2.setMaximumSize(new Dimension(150, 24));



     // In dieser Methode änderst du das anzuzeigende Panel
    // über die showPanel-Methode in mainPanel
    public void actionPerformed(ActionEvent e) {

        if(e.getActionCommand().equals("Übersicht"))
            mainPanel.showPanel("Overview");
 
        if(e.getActionCommand().equals("Veranstaltungen")) 
            mainPanel.showPanel("Events");
             
    }
  
}
```

Klasse Content: Setzt das Cardlayout und fügt Panels hinzu (Hier vermute ich evtl. einen Fehler)

```
public class Content  extends javax.swing.JPanel {

    private CardLayout cardLayout;
    public JPanel panel1, panel2, panel3, panel4, panel5, panel7;

    
    public Content() {


         //Panel für den Inhalt wird erstellt und mit CardLayout versehen
         cardLayout = new CardLayout();
         this.setLayout(cardLayout);

         //Es werden 3 Panels für den Inhalt erstellt (kann erweitert werden)
         panel1=new JPanel();
         panel2=new JPanel();

         //Panel "Karten" initialisieren
         panel1.add(new Overview());        //Panel: Übersicht
         panel2.add(new Events());          //Panel: Veranstaltungen


         //Karten dem Panel hinzufügen
         this.add(panel1,"Overview");
         this.add(panel2,"Events");

    }

    //gewählte Karte anzeigen
public void showPanel(String panel) {
         cardLayout.show(this, panel);
         
    }
```

Klasse Events: Um den Code kurz zu halten, poste ich hier nur die Events Klasse (Events wird über den Button Veranstaltungen aufgerufen. Es wird eine einfache JTable mit allen Terminen angezeigt)


```
public class Events extends javax.swing.JPanel  {

    DefaultTableModel model = new DefaultTableModel();
    JTable tab;
    
    /** Creates new form Events */
    public Events()  {
        initComponents();

        String query = "SELECT * from termine ORDER BY datum ASC";
        abfragen(query);        //SQL String an abfragen() übergeben
        myUpdate();
        setVisible(true);


   }

    /** 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() {

        jScrollPane1 = new javax.swing.JScrollPane();

        setBorder(javax.swing.BorderFactory.createTitledBorder("Veranstaltungen"));

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 574, Short.MAX_VALUE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 434, Short.MAX_VALUE)
                .addContainerGap())
        );
    }// </editor-fold>                        

    private void abfragen(String query)
    {
        Connection conn = Connect.driver();         //Verbindung herstellen
        Statement stm = Communication.stm(conn);    //Statement mit Verbindung erstellen
        ResultSet rs = Communication.rs(query,stm); //SQL String sowie Statement an Resultset übergeben, query wird ausgeführt
        tableModelEvents(rs);                       //ResultSet wird ans TM übergeben
        
         try {
                conn.close();
         }
                catch(Exception error){error.printStackTrace();}

     }

    private void tableModelEvents(ResultSet rs)
    {
        
               
        //Tabellenkopf Überschriften
        String datum,bez,kunde,pers,raum,beginn;
        datum="Datum";
        bez="Bezeichnung";
        kunde="Kunde";
        pers="Personen";
        raum="Raum";
        beginn="Beginn";
    
        //Tabellenkopf Überschriften zu addColumn
        model.addColumn(datum);
        model.addColumn(bez);
        model.addColumn(kunde);
        model.addColumn(pers);
        model.addColumn(raum);
        model.addColumn(beginn);


        //Vektor der Überschriften erstellen und ans TM übergeben
        Vector vectorHead = new Vector();
        vectorHead.add(datum);
        vectorHead.add(bez);
        vectorHead.add(kunde);
        vectorHead.add(pers);
        vectorHead.add(raum);
        vectorHead.add(beginn);
        model.setColumnIdentifiers(vectorHead);


        //Ausgabe der Datensätze in Vektor. Inhalte werden ans TM übergeben
        try{
            while(rs.next())
            {

                
                SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
                String dateFormat = formatter.format(rs.getDate("datum"));
                String switchDate[] = dateFormat.split("\\.");
                System.out.println(switchDate[0]);                          //Kontrollausgabe
               

                Vector vectorData = new Vector();
                vectorData.add(dateFormat);
                vectorData.add(rs.getString("bezeichnung"));
                vectorData.add(rs.getString("kunde"));
                vectorData.add(rs.getString("personen"));
                vectorData.add(rs.getString("raum"));
                vectorData.add(rs.getString("beginn"));
                model.addRow(vectorData);

                
             }
        }
        catch(Exception error){error.printStackTrace();}


       }
 //##############################
    public final void myUpdate() {

        System.out.println("bla");

        try {
                
        tab = new JTable(model) {

           public boolean isCellEditable(int x, int y) {   //Zellen nicht editierbar
                return false;
            }
        };

        //Tabelleneigenschaften (Anordnung bzw. Größe cht änderbar)
        tab.setRowHeight(25);
        tab.getTableHeader().setResizingAllowed(false);
        tab.getTableHeader().setReorderingAllowed(false);
        tab.setShowHorizontalLines(true);
        tab.setShowVerticalLines(false);
        tab.getModel();
        jScrollPane1.setViewportView(tab);
                

        } catch (Exception error){ System.out.println(error); }

           }

    
    // Variables declaration - do not modify                     
    private javax.swing.JScrollPane jScrollPane1;
    // End of variables declaration                   

}
```

Klasse NewEvent: Dies ist einfach nur ein Testformular um neue Datensätze während der Laufzeit des Programmes einzufügen. Es gibt keine Eingabekontrollen oder Sonstiges (also darauf achten, was man eingibt)

```
public class NewEvent extends javax.swing.JPanel {

    String datum, bezeichnung, kunde, beginn, raum, dekoration, aufbaubeginn;
    Integer personen;
    Boolean sekt, bedienung, dj, anzahlung;


    private PreparedStatement psInsert = null;
    private Overview Overview = null;
    private Events Events = null;

    /** Creates new form NewEvent */
    public NewEvent() {
        initComponents();
    }

private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                         
    getValues();        // TODO add your handling code here:
    addEvent();

    }                                        

   public void getValues() {
       datum = jTextField1.getText();
       bezeichnung = jTextField5.getText();
       kunde = jTextField2.getText();
       beginn = jTextField4.getText();
       raum = jComboBox1.getSelectedItem().toString();
       personen = Integer.parseInt(jTextField3.getText());
       sekt = jCheckBox1.isSelected();
       bedienung = jCheckBox2.isSelected();
       dj = jCheckBox3.isSelected();
       anzahlung = jCheckBox4.isSelected();
       dekoration = jTextArea1.getText();
       aufbaubeginn = jTextArea2.getText();
  }

  public void addEvent() {
      try {

            Connection conn = Connect.driver();
            Statement stm = Communication.stm(conn);

            String sql = "INSERT INTO TERMINE (datum, bezeichnung, kunde, "
                    + "personen, raum, beginn, sekt, bedienung, dj, anzahlung, "
                    + "dekoration, aufbaubeginn) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
            psInsert = conn.prepareStatement(sql);
            psInsert.setString(1, datum);
            psInsert.setString(2, bezeichnung);
            psInsert.setString(3, kunde);
            psInsert.setInt(4, personen);
            psInsert.setString(5, raum);
            psInsert.setString(6, beginn);
            psInsert.setBoolean(7, sekt);
            psInsert.setBoolean(8, bedienung);
            psInsert.setBoolean(9, dj);
            psInsert.setBoolean(10, anzahlung);
            psInsert.setString(11, dekoration);
            psInsert.setString(12, aufbaubeginn);
            psInsert.executeUpdate();
                     
            conn.close();
            } catch(Exception e) {
               
                System.out.println(e);
            }
      
     

  }
```

Das wars erstmal vom Code, hoffe habe das nötigste nicht rausgelöscht (wollte halt nicht das Forum sprengen )

Habe die ganzen fireTableDataChanged(), repaint() und revalidate() mal wieder rausgenommen. Habe diese Methoden zum Test echt überall mal eingefügt mit der Hoffnung, dass es irgendwann mal funktioniert. 

Ich vermute nun den Fehler bei: 1. fireTable... repaint und revalidate werden an den falschen Stellen aufgerufen bzw. nicht korrekt ans Tablemodel übergeben oder 2. in der Klasse Content werden die Panels einmal aufgerufen (z.b. durch new Events() und danach durch das Cardlayout nur angezeigt bzw. ausgeblendet. Die Klasse Events() wird also bei erneutem Aufruf nicht neu geladen bzw. aktualisiert sondern einfach nur wieder eingeblendet (mit den alten Daten). 

Das Gleiche Problem besteht halt auch bei meiner Overview Klasse (dort ist das JTable als Kalender dargestellt).

Und mal wieder ist mein Post recht lang geworden. Wusste nicht, wie ich mein Problem genauer schildern soll. Habe auch schon im Internet und in diesem Forum nach ähnlichen Problemen gesucht, jedoch hat mir nichts wirklich geholfen.

Mein Code ist relativ chaotisch, besonders weil ich das MVC-Konzept in einigen andere Klassen gut umsetzen konnte und dann teilweise wieder doch nicht. Hoffe jedoch, dass meine Codeschnipsel verständlich sind.

Ich hoffe auf erneute Hilfe eurerseits und bedanke mich im Voraus.

Mit freundlichem Gruß

chris


----------



## Michael... (15. Jul 2011)

chrisUBER hat gesagt.:


> Jetzt habe ich zum Test ein kleines Formular gebastelt (noch ohne Eingabekontrollen etc) um neue Termine hinzuzufügen. Sobald ich nun einen Termin hinzufüge, wird dieser in die Datenbank eingetragen (funktioniert), jedoch wird das JTable mit dem Kalender nicht aktualisiert. Erst bei Neustart des Programmes oder beim hin- und herschalten der Monate wird der neue Termin angezeigt.
> 
> Habe versucht das Tablemodel mit fireTableDataChanged() zu aktualisieren, auch JTable.repaint(), revalidate() haben keinerlei Wirklung gezeigt.


Woher soll die JTable bzw. deren Model denn wissen, dass in der Datenbank was neues drin steht? Wenn Du Änderungen an den Daten der DB vornimmst müssen die Daten eben erneut geladen werden (deswegen sind sie nach einem Hin- und Herschalten zu sehen)
Ist ja genauso wie wenn Du auf dem PC einen Brief schreibst und diesen ausdruckst. Wenn Du danach den Brief änderst bleibt ja der Ausdruck der selbe und man muss den Brief eben nochmals ausdrucken ;-)

Ich würde hier eventuell sogar anders herum vorgehen und die Einträge direkt der JTable hinzufügen bzw. darin editieren. An der JTable bzw. deren Model einen Listener hängen, der die Änderungen dann in die DB überträgt.


----------



## chrisUBER (15. Jul 2011)

Hmmm,

dann hab ich die Funktion vn fireTableDataChanged(); wohl falsch verstanden. Ich dachte immer, es reicht wenn ich nach dem Eintrag des Datensatzen in die DB dem Model einfach nur mit fireTableDataChanged klarmache, dass neue Daten vorliegen.

Dennoch verstehe ich dann die Lösung meines Problems immer noch nicht. In dem anderen Thread ist ja die Grafik abgebildet, wie mein Programm aufgebaut ist. Links das Menü, rechts werden die Daten dargestellt. Sobald ich nun über den Button die Klasse Events() aufrufe (mit Cardlayout show), wird diese Klasse doch komplett ausgeführt. In dieser Klasse ist doch eine SQL Abfrage über alle Daten vorhanden. Das bedeutet doch, dass sobald ich auf den Button klicke, die Klasse Events() aufgerufen wird -> eine neue SQL Abfrage gestartet wird (und diesmal mit neuem Datensatz) und daraufhin wieder im JTable dargestellt wird. Oder nicht?  

Irgendwie ist bei mir der Wurm drin, mein Verständnis sagt mir, dass dies klappen sollte.. aber es klappt nicht 

Wenn ich nun deinen Lösungsvorschlag umsetzen möchte, muss ich meinen kompletten Klassenaufbau ändern oder? Könnte Probleme mit der Klasse Overview geben, da ich mir dort echt viel zusammengebastelt habe (die ich hier nicht posten möchte, da sie einfach zu gross ist und ich mich für mein Chaos schäme ). Falls sich einer mit meinem Problem auseinandersetzen möchte und es über das Forum nicht machbar ist, erstelle ich auch gerne eine Dokumentation für das komplette Projekt und schicke es per PM. Dies sollte jedoch nur die letzte Möglichkeit sein, da ich eigentlich recht ehrgeizig bin und es doch irgendwie alleine schaffen möchte 

Danke schonmal für die Antwort

Gruß chris


----------



## Michael... (15. Jul 2011)

chrisUBER hat gesagt.:


> dann hab ich die Funktion vn fireTableDataChanged(); wohl falsch verstanden. Ich dachte immer, es reicht wenn ich nach dem Eintrag des Datensatzen in die DB dem Model einfach nur mit fireTableDataChanged klarmache, dass neue Daten vorliegen.


Damit teilt das DefaultTableModel all seinen Beobachtern mit, dass sich das Datenmodell geändert hat. Das hat zunächst mal garnichts mit irgendwelchen Datenbanken zu tun. Es besteht ja keine automatische Bindung zwischen TableModel und Datenbank.


chrisUBER hat gesagt.:


> Sobald ich nun über den Button die Klasse Events() aufrufe (mit Cardlayout show), wird diese Klasse doch komplett ausgeführt. In dieser Klasse ist doch eine SQL Abfrage über alle Daten vorhanden. Das bedeutet doch, dass sobald ich auf den Button klicke, die Klasse Events() aufgerufen wird -> eine neue SQL Abfrage gestartet wird (und diesmal mit neuem Datensatz) und daraufhin wieder im JTable dargestellt wird. Oder nicht?


Sehe hier jetzt nichts von einem Button, sehe nur eine in der Klasse NewEvent eine Methode 
	
	
	
	





```
jButton1ActionPerformed
```
 die ja vermutlich von ActionListener eines Buttons aufgerufen wird.
Diese ruft wiederum zwei Methoden auf. Eine in der vom Anwender eingegeben Daten ausgelesen werden und eine die mit diesen Daten einen Eintrag in die Datenbank vornimmt...
... und mehr passiert nicht.

Wenn Du neue Daten in die Datenbank schreibst und diese dann gleich auch im TableModel haben willst, musst Du diese Daten erneut aus der Datenbank auslesen. Und das passiert bei Dir nicht (wäre ganz einfach mit einem 
	
	
	
	





```
System.out.println("neue Daten werden ausgelesen");
```
 an der richtigen Stelle zu überprüfen).


----------



## chrisUBER (15. Jul 2011)

Ah tut mir leid, habe vielleicht den Code zu viel gekürzt.

Versuche das nochmal kurz darzustellen:

Habe link ein Menü mit 3 Buttons (hab einen oben beim Code anscheinend gelöscht). Übersicht (Overview), Veranstaltungen (Events) und neueVeranstaltung (NewEvent). Diese werden, wie in der Klasse Content beschrieben, über das Cardlayout gewechselt. Bei Programmstart wird automatisch die Übersicht angezeigt. Habe also ausversehen den Button neueVeranstaltung und das dazugehörige panel3.add(new NewEvent()); gelöscht

Du hast natürlich recht, ich habe die Kontrollausgabe in die Klasse Events geschrieben (Hier ist halt eine Tabelle mit allen Terminen). Beim start des Programmes wird die Kontrollausgabe einmal ausgeben. Beim hin- und herschalten der Klassen Übersicht, Veranstaltungen und neueVeranstaltung wird beim Aufruf vom Panel Veranstaltungen die Kontrollausgabe NICHT noch einmal ausgegeben -> bedeutet für mich, hier ändert sich nichts bzw. wird nichts neu geladen. Also wäre doch die einfachste, aber vielleicht nicht schönste Lösung beim wechsel der Cards jedesmal die Klassen neu laden zu lassen. Ist das überhaupt möglich?

Dein Vorschlag mit dem JTable bzw. Model und Listener hört sich sehr gut an, ich habe jedoch Angst, dass das komplette Umbauen sehr zeitintensiv ist und ich am Ende garnicht mehr durchblicke.


----------



## Michael... (15. Jul 2011)

chrisUBER hat gesagt.:


> Also wäre doch die einfachste, aber vielleicht nicht schönste Lösung beim wechsel der Cards jedesmal die Klassen neu laden zu lassen. Ist das überhaupt möglich?


Möglich schon Du könntest eine neue Komponente erzeugen und die alte ersetzen, wobei ....

Eigentlich müsstest Du ja nur die Methode 
	
	
	
	





```
abfragen(String query)
```
 erneut ausführen. Allerdings ist Deine Vorgehensweise beim Aufbau,Zusammenstellung und Befüllung des TableModels nicht gerade ideal und für einen wiederholten Aufruf geeignet. Mit Deiner Vorgehensweise müsstes Du ja jedesmal zunächst alle Spalten entfernen...


----------



## chrisUBER (15. Jul 2011)

Ja, ich seh gerade das mein nicht idealer Aufbau (sehr nett von dir ausgedrückt... mir wären schlimmere Sachen eingefallen ) nur mehr Probleme bringt. 

Oh man, wie kann ich das nun umbauen ohne den kompletten Projektordner wegzuschmeissen. Dachte echt ich bin auf dem richtigen Weg und man kann daraus noch was machen. Das größere Problem ist jedoch, dass bei den späteren Funktionen des Programmes ich wieder auf das gleiche Problem stoßen werde. Geplant ist noch eine kleine Bestandsverwaltung. Dort möchte ich direkt Werte im JTable editieren und diese sollen dann in die DB übergeben werden und diese natürlich auch direkt wieder ausgegeben haben.

Bin gerade echt am überlegen was ich nun mache, alles neu... oder irgendeine unschönere Lösung finden.

Habe nun versucht beim Einfügen eines neuen Datensatzes (NewEvent) das panel2 (für die Veranstaltungen) neu zu erzeugen und an die Klasse Content zu übergeben. In der Klasse Events habe ich am Ende der abfragen(String query) methode geschrieben, dass die tabelle neugezeichnet werden soll. Funktioniert leider auch nicht, es kommt zwar keinerlei Fehlermeldung, die Kontrollausgabe wird erneut aufgerufen, die Tabelle ist jedoch immer noch die alte.

Habe mich generell beim erstellen der Tabelle und des Models  sowie die Übergabe der SQL Statements an ein Javatutorial von Video2Brain gehalten. Scheint anscheinend in der Praxis nicht umsetzbar zu sein auf andere Projekte.

Du wirst wahrscheinlich bemerken, dass ich so langsam verzweifel  Kämpfe damit nun ganze 4 Tage, an einer eigentlich kleinen Sache.

Gruß chris


----------



## Michael... (15. Jul 2011)

Für's erste sollte es mal reichen die Erstellung und Befüllung des TableModels zu optimieren.
Wenn Du bei Vector bleiben willst, kannst Du ja beim Auslesen des ResultSets einen verschachteltes Vector Objekt verwenden und das Model mit 
	
	
	
	





```
model.setDataVector(Vector dataVector, Vector columnIdentifiers)
```
 befüllen.


Hier mal ein paar Codeschnippsel. Dabei wird allerdings ein eigenes TableModel (basierend auf dem DefaultTableModel) verwendet, das die Daten intern als 
	
	
	
	





```
ArrayList<String[]>
```
 hält. Das geht sicher noch schöner und objektorientierter, sollte aber für den Anfang genügen.
Mit der Methode initModel wird das TableModel einmalig erstellt, die Methode fillModel wird zum Befüllen des Models verwendet und müsste in Deinem Fall nach jeden Speichern in die Datenbank neu aufgerufen werden.

```
public class TabModel {
	private MyTableModel model;
	
	public void initModel() {
		model = new MyTableModel(new String[] {"Datum", "Bezeichnung", "Kunde", "Personen", "Raum", "Beginn"}, 0);
	}
	
	
	public void fillModel() throws SQLException {
		ArrayList<String[]> newValues = new ArrayList<String[]>();
        SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
		
        //ResultSet ausführen
		ResultSet rs = null; 
		
		while(rs.next()) {
			String[] rowData = new String[6];
			rowData[0] =  formatter.format(rs.getDate("datum"));
			rowData[1] =  rs.getString("bezeichnung");
			...
			newValues.add(rowData);
		}
		System.out.println(newValues.size() + " Datensaetze aus der DB geladen");
		
		//aktualisieren des TableModels
		model.updateData(newValues);
	}
	
	class MyTableModel extends DefaultTableModel {
		private ArrayList<String[]> model;
		
		public MyTableModel(String[] strings, int rows) {
			super(strings, rows);
			model = new ArrayList<String[]>();
		}

		public void updateData(ArrayList<String[]> newModel) {
			this.model = newModel;
			fireTableDataChanged();
		}

		public int getColumnCount() {
			return 6;
		}

		public int getRowCount() {
			if (model==null)
				return 0;
			return model.size();
		}

		public Object getValueAt(int rowIndex, int columnIndex) {
			if (rowIndex >= 0 && rowIndex<model.size()) {
				String[] rowValues = model.get(rowIndex);
				if (columnIndex>=0 && columnIndex < rowValues.length)
					return rowValues[columnIndex];
			}
			return null;
		}
	}
}
```


----------



## chrisUBER (15. Jul 2011)

Danke schonmal für deine Hilfe. Find ich echt super, dass mir damit jemand hilft.

Habe nun eine neue Klasse erstellt (TabModel) und das rowData genau angepasst sowie die imports eingefügt. Der Kompiler meckert schonmal nicht, sehr gut. Bin mittlerweile echt durcheinander, wie bzw. wo füge ich denn jetzt die Befehle ein, damit diese Klasse aufgerufen wird und mein JTable von der Klasse NewEvent() dem neuem Model zugewiesen wird? 

Deinen Code ansich habe ich verstanden (hoffe ich ) und ist auch einleuchtend für mich. Wie baue ich diesen jedoch richtig ein? Nachdem ich in NewEvent() auf speichern klicke, soll die Methode initModel, sovie fillModel ausgeführt werden und danach dem JTable in der Klasse Events() zugeordnet werden. Richtig? Oder bewege ich mich schon wieder in die falsche Richtung?

Gruß chris


----------



## bERt0r (15. Jul 2011)

Du weist nicht dem Model einen Table zu, du weist einem Table ein Model zu: JTable.setModel(TableModel dataModel)


----------



## chrisUBER (15. Jul 2011)

Ah sorry, meinte ich auch so  hab mich falsch ausgedrückt

Ok habe nun deinen Codeschnipsel übernommen und in meine Klasse Events eingefügt. Das neue Tablemodel wird auch richtig in der neuen GUI angezeigt. Bin also quasi nun wieder bei meinem Anfangsproblem, nur die Sache mit dem Tablemodel ist nun schöner gelöst. Jedoch ist nun auch hier wieder das Problem, dass sich das JTable nicht aktualisiert. 

Ich zeig mal, wie ich den Codeschnipsel bei mir eingesetzt habe. Falls ich das komplett falsch verstanden habe, bitte meckern 


```
public class Events extends javax.swing.JPanel  {
    
    private MyTableModel model;
    private JTable tab;

    /** Creates new form Events */
    public Events()  {


        initComponents();


        initModel();        //Model initialisieren

        tab = new JTable();
        tab.setModel(model);
        JScrollPane scrollPane = new JScrollPane(tab);
        tab.setFillsViewportHeight(true);
        jScrollPane1.setViewportView(tab);
        tab.repaint();
        

        try {
            fillModel();        //Model füllen
         }  catch(Exception e) {
                System.out.println(e);
         }

        setVisible(true);
   }
    
   public void initModel() {
        model = new MyTableModel(new String[] {"Datum", "Bezeichnung", "Kunde", "Personen", "Raum", "Beginn"}, 0);
    } 


    /** 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() {

        jScrollPane1 = new javax.swing.JScrollPane();

        setBorder(javax.swing.BorderFactory.createTitledBorder("Veranstaltungen"));

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 574, Short.MAX_VALUE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 434, Short.MAX_VALUE)
                .addContainerGap())
        );
    }// </editor-fold>                        

    public void fillModel() throws SQLException {
        ArrayList<String[]> newValues = new ArrayList<String[]>();
        SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
       
        //ResultSet ausführen
        String query = "SELECT * from termine ORDER BY datum ASC";
        Connection conn = Connect.driver();         //Verbindung herstellen
        Statement stm = Communication.stm(conn);    //Statement mit Verbindung erstellen
        ResultSet rs = Communication.rs(query,stm); //SQL String sowie Statement an Resultset übergeben, query wird ausgeführt

        while(rs.next()) {
            String[] rowData = new String[6];
            rowData[0] =  formatter.format(rs.getDate("datum"));
            rowData[1] =  rs.getString("bezeichnung");
            rowData[2] = rs.getString("kunde");
            rowData[3] = rs.getString("personen");
            rowData[4] = rs.getString("raum");
            rowData[5] = rs.getString("beginn");
            newValues.add(rowData);
        }
        System.out.println(newValues.size() + " Datensaetze aus der DB geladen");

        //aktualisieren des TableModels
        model.updateData(newValues);
        }

    class MyTableModel extends DefaultTableModel {
        private ArrayList<String[]> model;

        public MyTableModel(String[] strings, int rows) {
            super(strings, rows);
            model = new ArrayList<String[]>();
        }

        public void updateData(ArrayList<String[]> newModel) {
            this.model = newModel;
            fireTableDataChanged();
            System.out.println("Kontrollausgabe fireTableDataChanged");   //Diese Ausgabe funktioniert. Also wird die Methode anscheinend aufgerufen. Jedoch keine Änderung im JTable
        }

        public int getColumnCount() {
            return 6;
        }

        public int getRowCount() {
            if (model==null)
                return 0;
            return model.size();
        }

        public Object getValueAt(int rowIndex, int columnIndex) {
            if (rowIndex >= 0 && rowIndex<model.size()) {
                String[] rowValues = model.get(rowIndex);
                if (columnIndex>=0 && columnIndex < rowValues.length)
                    return rowValues[columnIndex];
            }
            return null;
        }
    }
```

Die Daten werden erfolgreich aus der Datenbank aufgerufen und im JTable angezeigt. Bei Eintragung eines neuen Datensatzes über NewEvent() wird der Datensatz, bei erneutem Aufruf von Veranstaltungen (Events()), nicht angezeigt.

Du sagtest, ich muss nach dem Eintragen eines neuen Datensatzes nur die Methode fillModel() aufrufen. Wo genau sollte ich diese denn bei meinem Beispiel tun? In meiner Navi bzw. Content Klasse? Oder ist diese Richtung komplett falsch? Bin leider immer noch ein wenig überfragt 

Der Aufbau dieses Tablemodels gefällt mir aber sehr gut, werde meine Overview Klasse auch umbauen. Habe das leider alles anders gelernt, teilweise auch sehr amateurhaft. Muss ich nun alles aufarbeiten 

Morgen...  Gute Nacht

Gruß chris

*Edit

Hallo, melde mich mit einem kleinen Update. Ich habe nun bei meiner Klasse Events zum Test einen JButton eingefügt. Bei Click dieses Buttons wird die Methode fillModel() nochmal aufgerufen... und siehe da, die Tabelle wird sofort aktualisiert und der neue Datensatz wird angezeigt. Aaaalso scheint für mich, als Leihe, der Fehler doch irgendwo beim Cardlayout zu liegen. Denn beim wechseln der Cards bzw. Panels wird die Tabelle nicht aktualisiert.


----------



## Michael... (17. Jul 2011)

chrisUBER hat gesagt.:


> Hallo, melde mich mit einem kleinen Update. Ich habe nun bei meiner Klasse Events zum Test einen JButton eingefügt. Bei Click dieses Buttons wird die Methode fillModel() nochmal aufgerufen... und siehe da, die Tabelle wird sofort aktualisiert und der neue Datensatz wird angezeigt. Aaaalso scheint für mich, als Leihe, der Fehler doch irgendwo beim Cardlayout zu liegen. Denn beim wechseln der Cards bzw. Panels wird die Tabelle nicht aktualisiert.


Warum auch. Das CardLayout steuert ja nur die Anzeige der Komponenten - nicht mehr und nicht weniger.
Du musst unmittelbar, nachdem die neuen Daten in die Datenbank geschrieben wurden direkt oder indirekt die filllModel() aufrufen.


----------



## chrisUBER (17. Jul 2011)

Hallo,

habe nun in der Klasse NewEvent bei den Variablen Events events = new Events(); eingefügt und hinter psInsert.executeUpdate(); (Zeile 60) events.fillModel(); ausgeführt.

Eine Fehlermeldung kommt nicht, die Kontrollausgaben in der Methode updateData() werden bei jedem Einfügen eines neuen Datensatzen erneut ausgegeben. Das JTable aktualisiert sich jedoch leider immer noch nicht. Es funktioniert nur, wenn ich die Klasse Events aufrufe (also die Tabelle sehe) und dann auf meinen seperaten "aktualisieren" (den ich zum Test gemacht habe) button Button klicke.

Ich seh den Unterschied bei dem Vorgang halt nicht. Liegt der Fehler bei der Art wie ich die Methode aufrufe? Sobald ich die Methode in der Klasse Events aufrufe (durch den Button), funktioniert es einwandfrei. Sobald ich die Methode über die Klasse NewEvent aufrufe, kommt zwar keine Fehlermeldung aber es passiert auch nichts. Ich muss die Tabelle also quasi immer "manuell" durch klick aktualisieren

Gruß chris


----------



## Michael... (17. Jul 2011)

chrisUBER hat gesagt.:


> habe nun in der Klasse NewEvent bei den Variablen Events events = new Events(); eingefügt und hinter psInsert.executeUpdate(); (Zeile 60) events.fillModel(); ausgeführt.


Da ich den aktuellen Aufbau nicht kenne, nur eine Vermutung: Das wird nicht viel bringen, da das neu erstellte Events Objekt (Vermutung jedes Events Objekt enthält ein eigenens TableModel) ja nichts mit dem auf der GUI angezeigten Events Objekt zu tun hat.
Und wie Dein Versuch mit dem Button zeigt, würde ein einfacher Aufruf von fillModel() die Ansicht der JTable aktualisieren. ==> einfach im Zuge der Datenaktualisierung in NewEvents die fillModel() aufrufen. Dazu muss aber auch das "DrumHerum" bzw. der Klassenaufbau stimmen bzw. der richtige "Referenzfluss" vorhanden sein d.h. das NewEvents Objekt muss eine Referenz auf das richtige Events Objekt besitzen bzw. auf das Objekt mit der Methode fillModel().

Falls doch eine Änderung des Klassen Designs in Frage kommt:
Ein dafür interessantes Entwurfsmuster: MVC


----------



## chrisUBER (17. Jul 2011)

Genau das ist das Problem.... das Event Objekt, welches auf der GUI angezeigt wird, kriege ich nicht aktualisiert nachdem ich einen Datensatz hinzugefügt habe. 

Ich werde mich nun nochmal an dem MVC Konzept versuchen. Habe mich damit schonmal beschäftigt und einige Dinge klappten auch wie ich es mir vorgestellt habe. Aber nach einer Weile war der Code-Misch-Masch dann jedoch wieder da. Manchmal garnicht so einfach umzusetzen  Werde mich dann heute Nacht damit auseinander setzen und versuchen die Datensatzeingabe mit dem MVC-Komzept zu realisieren.

Wäre es evtl. möglich dir mein komplettes Projekt zu schicken (Sind rund 10 Klassen inkl. Oberflächen mit Kommentierung)? Ich möchte natürlich nicht, dass du mir den Code fertig schreibst, will ja selbst etwas lernen . Mich würde halt nur die Meinung eines erfahrenen Programmierers interessieren, ob es sich lohnt dieses Projekt auf diesem Weg weiterzumachen oder ob ich von neu anfangen sollte. Evtl. siehst du ja dann auch direkt den Fehler bzgl. meines Problems. Ist natürlich nur eine Bitte, falls du keine Lust oder Zeit dazu hast verstehe ich das. 

Ich bedanke mich jedoch schonmal bei dir für die vielen Antworten, bin durch dich sehr viele Schritte voran gekommen (naja oder durch meine schlechte Vorarbeit halt auch wieder nach hinten ).

Melde mich heute Nacht mit einem hoffentlich positiven Ergebnis.

Gruß chris


----------



## Michael... (18. Jul 2011)

chrisUBER hat gesagt.:


> Ich werde mich nun nochmal an dem MVC Konzept versuchen. Habe mich damit schonmal beschäftigt und einige Dinge klappten auch wie ich es mir vorgestellt habe. Aber nach einer Weile war der Code-Misch-Masch dann jedoch wieder da.


Aber genau diesen "Code-Misch-Masch" sollte das MVC ja verhindern ;-)


chrisUBER hat gesagt.:


> Wäre es evtl. möglich dir mein komplettes Projekt zu schicken (Sind rund 10 Klassen inkl. Oberflächen mit Kommentierung)?


Kannst Du gerne machen, nur versprechen kann ich nichts, da es bei uns in dieser Woche ziemlich heiß hergeht...


----------



## chrisUBER (19. Jul 2011)

Also am Ende hat es doch so geklappt, wie ich es mir vorgestellt habe. Der Code ansich ist immer noch nicht der schönste, jedoch läuft nun alles so, wie es soll 

Habe versucht das MVC Prinzip umzusetzen und der Einsatz von Observern hat mein Problem endlich gelöst. MVC ist noch ausbaufähig  aber was läuft das läuft  Bis zum nächsten Problem 

Ich bedanke mich vielmals, tolles Forum. Bin hier fast den ganzen Tag am lesen und lernen 

Gruß chris


----------

