# Datensätze einer Datenbank in JTable ausgeben



## bOOgey (1. Nov 2008)

sehr geehrte community,

ich habe ein folgendes problem, welches ich auf dem schnellsten weg lösen muss. wir (meine klasse) haben den auftrag ein flug-managment-system zu entwerfen. dabei stoße ich nun auf das oben genannte problem. ich weis leider nicht, wie ich mehrere datensätze in eine JTable anzeigen lassen kann. ich habe mich im vorfeld schon in diveren thread mich belesen, dass allgemein gilt, eine eigenes tablemodell zu entwerfen. ich habe aber nicht wirklich eine ahnung, wie ich das umsetzen soll. ich habe 4 klassen, die dafür wichtig sind. einmal eine datenbankverbindung-klasse, die nur die aufgabe hat, die verbindung zur der (leider) mdb zu eröffnen. die statements zu übermitteln und die verbindung zu schließen.
die 2te klasse is mein SQLInterface. dort stehen meine ganzen abfragen drinne, die für meine (3te klasse) GUI wichtig sind.
ich werde nun mal meine DataTable-Modell posten


```
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import javax.swing.table.AbstractTableModel;

/**
 * Beschreibung Tabellen Modell Implementierung
 * @version 1.0 vom 19.10.2008
 * @author XXXXXX
 * @date 19.10.2008
    
**/

public class DataTable extends AbstractTableModel {
        
        private ResultSet rst;
        private ResultSetMetaData meta;
        
        /**
         * Hier wird eine Abfrage benötigt, die im SQLInterface implementiert sein sollte
         * @param abfrage
         * @throws SQLException
         */
        
        public DataTable(ResultSet abfrage) throws SQLException {
                
                this.rst = abfrage;
                
                try {
                        this.meta = rst.getMetaData();
                } catch(SQLException ex) {
                        
                }
        }
        
        /**
         * Gibt die maximalen Zeilenanzahl zurück
         */
        
        public int getRowCount() {
                
                try {
                        this.rst.last();
                        return this.rst.getRow();
                } 
                catch (SQLException ex) {
                        return -1;
                }
        }

        /**
         * Gibt die maximalen Spaltenanzahl zurück
         */
        public int getColumnCount() {
                
                try {
                        return this.meta.getColumnCount();
                        
                } 
                catch(SQLException ex){
                        return -1;
                }
        }

        /**
         * Gibt den Datensatz einer konkteten Zelle zurück
         */
        public Object getValueAt(int row, int column) {
                
                try {
                        this.rst.absolute(row + 1);
                        return this.rst.getObject(column + 1);
                }
                catch(SQLException ex) {
                        return null;
                }
        }
        
        /**
         * Setzt die Editierbarkeit der Tabelle ausser Kraft
         */
        
        public boolean isCellEditable(int x, int y) {
        return false;
    }
}
```

im vorfeld muss ich sagen, dass ich nicht genau weis, ob alles so richtig is, wie ich es mir überlegt hatte. darum würde ich gern bitten, dass ihr mir mal bitte erklären würde, wie ich dis am besten anstelln kann.

in meiner GUI sieht es wie folgt aus, um die tabelle mit den daten zu füllen


```
private JTable tblTest;
          private DataTable modelTest;         
         
         [...]     
         SQLInterface einSQLInterface = new SQLInterface();
         try {
         modelTest = new DataTable(einSQLInterface.belegteGates());
         }
         catch (Exception e) {
         }
         tblTest = new JTable(modelTest);
         pnlHauptL.setViewportView(tblTest);
```

thx in voraus


mfg


bOOgey


----------



## Gast (1. Nov 2008)

Ich würde nicht in deinem DataTable Modell direkt auf die Datenbank mit Hilfe von abfragen und ResultSets verweisen.

Frag deine Daten aus der Tabelle ab und wirf das Ergebnis dann in ein geeignetes Tabellenmodell (du kannst dafür auch das DefaultTableModel nehmen)


----------



## bOOgey (1. Nov 2008)

is ja alles sehr schön. aber wiederum weis ich es immer noch nicht, wie ich es machen soll. also wie ich das ergebnis (daten) in die tabelle reinbekomme. das is ja auch mein problem. das mit dem datatable modell habe ich deswegen angefangen, weil die zellen nicht editierbar sein sollen.

sehr hilfreich wäre auch ein beispiel-code oder ähnliches


mfg


bOOgey


----------



## Guest (1. Nov 2008)

Naja es ist zwar schwierig dir zu helfen wenn man nicht genau weiss wie und was in die tabelle soll aber vielleicht kann ich dir helfen.

Du frägst zunächst deine Datenbank ab:

```
/**
	 * Query the database and return the result
	 * as a DefaultTableModel
	 * @param query - the SQL query
	 * @return - DefaultTableModel containing the result
	 */
	private DefaultTableModel buildTable(String query){
		DefaultTableModel dtm = null;
		try {			
			Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
			ResultSet result = statement.executeQuery(query);
			ResultSetMetaData metadata = result.getMetaData();
			
			result.last();
			int rowcount = result.getRow();
			result.beforeFirst();
			int columncount = metadata.getColumnCount();
			
			String[] columnnames = new String[columncount];
			
			for(int i=0;i<columncount;i++){
				columnnames[i] = metadata.getColumnLabel(i+1);
			}
			
			int k = 0;
			String[][] data = new String[rowcount][columncount];
			while(result.next()){
				
				for(int i=0;i<data[0].length;i++){
					String s =  result.getString(i+1);
					data[k][i] = s;
				}
				k++;
			}
			dtm = new DefaultTableModel(data,columnnames);
			
		} catch (SQLException e) {
			dtm = new DefaultTableModel(new String[][]{{e.getMessage()},{}},new String[]{"Error"}); 
		}
		return dtm;
	}
```

Das DefaultTableModel, das von der Methode dann zurückgegeben wird fügst du einfach deiner Tabelle hinzu:

```
JTable table = new JTable();
DefaultTableModel dtm = buildTable("SELECT HINZ FROM KUNZ");
table.setModel(dtm );
```


Das ist eine Lösung aber es gibt natürlich noch zig andere Wege das zu machen.


----------



## bOOgey (1. Nov 2008)

also...

ich hatte es so vorgehabt, dass ich (bis jetzt) 2 abfragen habe, also SELECT-anweisungen, diese stehen im SQLInterface als methoden drin. die datenbankverbindung hab ich wie gesagt, auch schon auf dem cmd getestet. nun hab ich halt einfach nur das problem, dass ich das ergebnis, also z.b. eineSQLInterface.XXXXX(); nicht weis, wie ich dieses ding in die tabelle reinkriege. und vor allem, dass das tabellenmodel so flexibel is, dass diese noch eine weitere abfrage aufnehmen kann.

isses jetzt vielleicht ein wenig besser?
oder welche informationen sind noch von nöten, um mir besser zu helfen. aber wie gesagt..., soweit funktioniert es ja auch..., nur abfrage in die tabelle zu kriegen. da weis ich nicht mehr weiter


----------



## Gast (1. Nov 2008)

Naja, obige Methode macht doch genau das. Du übergibst eine beliebige SELECT Abfrage und das Ding holt dir mit Hilfe der Datenbankverbindung die entsprechenden Daten aus der Tabelle und speichert sie in einem DefaultTableModel.


----------



## bOOgey (1. Nov 2008)

oke, ich habs mir nochmal durch den kopf gehen lassen. ich werd es mal ausprobieren. ich hoffe doch mit erfolg. aber nochmal zum verständnis. ich muss einfach dein code, meinem datatable-modell überschreiben?
und wie für ich denn ein, dass ich es nicht mehr editierbar machen kann?
also deswegen dachte ich an tablemodel
weil, da überschreibe ich denn einfach die iscelleditable-methode


----------



## Gast (1. Nov 2008)

Du musst der obigen Methode noch dein Datenbankverbindungsobjekt übergeben. 

Wegen dem editierbar Zeugs würd ich mir keinen Kopf machen, das lässt sich später immer noch durch ein leicht durch ein eigenes TableModel machen, das von DefaulTableModel erbt. Dann ändert sich nur der Name des Models in obiger Funktion:

Bsp:

```
public class MyTableModel extends DefaultTableModel {

public boolean isCellEditable(int row, int col) {

return false;

}
```


----------



## bOOgey (1. Nov 2008)

ich interpretiere es mal als ja. oder soll ich daraus ne extra klasse machen. weil ich erhlich gesagt, keine ahnung habe wohin damit. wenn ich es in die SQLInterface reinschreibe, ist es von der logik her total sinnlos, weil dort die SQL statements reinkommen
Datenbankverbindung, is ja für die verbindung zuständig. theoretisch reicht es doch als ergenzung meiner model-klasse oder?


----------



## Gast (1. Nov 2008)

Das kann ich dir nicht sagen. ich persönlich würde die Methode zu dem Datenbankkram tun und dann ein Event auslösen wenn die Datenbank abgefragt wurde. in dem Event würde ich das Ergebnis (= DefaultTableModel) mit dem DB Inhalt übergeben.

Die JTable hängt als Listener an dem Model und erhält demnach das Event und das darin liegende DefaultTableModel.


----------



## bOOgey (2. Nov 2008)

oke, soweit komme ich mit. wie sieht hier dann das event aus? bzw. der programmcode für listener und event. inner schule hatten wir nur actionlistener und itemlistener, wobei ich glaube kaum, dass eins von beiden hier zum einsatz kommt

vielen thx schon mal für deine hilfe


----------



## Guest (2. Nov 2008)

Na gut dann wollen wir mal:

Zunächst brauchst du ein Event, dass das DefaultTableModel enthält:

```
public class QueryResultEvent extends EventObject{
	private DefaultTableModel dtm;
	public QueryResultEvent(Object source, DefaultTableModel dtm){
		super(source);
		this.dtm = dtm;
	}
	
	public DefaultTableModel getTableModel(){
		return dtm;
	}
}
```

Dann brauchst du ein Listener Interface, deren Methoden deine JTable Klasse implementieren muss, damit sie was von den Datenbankabfragen mitkriegt:

```
public interface IQueryResultListener extends EventListener{
	void queryPerformed(QueryResultEvent event);
}
```


So, nun muss deine Datenbankklasse ja auch wissen, wer da alles mitlauscht. Also muss sie alle Objekte, die sich als Listener auf Datenbankabfragen registriert haben, speicher. Dazu implementiert sie Methoden um einen neuen Listener aufzunehmen, einen zu enfernen und eine Methode um die Listener zu benachrichtigen:

```
private EventListenerList listeners = new EventListenerList();
	
	/**
	 * Adds a IQueryResultListener which will be notified of
	 * query data changes
	 * @param listener
	 */
	public void addQueryResultListener( IQueryResultListener listener ) {
		listeners.add( IQueryResultListener.class, listener );
	}

	/**
	 * Removes a IQueryResultListener to no longer be notified of 
	 * query data changes
	 * @param listener
	 */
	public void removeQueryResultListener( IQueryResultListener listener ){
		listeners.remove( IQueryResultListener.class, listener );
	}

	/**
	 * Notify Listeners of a query data change
	 * @param event
	 */
	protected synchronized void notifyQueryResultListener( QueryResultEvent event )  {
		for ( IQueryResultListener l : listeners.getListeners( IQueryResultListener.class )){
			l.queryPerformed(event);
		}
	}
```

So jetzt ist der theoretische Unterbau vollständig. Jetzt muss deine Anzeige Klasse (die die JTable enthält) das Listener Interface implementieren:

```
public class AnzeigeKlasse implements IQueryResultListener{
...
	/**
	 * This method is implemented because of the IQueryResultListener interface
	 */
	public void queryPerformed(QueryResultEvent e){
		myJTable.setModel(e.getTableModel());
	}
```

Soweit mitgekommen? Prima, jetzt musst du die AnzeigeKlasse nur noch als Listener registrieren, also die Methode addQueryResultListener aufrufen:

```
dbKlasse.addQueryResultListener(dieAnzeigeKlasse);
```


----------



## Gast (2. Nov 2008)

Ach ja vergessen,
nach Aufruf der buildTable Methode musst du natürlich die Listener benachrichtigen:

```
// notify listeners
		notifyQueryResultListener(new QueryResultEvent(this,dtm));
```


----------



## bOOgey (8. Nov 2008)

das sorgenkind meldet sich wieder zurück 

sooo..., das mit der tabelle habe ich jetzt soweit hinbekommen. aber wie so oft...., folgt immer gleich das nächste problem.
also das mit der tabellenklasse hab ich jetzt wie folgt gelöst:


```
public class Tabelle {

       private DefaultTableModel dtm;
       private ResultSet result;
       private ResultSetMetaData metadata;

       public Tabelle() {
       }

       public DefaultTableModel erstelleTabelle(ResultSet result) {
         dtm = null;
         try {

           metadata = result.getMetaData();

           int rowcount = metadata.getColumnCount();
           int columncount = metadata.getColumnCount();
           String[] columnnames = new String[columncount];

           for(int i = 0; i < columncount; i++) {
             columnnames[i] = metadata.getColumnLabel(i + 1);
           }
           int k = 0;
           String[][] data = new String[rowcount][columncount];

           while(result.next()) {
             for(int i = 0; i < rowcount; i++) {
               String s = result.getString(i + 1);
               data[k][i] = s;
             }
             k++;
           }
           dtm = new DefaultTableModel(data, columnnames);
         }
         catch(SQLException e) {
           dtm = new DefaultTableModel(new String[][]{{e.getMessage()},{}}, new String[] {"Error"});
         }

         return dtm;
       }

}
```

also hab deinen code bissel abgewandelt und es funktioniert auch soweit mit dem aufruf inner gui:


```
Tabelle eineTabelle = new Tabelle();
        DefaultTableModel dtm = eineTabelle.erstelleTabelle(sql.belegteGates());
        tblLinks = new JTable(dtm) {
            public boolean isCellEditable(int x, int y) {
                return false;
                }
            };
        pnlHauptL.setViewportView(tblLinks);
```

nun habe ich aber folgendes problem. ich hätte gern diesen aufruf für die tabelle in einem button. also wenn ich den button benutze (drauf klicke) soll er die tabelle auf dem anderen panel anzeigen. ich habe also den kompletten aufruf in den actionlistener geworfen und rauskam eine monsterexception.



> Fehler bei SQL-Abfrage: Invalid handle
> Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
> at Tabelle.erstelleTabelle(Tabelle.java:27)
> at Hauptfenster$3.actionPerformed(Hauptfenster.java:141)
> ...



also die buttons an sich sind richtig implementiert, aber dis mit der tabelle will net so wie ich. also frag ich euch experten, was die exception bedeutet und wie ich dis problem lösen kann.

also....

HELP! ^^


mfg


bOOgey


----------



## bOOgey (8. Nov 2008)

update:

oke habs selber hinbekommen.

nun funktioniert aber der wechsel der modelle nicht. wie krieg ich das hin?


----------

