# JTable mit Suchfunktion



## beens0n (20. Feb 2012)

Hallo,
ich versuche zur Zeit eine Suchfunktion mit einer JTable als Anzeige zu erstellen und bin dabei auf 2 Probleme gestoßen...
Ich ziehe mir Daten aus einer Datenbank und habe mir eine Klasse erstellt. Diese erstellt aus den Daten Vektoren um sie dann in einer JTable anzeigen zu lassen. Hier erstmal der Code:

```
public Vector getEventsFromDB(Connection dbVerbindung) {
		Vector results = new Vector();
		try {
			Statement stmt = dbVerbindung.createStatement();
			ResultSet rs = stmt
					.executeQuery("SELECT * FROM tbl_veranst Where ID_VA = 1 OR (ID_VA =5)");

			while (rs.next()) {
				Vector dbEvent = new Vector();
				dbEvent.add(rs.getString(3));
				dbEvent.add(rs.getDate("Startdatum"));
				results.add(dbEvent);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return results;
	}
```
Jetzt habe ich gelesen, dass Vektoren veraltet sind und man lieber ein default table model verwenden soll, hierzu wollte ich mir einmal professionelle Meinungen einholen, da ich ziemlich neu in der Swing-Programmierung bin. Ein Codebeispiel wäre nett, wenn ich nicht mit Vektoren arbeiten soll.

Mein zweites Problem ist die Suchfunktion. Die JTable ist mit dem Inhalt der Datenbank gefüllt und es wird auch alles korrekt angezeigt. Es gibt ein Textfeld für den Suchbegriff und einen Button, dieser Button erstellt eine neue Liste in der die Strings mit dem gesuchten Inhalt ausgegeben werden. Diese Liste soll nun in der vorhandenen JTable ausgegeben werden, aber das kriege ich beim besten Willen nicht hin, bin anscheinend zu doof, die JTable zu aktuallisieren 
Hier noch der Code für den Button:

```
JButton btnNewButton = new JButton("Search");
		final Vector searchList = new Vector<String>();
		btnNewButton.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent arg0) {
				for (int i = 0; i < eventList.size(); i++) {
					if (eventList.get(i).toString()
							.contains(textField.getText())) {
						String txt = eventList.get(i).toString();
						searchList.add(txt);
					}
				}
				System.out.println(searchList);
			}

		});
```


----------



## Camino (20. Feb 2012)

Du könntest dir ein eigenes TableModel schreiben, eine Klasse abgeleitet von AbstractTableModel. Dort holst du dir dann deine Daten aus der Datenbank und hälst die z.B. in einer ArrayList gespeichert. Das TabelModel aktualisiert die Tabelle. Mit einem DocumentListener und einem RowFilter kannst du dann über ein Textfeld die Daten in der Tabelle filtern, und zwar schon immer gleich mit Eingabe eines Buchstabens in das Textfeld. Es lassen sich auch mehrere Textfelder kombinieren (z.B. Personen-Daten nach Vornamen und Nachnamen filtern lassen)


----------



## beens0n (20. Feb 2012)

Hatte die Daten auch zu erst in einer ArrayList, aber hab sie so nicht in meiner JTable angezeigt bekommen.. (bin wie schon gesagt erst am Start meiner Javakarriere...)
Aber an sich spricht sonst nichts gegen Vektoren und ich könnte es auch so lassen? Über Codeschnipsel bin ich im Übrigen immer sehr dankbar, tue mich damit vom Verständniss her um einiges leichter.
Das mit der Suche ist eine gute Idee, aber übersteigt meine Fähigkeiten wohl erst einmal. Will eigentlich nur die Vektorliste der JTable austauschen, wenn ich auf den Button drücke.


----------



## Camino (20. Feb 2012)

Es gibt hier im Forum ein gutes Tutorial zu Tabellen:
http://www.java-forum.org/bilder-gui-damit-zusammenhaengt/4841-jtable-ubersicht-teil-1-teil-8-a.html

Ich weiss ja nicht, wie umfangreich deine Daten sind. Aber ich würde mir für diese Daten eine Klasse erstellen, und dann beim Holen der Daten aus der Datenbank aus jedem Datensatz ein Objekt dieser Klasse erstellen. Diese Objekte dann in einer ArrayList sammeln. Über das TableModel kannst du dir dann diese Daten in der Tabelle anzeigen lassen. Wenn du Daten in der Datenbank änderst, kannst du z.B. die Daten neu aus der Datenbank in das TableModel holen und die Tabelle aktualisieren.

In deinem Fall, wenn du dich damit noch nicht so gut auskennst, würde ich erst mal damit anfangen. Und wenn das mit der Tabelle und dem TableModel (mit den Daten aus der Datenbank) funktioniert, dann kannst du dir das mit dem RowFilter und dem DocumentListener für das Filtern aus den Textfeldern heraus nochmal genauer anschauen.


----------



## beens0n (20. Feb 2012)

Ja, das Tutorial ist mir bekannt, allerdings ist mir daraus nicht wirklich klar geworden, wie ich mein Vorhaben nun angehen sollte.

Ok, dann werde ich mal versuchen das ein bisschen umzubasteln... Zum Verständniss.. Um meine Daten anzeigen zu lassen (sind ca 50 Zeilen mit je 5 Spalten) erstelle ich mir eine ArrayList und um die ArrayList in der JTable anzeigen zu lassen, gehe ich den Zwischenschritt über das TableModel?



> In deinem Fall, wenn du dich damit noch nicht so gut auskennst, würde ich erst mal damit anfangen. Und wenn das mit der Tabelle und dem TableModel (mit den Daten aus der Datenbank) funktioniert, dann kannst du dir das mit dem RowFilter und dem DocumentListener für das Filtern aus den Textfeldern heraus nochmal genauer anschauen.



Das hört sich natürlich auch ein bisschen eleganter an, als die Liste einfach durch eine neue zu ersetzen 
Danke erstmal, werde meinen Code gleich mal umschreiben und schauen, obs klappt


----------



## Camino (20. Feb 2012)

beens0n hat gesagt.:


> Ok, dann werde ich mal versuchen das ein bisschen umzubasteln... Zum Verständniss.. Um meine Daten anzeigen zu lassen (sind ca 50 Zeilen mit je 5 Spalten) erstelle ich mir eine ArrayList und um die ArrayList in der JTable anzeigen zu lassen, gehe ich den Zwischenschritt über das TableModel?



Das ist dann halt die Trennung zwischen Darstellung/View (JTable) und Daten (TableModel). Du erstellst eine Tabelle und fügst dieser das TabelModel hinzu. Wenn nun Änderungen an den Daten passieren, dann interessiert es nicht mehr die Tabelle, sondern das TableModel. Dort kannst du dann auch angeben, wieviele Spalten du haben möchtest (also, welche Daten in welchen Spalten stehen). Die Zeilenanzahl passt sich dann automatisch an die Länge der ArrayList an, also je nachdem wieviele Datensätze du dort reinpackst. Es lohnt sich auf jeden Fall, sich mit dem TableModel zu beschäftigen, auch wenn es anfangs etwas schwierig wirkt.


----------



## beens0n (20. Feb 2012)

Bekomme jetzt die Daten in eine ArrayList geschrieben (erst mal nur 2 Spalten):

```
public ArrayList<String> getEventsFromDB(Connection dbVerbindung) {

		ArrayList<String> eventList = new ArrayList();
		try {
			Statement stmt = dbVerbindung.createStatement();
			ResultSet rs = stmt
					.executeQuery("SELECT * FROM tbl_veranst Where ID_VA = 1 OR (ID_VA =5)");

			while (rs.next()) {
				eventList.add(rs.getString(3));
				eventList.add(rs.getString("Startdatum"));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return eventList;
	}
```
Der nächste Schritt ist jetzt, dass ich die ArrayList in ein Tablemodel übertrage um dieses dann in meiner JTable anzeigen zu lassen? Weil ich nicht wirklich was gescheites finde, wie ich die ArrayList in ein Tablemodel übertragen kann... Verzeihung für meine Dummheit, aber nach nem langen Tag sehe ich den Wald wohl vor lauter Bäumen nicht mehr -.-


----------



## Camino (20. Feb 2012)

Na ja, du könntest z.B. in einem JPanel ein TableModel erstellen und dies im Konstruktor einer JTable übergeben:

```
MyTableModel myTableModel = new MyTableModel();
JTable myTabelle = new JTable(myTableModel);
```

Die eigene Klasse MyTableModel ist dann von AbstractTableModel abgeleitet und dort hast du dann auch deine ArrayList, welche du aus der Datenbank mit den Datensätzen füllst. Wenn du das TableModel zuerst erstellst und die Daten dort schon in der ArrayList vorliegen, und du übergibst das der JTable im Konstruktor, dann werden die Daten dort gleich angezeigt.


----------



## Camino (20. Feb 2012)

beens0n hat gesagt.:


> Bekomme jetzt die Daten in eine ArrayList geschrieben (erst mal nur 2 Spalten)



So wie du das gemacht hast, bekommst du nicht 2 Spalten, sondern 2 Zeilen pro Datensatz des ResultSets. Du hast 1 String-Wert (das ist die Spalte) und fügst aus dem ResultSet dort mehrere String-Werte hinzu, d.h. du hast dann nachher eine Liste mit Strings, die du aber nicht mehr den Datensätzen richtig zuordnen kannst.

Besser wäre es, du erstellst dir eine Klasse für deine Datensätze, z.B. Person, welche die Felder aus der Datenbank hat, wie z.B. Vorname, Nachname, Geburtsdatum, Anschrift usw. Das füllst du dann in der Schleife mit den Daten aus dem ResultSet und fügst das Objekt der ArrayList<Person> hinzu. Im TableModel sind dann die Spalten die Felder, welche du aus den Objekten/Datensätzen angezeigt haben möchtest, und die Zeilen die Objekte, die in der ArrayList drinstehen.


----------



## truesoul (20. Feb 2012)

Hallo.

Hast du dir eigentlich das Beispiel von hier angeschaut? ( TableFilterDemo )


----------



## beens0n (21. Feb 2012)

Ja, die Oracle Docs sind mit bekannt, hab irgendwie noch Probleme die ganzen Sachen zu verknüpfen, was wohl daran liegt, dass ich den ganzen Tag programmiere und mein Kopf irgendwann nicht mehr so aufnahmefähig ist...
Danke Camino, habs iwie gestern nicht mehr ganz geschnallt was du mir sagen wolltest, finds aber super, dass du nicht gleich genervt, von meinen Fragen, aufgibst


----------



## Camino (21. Feb 2012)

Das Forum ist ja schliesslich dazu da, Fragen zu stellen und Antworten zu geben. Wenn jemand von Fragen genervt ist, muss er ja nicht antworten...

Bei der JTable und dem TableModel hatte ich am Anfang auch Probleme, das erst mal zu verstehen. Aber eigentlich ist das nicht besonders schwer. Einfach mal ransetzen und machen. Wenn dann was nicht so richtig klappt, einfach nochmal nachfragen...


----------



## beens0n (22. Feb 2012)

Habs tatsächlich geschafft  Habe jetzt eine JTable, die aus meinem TableModel gefüllt wird (enthält Objekte) und kann die spalten auch über deren Header sortieren.
Jetzt bin ich mir nicht ganz sicher, wie ich die Suchfunktion am elegantesten erstellen soll. Ich möchte ja immer von meiner Haupttabelle ausgehend suchen, auch wenn mir grade andere Suchergebnisse angezeigt werden. Habe von Iteratoren gelesen, die von der Performanz her den schleifen vorzuziehen wären. Und wie zeige ich die Suchergebnisse in meiner JTable am besten an?


----------



## Camino (22. Feb 2012)

Hey prima, war doch garnicht so schwer mit dem TableModel, oder?

Und wegen dem Filtern der Tabelle: Ich hab ja in meiner Anwendung auch eine Tabelle mit Personendaten und darüber 3 Texteingabefelder (Vorname, Nachname, Matrikelnummer). Sobald ich da was eingebe, wird schon die Tabelle danach gefiltert. Und zwar hab ich erstmal einen DocumentListener an die Textfelder gehängt, der sofort reagiert, sobald ich dort ein Zeichen eingegeben habe. Dazu habe ich mir eine eigene Klasse erstellt, die den DocumentListener implementiert. In diesem wird dann auch gefiltert, sobald der Listener eine Eingabe mitkriegt. Die 3 Textfelder sind auch kombinierbar, also wenn ich bei Nachname ein 'r' und bei Vorname ein 's' eintippe (Gross- und Kleinschreibung ist egal), werden in der Tabelle alle Datensätze angezeigt, deren Nachname mit 'R' und Vornamen mit 'S' beginnen. Das Filtern selbst läuft über einen RowFilter bzw. über einen TableRowSorter.

Du kannst ja mal schauen, was du dazu findest. Ansonsten kann ich dir bestimmt auch noch weiterhelfen...


----------



## beens0n (22. Feb 2012)

Wenn man erstmal versteht, was da so abläuft, dann ists wirklich nicht so schwer^^
n tableRowSorter hab ich schon in der JTable drin und auf den RowFilter bin ich auch schon gestoßen, tue mich damit aber noch ein bisschen schwer. So Ajax-mäßig muss es garnicht sein. Wollte das eigentlich mit nem ganz normalen MouseListener auf nem Button machen, aber bin grad ziemlich planlos, wie ich da den RowFilter reinbekomme. Hab mir die Oracle.docs dazu durchgelesen, aber kriegs beim besten Willen nicht hin, dass in mein Code zu integrieren. Das Beispiel dort ist ja:


```
RowFilter<PersonModel,Integer> ageFilter = new RowFilter<PersonModel,Integer>() {
   public boolean include(Entry<? extends PersonModel, ? extends Integer> entry) {
     PersonModel personModel = entry.getModel();
     Person person = personModel.getPerson(entry.getIdentifier());
     if (person.getAge() > 20) {
       // Returning true indicates this row should be shown.
       return true;
     }
     // Age is <= 20, don't show it.
     return false;
   }
 };
 PersonModel model = createPersonModel();
 TableRowSorter<PersonModel> sorter = new TableRowSorter<PersonModel>(model);
 sorter.setRowFilter(ageFilter);
```


----------



## Camino (22. Feb 2012)

Na ja, wenn das Filtern der Tabelle direkt über das Eintippen in die Textfelder geht, dann kann man ja auch auf den Klick eines Buttons verzichten. Ich kann dir ja mal die relevanten Teile meiner Anwendung zeigen, wie es bei mir funktioniert. Falls du es dann trotzdem mit dem Buttonklick machen willst, musst du das halt wohl irgendwie umschreiben...

Also, ich hab einen JDialog in dem diese Tabelle und die Textfelder drin sind. Und ich hab eine Klasse mit einem DocumentListener (PersonDocumentListener), wo der RowFilter mit den Eingaben der Textfelder erstellt und dem Sorter übergeben wird.

In meinem Dialog steht das so drin (Auszüge):

```
...
// DocumentListener dem Textfeld hinzufügen
tfNachname.getDocument().addDocumentListener( new PersonDocumentListener( this ) );
...

// TableModel erstellen
tableModel = new TableModelPersonSuche();

// Tabelle erstellen (TableModel im Konstruktor übergeben)
datenTabelle = new JTable( tableModel );

// Sorter erstellen und zur Tabelle hinzufügen
sorter = new TableRowSorter<TableModel>( tableModel );
datenTabelle.setRowSorter( sorter );
...

// weiter unten hab ich dann diese Methode, mit welcher
// der DocumentListener später den Sorter holt
public TableRowSorter<TableModel> getSorter() {
		return sorter;
	}
...
```

Und mein DocumentListener sieht so aus:

```
import java.util.ArrayList;

import javax.swing.RowFilter;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

/*
 * DocumentListener zur Erkennung von Änderungen
 * bei den Texteingabefeldern. Filtert die Personeneinträge in der Liste.
 */
public class PersonDocumentListener
implements DocumentListener {
    
    private DialogPersonSuche dialog;
    
	/* Konstruktor: der Dialog wird mit übergeben, damit
	 * Zugriff auf die Textfelder möglich ist
	 */
    public PersonDocumentListener( DialogPersonSuche dialog ) {
    	
    	this.dialog = dialog;
    	
    }
 
    // Text wird zu einem Textfeld hinzugefügt.
    public void insertUpdate( DocumentEvent e ) {
        filter();
    }
    
    // Text wird von einem Textfeld entfernt.
    public void removeUpdate( DocumentEvent e ) {
    	filter();
    }
    
    // Methode wird nicht gebraucht. Ist nur für style changes...
    public void changedUpdate( DocumentEvent e ) {
        //Plain text components do not fire these events
    }
    
    // RowFilter wird zusammengestellt und gesetzt
    public void filter() {
    	
    	// Suchtexte werden aus den Eingabefeldern geholt
    	String tf1 = dialog.getTextNachname();
    	String tf2 = dialog.getTextVorname();
    	String tf3 = dialog.getTextMatrikelnummer();
    	
    	// eine Liste mit den 3 Filtern wird erstellt
    	java.util.List<RowFilter<Object, Object>> filters = new ArrayList<RowFilter<Object, Object>>(3);
    	
    	// die 3 Filter werden hinzugefügt
    	filters.add( RowFilter.regexFilter("^(?i)" + tf1, 1) ); //Textfeld1 für Spalte 0
    	filters.add( RowFilter.regexFilter("^(?i)" + tf2, 2) ); //Textfeld2 für Spalte 1
    	filters.add( RowFilter.regexFilter("^(?i)" + tf3, 3) ); //Textfeld3 für Spalte 2
    	
    	// RowFilter.andFilter wird erstellt
    	RowFilter<Object, Object> filter = RowFilter.andFilter( filters );
    	
    	
    	
    	// Filter wird gesetzt
    	dialog.getSorter().setRowFilter( filter );

    }

}
```

Ich hoffe, du kannst damit was anfangen...


----------



## beens0n (23. Feb 2012)

Super, dass hat mir sehr geholfen, vielen Dank :toll:


----------

