# ResultSet TableModel aktualisieren?



## Aruhn (3. Nov 2009)

Hallo Java Forum, 
ich hab ein kleines feines ResultSet Tabellenmodell, aber ich kann dies nicht aktualisieren. 
Also hab ich nach schon seit einiger Zeit im Internet nach einem kompakten rundum TabellenModell das mit ResultSets arbeitet, gesucht aber nichts hilfreiches gefunden. 

Ich stehe nach wie vor, vor dem Problem, wie ich dieses ResultSet Tabellenmodell aktualisieren kann. Ich möchte neue Daten direkt in der Datenbank hinzufügen (über SQL Statement), aber auch löschen können (ebenfalls über SQL) und das Tabellenmodell soll diese Veränderungen in meiner Swing Oberfläche darstellen.
Da finden sich aber so einige Fragezeichen.


Zum Programm:
hier ein Auszug vom MitarbeiterPanel, die grafische Oberfläche in der das stattfinden soll.

```
public class MitarbeiterPanel
extends JPanel
implements ActionListener
{
	//Eingabe-Attribute
	private JLabel lblInfo;
	private JFormattedTextField ftfBearbMitarbNr, ftfErstMitarbNr;
	private JTextField txtBearbName, txtErstName;
	private JTextField txtBearbVorname, txtErstVorname;
	private JTextField txtBearbStrasse, txtErstStrasse;
	private JTextField txtBearbOrt, txtErstOrt;
	private JFormattedTextField ftfBearbPLZ, ftfErstPLZ;
	private JFormattedTextField ftfBearbTel, ftfErstTel;
	private JTextField txtBearbEmail, txtErstEmail;

	//Suchen Attribute
	private JFormattedTextField ftfSuchMitarbNr;
	private JTextField txtSuchName;
	private JTextField txtSuchVorname;

	private JTable tblAnzeige;
	private JScrollPane scrolltabelle;

	//Buttons
	private JButton btnErstellen;
	private JButton btnBearbeiten;
	private JButton btnLoeschen;
	private JButton btnZurueck, btnWeiter;

	//Tabellen Attribute
	private Vector spaltenNamen = new Vector();
	private Vector daten = new Vector();

	//Klassen
	private DBConnect dbc = new DBConnect();

	//Zaehlvariable
	...


	public MitarbeiterPanel()
	{
		//Layout festlegen für alle Panels, einschließlich
		//Hauptpanel
		GridBagLayout gridbag = new GridBagLayout();
		GridBagConstraints gdc = new GridBagConstraints();
		gdc.fill = GridBagConstraints.BOTH;
		gdc.insets = new Insets(5,5,5,5);

		//Layout für das Bearbeiten
..........

		// === Layout für das Erstellen eines Mitarbeiters ===

		JPanel erstellen = new JPanel();
		erstellen.setLayout(gridbag);
		erstellen.setBorder(new TitledBorder("Dienstleister Erstellen"));
		lblInfo = new JLabel("Mitarbeiter Nr.");
		gdc.gridx = 0;
		gdc.gridy = 0;
		erstellen.add(lblInfo, gdc);
		ftfErstMitarbNr = new JFormattedTextField("#####");
		ftfErstMitarbNr.setEditable(false);
		ftfErstMitarbNr.setToolTipText("Automatisch ausfüllende Mitarbeiter-Nummer");
		gdc.gridx = 1;
		gdc.gridy = 0;
		erstellen.add(ftfErstMitarbNr, gdc);
		lblInfo = new JLabel("Nachname");
		gdc.gridx = 0;
		gdc.gridy = 1;
		erstellen.add(lblInfo, gdc);
		txtErstName = new JTextField(10);
		txtErstName.setToolTipText("Geben Sie den Nachnamen ein");
		gdc.gridx = 1;
		gdc.gridy = 1;
		erstellen.add(txtErstName, gdc);
		lblInfo = new JLabel("Vorname");
		gdc.gridx = 0;
		gdc.gridy = 2;
		erstellen.add(lblInfo, gdc);
		txtErstVorname = new JTextField(10);
		txtErstVorname.setToolTipText("Geben Sie den Vornamen ein.");
		gdc.gridx = 1;
		gdc.gridy = 2;
		erstellen.add(txtErstVorname, gdc);
		lblInfo = new JLabel("Strasse");
		gdc.gridx = 0;
		gdc.gridy = 3;
		erstellen.add(lblInfo, gdc);
		txtErstStrasse = new JTextField(10);
		txtErstStrasse.setToolTipText("Geben Sie die Straße ein.");
		gdc.gridx = 1;
		gdc.gridy = 3;
		erstellen.add(txtErstStrasse, gdc);
		lblInfo = new JLabel("PLZ");
		gdc.gridx = 0;
		gdc.gridy = 4;
		erstellen.add(lblInfo, gdc);
		ftfErstPLZ = new JFormattedTextField("#####");
		ftfErstPLZ.setToolTipText("Eingabe der fünfstelligen PLZ.");
		gdc.gridx = 1;
		gdc.gridy = 4;
		erstellen.add(ftfErstPLZ, gdc);
		lblInfo = new JLabel("Ort");
		gdc.gridx = 0;
		gdc.gridy = 5;
		erstellen.add(lblInfo, gdc);
		txtErstOrt = new JTextField(10);
		txtErstOrt.setToolTipText("Ort des Mitarbeiters");
		gdc.gridx = 1;
		gdc.gridy = 5;
		erstellen.add(txtErstOrt, gdc);
		lblInfo = new JLabel("Telefon");
		gdc.gridx = 0;
		gdc.gridy = 6;
		erstellen.add(lblInfo, gdc);
		ftfErstTel = new JFormattedTextField();
		ftfErstTel.setToolTipText("Telefonnummer");
		gdc.gridx = 1;
		gdc.gridy = 6;
		erstellen.add(ftfErstTel, gdc);
		lblInfo = new JLabel("eMail");
		gdc.gridx = 0;
		gdc.gridy = 7;
		erstellen.add(lblInfo, gdc);
		txtErstEmail = new JTextField(10);
		txtErstEmail.setToolTipText("eMail Adresse des Mitarbeiters");
		gdc.gridx = 1;
		gdc.gridy = 7;
		erstellen.add(txtErstEmail, gdc);
		btnErstellen = new JButton("Erstellen");
		btnErstellen.setToolTipText("Ausführen der Aktion 'Erstellen eines Mitarbeiters'.");

		// === Panel Button für Mitarbeiter erstellen ===
		JPanel erstellenBtn = new JPanel();
		erstellenBtn.setLayout(gridbag);
		erstellenBtn.setBorder(BorderFactory.createLoweredBevelBorder());
		gdc.gridx = 0; gdc.gridy = 0;
		erstellenBtn.add(btnErstellen);
		btnErstellen.addActionListener(this);

		//=== Panel für das Suchenfeld ===
........

		//=== Panel für den Suchen Button ===
........


		// <<< DATABASE - Mitarbeitertabelle >>>


		tblAnzeige = performTabelle();
		scrolltabelle = new JScrollPane(tblAnzeige);


		/*
		 * Hauptpanel - die ganzen Panels werden in einem Haupt GridBagLayout
		 * zusammengepickt
		 */
		JPanel hauptpanel = new JPanel();
		hauptpanel.setLayout(gridbag);
		gdc.gridx = 0;
		gdc.gridy = 0;
		hauptpanel.add(suchen, gdc);
		gdc.gridx = 1;
		gdc.gridy = 0;
		hauptpanel.add(bearbeiten, gdc);
		gdc.gridx = 1;
		gdc.gridy = 1;
		hauptpanel.add(aktiontitelpanel, gdc);
		gdc.gridx = 2;
		gdc.gridy = 0;
		hauptpanel.add(erstellen, gdc);
		gdc.gridx = 2;
		gdc.gridy = 1;
		gdc.weighty = 0;
		hauptpanel.add(erstellenBtn, gdc);

		/* *** Das Hauptlayout alias BorderLayout *** */
		setLayout(new BorderLayout());
		add(hauptpanel, BorderLayout.NORTH);
		add(scrolltabelle, BorderLayout.CENTER);


	}

	public JTable performTabelle()
	{
		try {
			String sql = "SELECT * FROM Mitarbeiter";
			Statement stmt = dbc.getDatabaseConnection().createStatement();
			ResultSet rs = stmt.executeQuery(sql);
			ResultSetMetaData rsmd =  rs.getMetaData();

			tblAnzeige = new JTable(DBUtils.resultSetToTableModel(rs));
			return tblAnzeige;
		} catch (Exception ex) {
			System.out.println(ex);
			return null;
		}

	}


	public void actionPerformed(ActionEvent e)
	{
		Object obj;

.......


		} else if (e.getSource() == btnErstellen) {

			try {

				String sql = "INSERT INTO Mitarbeiter(nachname, vorname, straße, "+
				"plz, ort, tel, email) VALUES(";
				//sql += ftfErstMitarbNr.getText() + ", '";
				sql += "'"+ txtErstName.getText() + "', '";
				sql += txtErstVorname.getText() + "', '";
				sql += txtErstStrasse.getText() + "', ";
				sql += ftfErstPLZ.getText() + ", '";
				sql += txtErstOrt.getText() + "', '";
				sql += ftfErstTel.getText() + "', '";
				sql += txtErstEmail.getText() + "')";

				System.out.println(sql);

				Statement stmt = dbc.getDatabaseConnection().createStatement();
				stmt.execute(sql);
				tblAnzeige = performTabelle();
				scrolltabelle.revalidate();

				stmt.close();

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

			}


		}

	}

}
```

Mein "Tabellenmodell", es arbeitet nur mit ResultSet

```
public class DBUtils
{

	public DBUtils()
	{

	}

    public static TableModel resultSetToTableModel(ResultSet rs)
    {
        try
        {
            ResultSetMetaData metaData = rs.getMetaData();
            int numberOfColumns = metaData.getColumnCount();
            Vector columnNames = new Vector();

            // Get the column names
            for (int column = 0; column < numberOfColumns; column++) {
                columnNames.addElement(metaData.getColumnLabel(column + 1));
            }

            // Get all rows.
            Vector rows = new Vector();

            while (rs.next()) {
                Vector newRow = new Vector();

                for (int i = 1; i <= numberOfColumns; i++) {
                    newRow.addElement(rs.getObject(i));
                }

                rows.addElement(newRow);
            }

            return new DefaultTableModel(rows, columnNames);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }
    }
}
```

DBConnect

```
public class DBConnect
{
	private String treiber = "net.sourceforge.jtds.jdbc.Driver";
	private String servertyp = "jdbc:jtds:sqlserver://";
	private String serveradresse = "192.168.1.6:1433/";
	private String datenbank = "Hausverwaltung";
	private String benutzer = "sa";


	protected DBConnect()
	{

	}

	public Connection getDatabaseConnection()
	{
		Connection con = null;
		try
		{
			Class.forName(treiber);

		}
		catch (ClassNotFoundException ex)
		{
			System.out.println("Kein Treiber gefunden");
		}

		try
		{
			return con = DriverManager.getConnection(servertyp+serveradresse+
					datenbank, benutzer, "" );
		}
		catch (SQLException ex)
		{
			System.out.println("Keine Serververbindung");
			return con;

		}

	}

}
```

Die einfachste Überlegung die ich angestellt habe, die Query SELECT * FROM ... einfach neu abschicken und die Tabelle neu setzen (Funktion performTabelle()). Was aber nicht so funktioniert wie ich es gedacht hatte.
.revalidate() richtet es leider auch nicht. 

So komm ich wohl bei dem Tabellenmodell nicht drumrum dort anzusetzen aber wie stelle ich dies bei meiner Vorlage (DBUtils) am Besten an? Auch so das nicht nur neue Datensätze angezeigt werden, sondern auch Veränderungen (UPDATE und DELETE) gleich übernommen werden?

Über jede Hilfe wäre ich sehr dankbar.


----------



## SlaterB (4. Nov 2009)

> public void actionPerformed(ActionEvent e)  {
> [..]
>                tblAnzeige = performTabelle();
>                scrolltabelle.revalidate();

scrolltabelle enthält immer noch das alte JTable-Objekt,
wenn du irgendwo eine Variable neusetzt, interessiert das scrolltabelle doch nicht

zwei Möglichkeiten
1. 
die neue JTable korrekt einbauen:
scrolltabelle.setViewPortView(table);
jFrame.validate();
jFrame.repaint();

2.
immer die alte Tabelle beibehalten,
in der alten Tabelle das Model neusetzen (setModel() )
oder auch das Model beibehalten und darin nur die Daten komplett austauschen (setDataVector() )


----------



## Aruhn (23. Nov 2009)

Die erste Möglichkeit funktioniert ganz gut.
Nun bin ich in dieser Hinsicht auf ein neues Problem gestoßen.
Von einer anderen Klasse erstelle ich bei einem actionPerformed einen neuen Datensatz in der Tabelle (geschieht in der SQL Datenbank). Der anderen Klasse mach ich ja mittels Klasse name = new Klasse() bekannt. 
Ich habe anschließend in dieser Klasse den Hinweis von oben in eine Methode gepackt: 


```
public void updateTabelle()
	{
		try {
			tblAusgabe = dbu.showTabelle(sql);
			scrolltabelle.setViewportView(tblAusgabe);
			scrolltabelle.revalidate();
			scrolltabelle.repaint();
			System.out.println("Update erfolgt");

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

		}
	}
```

In der anderen Klasse rufe ich mit der bei ActionPerformed Methode mit klassenname.updateTabelle() diese oben genannte Methode auf - nachdem ich das INSERT INTO Statement abgeschickt habe.
Sinn und Zweck ist, das eine neue SELECT * Abfrage gemacht wird und mein resultSet Tablemodell so aktualisiert werden soll. Nur wird es dies leider nicht.
Wie kann ich die Tabelle in der Klasse, in der sie dargestellt wird, von einer anderen Klasse aktualisieren lassen. So das der neue Datensatz angezeigt wird? 
Würde es durch so eine "einfache" Methode funktionieren - fehlt vielleicht nur eine kleine Zeile von Code?

Für jeden Hinweis/Hilfe bin ich dankbar.


----------



## SlaterB (23. Nov 2009)

> Wie kann ich die Tabelle in der Klasse, in der sie dargestellt wird, von einer anderen Klasse aktualisieren lassen.

durch korrekten Code

dein Code sagt wenig, läßt viele Spekulationen zu, etwa dass updateTabelle() mit 
> tblAusgabe = dbu.showTabelle(sql);
in einer anderen Klasse steht und daher völlig andere Variablen aktualisiert

nur vollständige kurze Testprogramme bringen etwas voran, zumindest immer zu allen auftauchenden Variablen auch die Deklaration + den Sinn und Zweck erwähnen,
und wenn du sowas wie 'Klasse x erzeugt Klasse y' beschreibst, dann alle beteiligten Klassen, deren Funktion, die Aufrufe usw. abdecken,

mit einem vollständigen kleinen Testprogramm klappt das fast automatisch


----------



## Aruhn (24. Nov 2009)

Ok ich hab mal auf der Schnelle ein kleines Extra-Testprojekt geschrieben, wo es einfach nur um den Fehler geht.

Ich habe ein JTabbedPane, in dem jeweils ein Tab eine Klasse beinhaltet. 
Die sieht wie folgt aus:


```
public class GUI
extends JFrame
{
	JTabbedPane tabpane = new JTabbedPane();

	ErstellPanel ep = new ErstellPanel();
	ZeigPanel zp = new ZeigPanel();

	public GUI()
	{
		super("DB Tabelle Test");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setSize(500,400);

		tabpane.addTab("Erstellen", ep);
		tabpane.addTab("Show", zp);

		this.add(tabpane);

	}

}
```

Die einzelnen Tab-Klassen sehen wie folgt aus:
Die Klasse zum Erstellen eines Datensatzes in der SQL-Datenbank:

```
public class ErstellPanel
extends JPanel
implements ActionListener
{
	private JTextField txtDname = new JTextField();
	private JTextField txtFatherdid = new JTextField();
	private JTextField txtEntries = new JTextField();
	private JButton btnErstellen = new JButton("Erstellen");

	private ZeigPanel zp = new ZeigPanel();
	private DBConnect dbc = new DBConnect();
	private DBUtils dbu = new DBUtils();

	public ErstellPanel()
	{
		btnErstellen.addActionListener(this);
		JPanel panel = new JPanel(new GridLayout(4,1));
		panel.add(txtDname);
		panel.add(txtFatherdid);
		panel.add(txtEntries);
		panel.add(btnErstellen);

		add(panel);

	}

	public void actionPerformed(ActionEvent e)
	{
		String lsql = "INSERT INTO dir(dname, fatherdid, entries) VALUES (";
		lsql += "'" + txtDname.getText() + "', " + txtFatherdid.getText() + ", ";
		lsql += txtEntries.getText() + ")";

		try {
			Statement stmt = dbc.getDatabaseConnection().createStatement();
			stmt.execute(lsql);
			zp.updateTabelle();

			txtDname.setText("");
			txtFatherdid.setText("");
			txtEntries.setText("");
		} catch (Exception ex) {
			System.out.println(ex);
		}

	}

}
```

Die Klasse in der die Datenbanktabelle dann angezeigt wird, das Modell befindet sich in einer extra Klasse DBUtils, mit der Methode showTabelle(). 

```
public class ZeigPanel
extends JPanel
implements ActionListener
{
	private JTable tblAusgabe;
	private JScrollPane scrolltabelle;

	private DBUtils dbu = new DBUtils();
	private DBConnect dbc = new DBConnect();

	private JButton btnDatensatz = new JButton("Datensatz erstellen");
	private String sql = "SELECT * FROM dir";

	public ZeigPanel()
	{
		btnDatensatz.addActionListener(this);

		try {

			Statement stmt = dbc.getDatabaseConnection().createStatement();
			ResultSet rs = stmt.executeQuery(sql);

			//tblAusgabe = new JTable(dbu.resultSetToTableModel(rs));
			tblAusgabe = dbu.showTabelle(sql);
		} catch (Exception ex) {
			System.out.println(ex);
		}
		scrolltabelle = new JScrollPane(performTabelle());

		setLayout(new BorderLayout());
		add(scrolltabelle, BorderLayout.CENTER);
		add(btnDatensatz, BorderLayout.SOUTH);


	}

	public JTable performTabelle()
	{
		try {

			Statement stmt = dbc.getDatabaseConnection().createStatement();
			ResultSet rs = stmt.executeQuery(sql);

			tblAusgabe = new JTable(dbu.resultSetToTableModel(rs));

			return tblAusgabe;
		} catch (Exception ex) {
			System.out.println(ex);
			return null;
		}

	}

	public void updateTabelle()
	{
		try {
			tblAusgabe = dbu.showTabelle(sql);
			scrolltabelle.setViewportView(tblAusgabe);
			scrolltabelle.revalidate();
			scrolltabelle.repaint();
			System.out.println("Update erfolgt");

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

		}
	}

	public void actionPerformed(ActionEvent e)
	{
		String lsql = "INSERT INTO dir(dname, fatherdid, entries) VALUES (";
		lsql += "'golem', 2, 4)";

		try {
			Statement stmt = dbc.getDatabaseConnection().createStatement();
			stmt.execute(lsql);
			updateTabelle();

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

	}

}
```

DBUtils, mit TableModel und showTabelle().

```
public class DBUtils
{
	private JTable tblAusgabe;
	private DBConnect dbc = new DBConnect();

	public DBUtils()
	{

	}

    public static TableModel resultSetToTableModel(ResultSet rs)
    {
        try
        {
            ResultSetMetaData metaData = rs.getMetaData();
            int numberOfColumns = metaData.getColumnCount();
            Vector columnNames = new Vector();

            // Get the column names
            for (int column = 0; column < numberOfColumns; column++) {
                columnNames.addElement(metaData.getColumnLabel(column + 1));
            }

            // Get all rows.
            Vector rows = new Vector();

            while (rs.next()) {
                Vector newRow = new Vector();

                for (int i = 1; i <= numberOfColumns; i++) {
                    newRow.addElement(rs.getObject(i));
                }

                rows.addElement(newRow);
            }

            return new DefaultTableModel(rows, columnNames);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }
    }

    public JTable showTabelle(String code)
    {
    	try {
			String sql = code;
			Statement stmt = dbc.getDatabaseConnection().createStatement();
			ResultSet rs = stmt.executeQuery(sql);
			ResultSetMetaData rsmd =  rs.getMetaData();

			tblAusgabe = new JTable(DBUtils.resultSetToTableModel(rs));
			JScrollPane ausgabe = new JScrollPane(tblAusgabe);
			ausgabe.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
			return tblAusgabe;
		} catch (Exception ex) {
			System.out.println(ex);
			return null;
		}
    }

}
```

ShowTabelle() wurde von mir als eine Art simple Aktualisierung ausgedacht. Ich übergebe eine neue SELECT * Abfrage an mein TableModel, lass mir eine neue Tabelle mit eben dieser Abfrage erstellen und gebe diese dann zurück. 

In der Klasse ZeigTabelle erstelle ich beim Klicken des Buttons "btnDatensatz" Testweise einen neuen SQL-Datensatz, anschließend rufe ich die updateTabelle() Methode innerhalb der selben Klasse (zeigPanel) auf. Diese widerrum ruft showTabelle() in DBUtils auf und siehe da es funktioniert. Die Tabelle zeigt den neuen Eintrag. 

Meine ganz simple Überlegung ist, wenn ich updateTabelle() von ZeigPanel in der Klasse ErstellPanel aufrufe, wo ich bewusst den neuen Datensatz erstellen will, das mit dem Aufruf wie innerhalb von ZeigPanel die Tabelle eben aktualisiert wird. 

Dies ist jedoch nicht der Fall und da steckt bei mir der Wurm drin, ich frag mich nur Wo und Wieso? 
An der Methode updateTabelle() hat sich ja nichts geändert und die wird auch von der ErstellPanel Klasse aufgerufen, da ich mir in der Methode auch Testweise mal ein System.out.println ausgeben ließ - was auch geschah.

PS
performTabelle() in ZeigPanel ist nur Testweise drin, ich hatte mir auch überlegt in die JScrollPane scrolltabelle diese Methode reinzusetzen (funktioniert auch) und diese Methode in der ErstellPanel Klasse aufzurufen mit der Hoffnung, eine neue Tabelle wird in die bestehende JScrollPane gelegt - widerrum ohne Erfolg.


----------



## SlaterB (24. Nov 2009)

in ErstellPanel hast du ein eigenes ZeigPanel, welches nicht in der GUI enthalten ist, welches ein anderes Objekt als das ZeigPanel in GUI ist,
dort die Tabelle zu aktualisieren bringt gar nix

immer schön wenig Objekte erzeugen und z.B. im Konstruktor mit übergeben, 
3x DBConnect ist vielleicht auch unnötig


----------



## Aruhn (25. Nov 2009)

Das war auch im Stillen so mein Gedanke, das es mit dem neu erzeugten ZeigPanel Objekt nicht richtig ist. Allerdings muss man ja die Methode einer Klasse, der anderen Klasse irgendwie bekannt machen. Geläufig ist mir eben dies in dem man ein Objekt davon erzeugt und mit dem Objekt arbeitet.
Was mich also momentan beschäftigt ist, wie kann von ErstellPanel auf das bereits existierende ZeigPanel Objekt in GUI zugreifen? Ein GUI Objekt in ErstellPanel zu erzeugen bringts jedenfalls nichts, es kommt dann zu einem StackOverflow.


----------



## SlaterB (25. Nov 2009)

schreibe in GUI
    ZeigPanel zp = new ZeigPanel();
ErstellPanel ep = new ErstellPanel(zp);

ErstellPanel merkt sich im Konstruktor dieses Objekt, statt ein neues zu erzeugen


----------



## Aruhn (26. Nov 2009)

Wunderbar, funktioniert. 
Gut zu wissen das es möglich ist im Konstruktor bestehende Objekte weiter geben zu können. Das macht mir, denke ich, so einiges einfacher.


----------



## SlaterB (26. Nov 2009)

du verwendest sowas eh schon ständig, anders würde kaum etwas funktionieren, 
z.B.
> new DefaultTableModel(rows, columnNames);

da soll das Model doch die bestehenden rows verwenden und sich nicht etwa einen neuen leeren Vector anlegen


----------

