# ResultSet in JTable



## 0plan (28. Sep 2011)

In meiner Anwendung möchte ich Daten aus einer Datenbank (Tabelle ca 300 000 Datensätze) in einen JTable darstellen. Mit Datensätzen <=100 000 klappt dies auch. Bei mehreren Datensätzen im ResultSet bekomm ich die Out of Heap space Exception.

Wie kann ich es so programmieren, das er quasi in Intervallen die Daten einliest und nicht gleich alle aufeinmal ins RS speichert? Den Heap möchte ich nicht vergrößern, da das ja keine optimale Lösung ist. Es sollten auch ein paar Millionen an Datensätzen eingelesen werden können.

Hier mein Source


```
ResultSetMetaData rsmd = null;
        int clmCnt = -1;
        try {
            rsmd = rs.getMetaData();
            clmCnt = rsmd.getColumnCount();
 
        } catch (SQLException e) {
        	 JOptionPane.showMessageDialog(this,e.getMessage(),"Fehler",JOptionPane.ERROR_MESSAGE);
        }
 
        if (rsmd == null || clmCnt == -1) {
            throw new RuntimeException("rsmd is null");
        }
        
        try {
            
        	rs.beforeFirst();
            rs.last();
            int rowCnt = rs.getRow();
            rs.beforeFirst();
        	Object[][] odata = new Object[rowCnt][clmCnt];
            Object[] clmHeaders = new Object[clmCnt];
            
           for (int i = 1; i <= clmCnt; i++) {
               clmHeaders[i - 1] = rsmd.getColumnName(i);
            }
 
            int row = 0;
            while (rs.next()) {
 
                for (int i = 1; i <= clmCnt; i++) {
                    //System.out.print(rs.getString(i) + " ");
                    odata[row][i - 1] = rs.getString(i);
                }
                
                row++;
                System.out.println();
            }
 
            dtm.setDataVector(odata, clmHeaders);
         
        } catch (SQLException e1) {
        	 JOptionPane.showMessageDialog(this,e1.getMessage(),"Fehler",JOptionPane.ERROR_MESSAGE);
        	
        }
```


----------



## SlaterB (28. Sep 2011)

das kann man in verschiedenen Stufen ziemlich kompliziert machen,
eine beherrschbare Variante ist wohl, neben der Tabelle noch Buttons Hoch/ Runter zu setzen, die dann zu den nächsten x000 Einträge wechseln,
evtl. Anzeige, Eingabe einer bestimmten Seite (Ergebnisse 650/700, vergleichbar mit Forum) statt unendlich oft zu klicken,
im Hintergrund eine Verwaltung der Indexe und Nachladen neuer Daten, alte werden ersetzt, Benutzer muss evtl. immmer bisschen warten, 
DB-Anfragen also mit eingeschränkten Bereichen, was je nach Verteilung für sich auch schon ein Problem sein kann

oder die JTable von Anfang an mit komplett mit Dummy-Zeilen befüllen, was allerdings für sich schon den Speicher belasten kann,
und dann die ScrollBar beobachten, je nach Bereich teils im Voraus Daten laden, falls langsam gescrollt wird, 
bei großen Sprüngen sicherlich kaum möglich, dann Warten wie in Variante 1, aber zumindest alles ohne separate Buttons

möglich ist auch dass die Tabelle anfangs nicht zu groß ist, wenn sich aber die ScrollBar dem Ende nähert Schritt um Schritt erweitert wird,

besonders schön wäre bestimmt eine scheinbar riesige ScrollBar, aber dennoch nur kleine Tabelle, deren Einträge intelligent hin und her geschoben/ ersetzt werden,
also Aussehen wie Variante 2, nur ohne extrem riesiges Model, sondern effizientere interne Umsetzung

hab eben kurz gesucht, kann dir keinen Link nennen, an Umsetzung dürfte wohl auch nur die erste Variante in Frage kommen,
Rest wäre zumindest für mich auch was neues


----------



## 0plan (28. Sep 2011)

Dann hab ich mich ja zumindest nicht all zu dumm bei der Suche im Netz angestellt  Hatte mir auch schon sowas wie einen Scrollbarlistener überlegt aber da das ganze ziemlich weit geht dachte ich es gibt eventuell schon vorgefertigte Funktionen welche ich nur nicht gefunden hatte. Danke dir


----------



## SlaterB (28. Sep 2011)

einen Link habe ich, mit Link darin auf was englisches, 

JTable aus Datenbank versorgen nachladen - Entwickler-Forum
Java Tip 137: Manage distributed JTables - JavaWorld
vielleicht nicht ganz dein Problemfall, sieht grob so aus als werden eher einzelne Zeilen nachgeladen..


----------



## bERt0r (28. Sep 2011)

Hast du schon versucht mit setFetchSize die Buffergröße im ResultSet zu begrenzen?


----------



## SlaterB (28. Sep 2011)

was bringt das wenn doch das gesamte ResultSet durchlaufen, alle Ergebnisse übertragen werden?


----------



## 0plan (29. Sep 2011)

Durch die Fetchsize wird das Problem leider auch nicht gelöst, dies hatte ich bereits zu Anfang probiert. Die Lösungsvorschläge von dem Link den du gepostet hattest sind wohl eine Möglichkeit, jedoch viel zu Umfangreich für mein Vorhaben. Dort werden vorab eigene Libs erstellt und das geht mir dann vom Aufwand her doch zu weit. 

Bisher habe ich noch immer keinen richtigen Ansatz. Wie könnte eine Methode aussehen um in Intervallen ein ResultSet zu füllen, auszulesen und anschließend zu leeren?


----------



## SlaterB (29. Sep 2011)

das ganze besteht als mehr als einer Zeile Code, aus z.B. Buttons, Listener, gestellen DB-Anfragen, Lösch-Aufrufe am Model,
Einfügen neuer Befehle usw.,
tausend Dinge, die je für sich aber einfach sind und sich damit überhaupt keine richtige Frage ergibt

ich werde dir kaum seitenweise Code bauen, auch gar nicht möglich im Detail, aber nichtmal ein Grundgerüst,
du musst nicht unbedingt vorher den perfekten Plan habe und dann alles nur einmal abtippen -> fertig,
arbeite Schritt für Schritt, zunächst in Teilbereichen, Datenbank kann etwa komplett wegfallen, nimm zunächst Dummy-Daten,
nur eine Spalte, in den Zeilen einfache Werte wie 1, 2, 3 oder "a", "b", "c"

fange mit einer Tabelle an, und baue dazu einen ersten Button und überlege was dieser leisten könnte/ sollte

so, jetzt habe ich doch wieder ne Menge geschrieben, aber so allgemein, dass du nicht um eigenes Denken herumkommst


----------



## 0plan (29. Sep 2011)

Danke dir, ich werd mich dann mal dran machen. Finds nur ziemlich beknackt das man für eine einzige Select Anweisung so ein haufen Code in Java schreiben muss. In Delphi ist sowas kein Problem, da es mit der Datenbankverknüpfte Tablecomponents gibt. Schade und danke dir für deine Hilfe


----------



## SlaterB (29. Sep 2011)

ich habe nun neu gesucht und dabei erfolgsversprechendere Begriffe wie 'jtable database' + noch paging verwendet

hier
A quick application that demonstrates the PagingModel : TableSwing JFCJava
sieht es immerhin nach einer Tabelle mit in die Scrollbar eingebauten Page-Buttons aus

oder auch
JTable Paging (Swing / AWT / SWT / JFace forum at JavaRanch)
und andere Seiten helfen vielleicht, ne wirklich standardisierte Komponente gibts aber in der Tat nicht, soweit ich weiß


----------



## 0plan (29. Sep 2011)

Super danke dir, das sieht doch um einiges besser aus! Ich poste mal meinen Code wenn es funktioniert für die jenigen die das selbe Problem haben.


----------



## bERt0r (29. Sep 2011)

Also da dein Error beim Result Set geworfen wird, werden dir spezielle TableModels auch nicht helfen. Ich würde mir mal überlegen, ob die Art wie du dein Datenarray baust nicht suboptimal ist.


> rs.beforeFirst();
> rs.last();
> int rowCnt = rs.getRow();
> rs.beforeFirst();


Du läufst ganz am Anfang schon mal das ganze Result set durch, da hilft dir dann die Fetch size natürlich auch nix mehr. Spar dir das und schmeiss die Daten in einen Vector.

Zum Beweis, dass ein Table mit 300000 Datensätzen ohne Probleme klarkommt:

```
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.util.Random;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.border.EmptyBorder;


public class TableTest extends JFrame {

	private JPanel contentPane;
	private JTable table;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					TableTest frame = new TableTest();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public TableTest() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 450, 300);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
		
		JScrollPane scrollPane = new JScrollPane();
		contentPane.add(scrollPane, BorderLayout.CENTER);
		
		
		System.out.println("creating data");
		
		Vector<String> cols=new Vector<String>();
		cols.add("RowNr");
		char c=65;
		for(int k=0;k<10;k++)
		{
			cols.add(Character.toString(c));
			c++;
		}
		
		
		Random rand=new Random();
		Vector<Vector<String>> data=new Vector<Vector<String>>();
		for(int i=0;i<300000;i++)
		{
			Vector<String> row=new Vector<String>();
			row.add(Integer.toString(i));
			for(int j=0;j<10;j++)
			{
				String s=Long.toString(rand.nextLong(),26);
				row.add(s);
			}
			data.add(row);
		}
		System.out.println("done");
		
		table = new JTable(data,cols);
		
		scrollPane.setViewportView(table);
	}

}
```


----------



## SlaterB (29. Sep 2011)

ohne nerven zu wollen habe ich auch dazu dann doch nervend anzumerken:
was sollte denn durch das vorherige Durchlaufen passieren, werden deswegen alle Daten doppelt vorgehalten?
selbst wenn, ein konstanter Faktor im niedrigen Bereich wie 2 bedeutet wenig,

und wenn es für 300.000 reichen sollte, was bei anderer Datenmenge pro Eintrag schon wieder anders aussehen kann, dann ist vielleicht bei 600.000 oder 6 Mio. Schluss,
eine sinnvolle Grenze gibts da kaum
mit Paging auf 10.000 Einträge runterzugehen oder auch nur 1000 oder bei tatsächlich noch Speicherproblemen eine individuell gewählte noch niedrigere Zahl X funktioniert dann für beliebig große Tabellen


----------



## bERt0r (29. Sep 2011)

Vielleicht hab ich mich unklar ausgedrückt: Du wirst auch beim PagingModel die FetchSize für das Result set runtersetzten müssen, sonst sind die Daten ja trotzdem in deinem Heap. Das Model macht ja nix anderes, als dass es, wenn der User weiterscrollt, die nächsten 10000 Datensätze anfordert und dann anzeigt.


----------



## SlaterB (29. Sep 2011)

das ist eine Variante, die vielleicht auch in meinen Links so stehen wenn man genauer nachschaut,

ich schrieb ja ursprünglich eher, dass bei Page-Wechsel das Model geleert, eine neue Datenbankabfrage mit einem bestimmten Index-Bereich ausgeführt und dieses neue ResultSet komplett ins Model geschrieben wird


----------

