# Beste Möglichkeit JTable-Änderungen in DB zu schreiben?



## UnkiDunki (6. Feb 2009)

Hi,

ich suche eine Lösungsmöglichkeit für folgende Problemstellung, die ich mal ganz primitiv und aufs Wesentliche bezogen beschreiben möchte:

Ich habe ein Frame, welches unter anderem aus mehreren Tabellen besteht, die aus einer Datenbank automatisch gefüllt werden und vom Benutzer verändert werden können. Datensätze löschen, editieren, hinzufügen...

Nun besitzt das Frame einen "Save"- und einen "Cancel"-Button. Bei letzterem sollen natürlich alle Änderungen, die der Benutzer an den Tabellen vorgenommen hat nicht übernommen werden.

Jetzt ist die Frage, wie man das Ganze am elegantesten macht. Und wie im Titel schon steht: Sind hierbei Transaktionen am sinnvollsten? Bei "Save" also ein commit() und bei "Cancel" ein rollback() absetzen?

Würdet ihr das auch so umsetzen oder wie geht man da sonst generell am besten vor?

Vielen Dank schon mal im Voraus


----------



## Gast (6. Feb 2009)

Nun, ich gehe davon aus, dass deine Daten sowieso ERST NACHDEM du auf den Save  button drückst in die Datenbank geschrieben werden. Es findet also sicherlich keine RealTime - Veränderung statt. In dem Fall wird die Datenbank eh vor vollendete Tatsachen gestellt oder?
Also : Save - > Daten übernehemen
         Cancel -> Daten ebend nicht übernehmen.
Wenn ich dass so richtig sehe, dann ist Transaktionssicherheit überflüssig, Da deine GUI sowieso nur auf expliziten Wunsch speichert, oder?


----------



## UnkiDunki (6. Feb 2009)

Ich hatte es wirklich erst so gelöst, wie du geschrieben hast: Die Daten erst nach "Save" in die Datenbank zu schreiben, aber richtig Fan bin ich davon nicht geworden.
Ich finde es wesentlich bequemer die Änderungen direkt in Realtime umzusetzen.
Wenn man bei sowieso schon bestehenden Datensätzen NUR editiert, lässt sich das aus meiner Sicht noch ganz gut managen, aber bei Hinzufügen und Löschen von Sätzen wird das recht komplex umzusetzen oder habe ich bis jetzt einfach nur noch nicht den richtigen Umsetzungsansatz gefunden?


```
aModel.addTableModelListener(new TableModelListener(){

    public void tableChanged(TableModelEvent arg0) {
		
                tableChangeObjectID.add(Integer.parseInt(""
                      +aTable.getModel().getValueAt(arg0.getFirstRow(), 0)));
                
                tableChangeObjectValue.add(Double.valueOf(""
                      +aTable.getModel().getValueAt(arg0.getFirstRow(),arg0.getColumn())));
		}
});
```

So würde das jetzt bei einer 2-spaltigen (ID und Value) Tabelle aussehen. tableChangeObjectID/Value sind Listen, die ich beim Drücken auf "Save" nach ihrer Größe überprüfe und bei >0 in einer Schleife dann via PreparedStatements in die Datenbank schreibe. Sicher super umständlich und "dirty", oder?
Vielleicht tue ich mich deswegen so schwer, wenn schon das Editieren der Zeilen so von mir gelöst wurde.
Das Hinzufügen und Löschen mhmmm...


----------



## UnkiDunki (9. Feb 2009)

Ok... angenommen ich verzichte jetzt auf Transaktionen und löse das so, dass ich die Änderungen erst beim Drücken auf den "Save"-Button in der Datenbank aktualisiere, so habe ich doch die Frage, wie ich das am geschicktesten mache.

Wie löse ich das am besten??

- Beim Editieren schon vorhandener Datensätze benutze ich, siehe oben, eine Liste oder ein Feld, wo ich bei Änderungen die jeweilige ID des Datensatzes und den neuen Wert speichere und dann beim Speichern abarbeite.

Wie ich oben schon fragte: Ist das gut so und wie mache ich das mit neuen und gelöschten Datensätzen?
Genauso? Also beim Hinzufügen ein neuer Eintrag in Liste/Feld und beim Löschen einfach die ID des Datensatzes in eine Gelöscht-Liste und dann auch hinterher durchlaufen und entsprechende Änderungen vornehmen?

Klingt für mich irgendwie "dirty" und umständlich...
Um nur ein Feld für Editieren, Löschen und Hinzufügen zu benutzen, könnte man natürlich auch am Anfang immer ein "Flag" dem Feld hinzufügen, 0 - Löschen, 1 - Editieren, 2 - Hinzufügen... Mhmmm...

Jemand ne bessere Idee??

Vielen Dank im Voraus


----------



## Kaffeemaschinist (10. Feb 2009)

Ein paar Ansätze, die ich von ORMs kenne und dir die vielleicht helfen könnten:

Transaktionen und vielleicht DB-Locks für die GUI zu nutzen ist wenig hilfreich, denn wer sagt denn, dass der Nutzer nicht mal ne zweistündige Kaffeepause macht und damit die Daten blockiert. Wäre unschön ...

In den meisten Fällen kannst du dir die Sache relativ einfach machen. Angenommen, hinter deiner Darstellung liegen ein paar Datensätze als Java-Objekte (z.B. POJOs). In den meisten Fällen hat jedes dieser Objekte ne ID, meistens ein Integer > 0. Falls du Löschungen nicht sofort übertragen möchtest, kannst du deinen Java-Objekten eine zusätzliche Member-Variable 
	
	
	
	





```
boolean deleted = false;
```
 mitgeben, die du, sobald du auf Löschen klickst, natürlich auf 
	
	
	
	





```
true
```
 setzt.

Bevor du die Daten abspeicherst (NEU), holst du dir die Daten nochmal aus der Datenbank (ALT) und vergleichst nun:
1. speichere erstmal jedes Element in die DB, bei dem die ID==0 ist, denn das sind neue Datensätze
2. lösche alle als gelöscht markierten Objekte
3. vergleiche jedes Objekt NEU, das _weder neu erstellt wurde, noch als gelöscht markiert ist, mit dem dazugehörigen Objekt ALT und übertrage die Änderungen in die DB



Natürlich können Fehler bei solchen Lazy Updates auftreten. Beispielsweise könnte es sein, dass du von einem anderen Nutzer bereits gelöschte Datensätze wiederherstellst, falls du diese bei dir im gleichen Moment änderst. INSERTs und DELETEs würde ich also vllt. sofort propagieren._


----------



## UnkiDunki (10. Feb 2009)

Hi,

erstmal vielen Dank für deine Antwort 



			
				Kaffeemaschinist hat gesagt.:
			
		

> Ein paar Ansätze, die ich von ORMs kenne und dir die vielleicht helfen könnten:
> 
> Transaktionen und vielleicht DB-Locks für die GUI zu nutzen ist wenig hilfreich, denn wer sagt denn, dass der Nutzer nicht mal ne zweistündige Kaffeepause macht und damit die Daten blockiert. Wäre unschön ...



Da hast du natürlich völlig recht.  Hatte den Titel des Themas "Transaktionen sinnvoll?" eben auch schon geändert, weil diese Technik in diesem Zusammenhang wohl wirklich mehr als ungünstig ist...



			
				Kaffeemaschinist hat gesagt.:
			
		

> In den meisten Fällen kannst du dir die Sache relativ einfach machen. Angenommen, hinter deiner Darstellung liegen ein paar Datensätze als Java-Objekte (z.B. POJOs). In den meisten Fällen hat jedes dieser Objekte ne ID, meistens ein Integer > 0. Falls du Löschungen nicht sofort übertragen möchtest, kannst du deinen Java-Objekten eine zusätzliche Member-Variable
> 
> 
> 
> ...



Muss gestehen, dass ich die Bezeichnung POJO erst mal googeln musste...  Ok... komisch, dass ich das anscheinend noch nie gehört habe...
Die Datensätze befinden sich ja in JTables, sind also keine Objekte mehr, sondern nur noch abgebildete Werte...
Generell ist es aber so, dass ich darauf achte, auch die Schlüssel in den Tabellen darzustellen bzw. ausgeblendet zur Identifikation der jeweiligen Datensätze (Zeilen) mit zu übergeben.

Identifizierung ist also gegeben...



			
				Kaffeemaschinist hat gesagt.:
			
		

> Bevor du die Daten abspeicherst (NEU), holst du dir die Daten nochmal aus der Datenbank (ALT) und vergleichst nun:
> 1. speichere erstmal jedes Element in die DB, bei dem die ID==0 ist, denn das sind neue Datensätze
> 2. lösche alle als gelöscht markierten Objekte
> 3. vergleiche jedes Objekt NEU, das _weder neu erstellt wurde, noch als gelöscht markiert ist, mit dem dazugehörigen Objekt ALT und übertrage die Änderungen in die DB
> ...


_

Ok... das ist mir jetzt ein wenig klarer geworden und das mit den "Lazy Updates" war ja auch mein Bedenken...
Hatte das am Anfang sogar noch so realisiert, dass ich beim Öffnen des Frames und Befüllen der JTable den aktuellen Stand gespeichert hatte, dann alle Änderungen an der Table realtime an die DB gegeben habe und bei "Cancel" dann einfach das Backup zurückgespielt habe, so dass der vorherige Stand erreicht wurde... auch mehr als unglücklich 

Das Arbeiten von mehreren Benutzers an den gleichen Daten ist ja generell immer ein heikles Thema...
Aber bei z.B. einer Kundenverwaltung einen Kunden so lange zu sperren bis ein User mit der Editierung seiner Daten fertig ist ist ja wie oben schon erwähnt auch nicht möglich... evtl. ne Kaffeepause oder ähnliches macht da einen Strich durch die Rechnung..._


----------



## Guest (10. Feb 2009)

UnkiDunki hat gesagt.:
			
		

> erstmal vielen Dank für deine Antwort


Meine Antwort ist frei aus dem Bauch heraus, da ich auch noch nicht in die Verlegenheit gekommen bin, kritische Multi-user-Umgebungen zu schreiben.



			
				UnkiDunki hat gesagt.:
			
		

> Muss gestehen, dass ich die Bezeichnung POJO erst mal googeln musste...  Ok... komisch, dass ich das anscheinend noch nie gehört habe...


Vielleicht empfiehlt es sich für dein Projekt, mal in eins der gängigen ORM-Frameworks reinzuschauen, z.B. Hibernate und IBatis (mein bisheriger Favorit). Das könnte evtl., da es nach kurzer Einarbeitungszeit den Aufwand an SQL-Getippe vereinfacht. Da wirst du öfters auf den Begriff POJO treffen, der nichts anderes aussagt, als dass es eine simple Klasse ist, die dort einen Datensatz deiner Tabelle entspricht und lediglich simple getter/setter besitzt - mehr nicht.



			
				UnkiDunki hat gesagt.:
			
		

> Die Datensätze befinden sich ja in JTables, sind also keine Objekte mehr, sondern nur noch abgebildete Werte...
> Generell ist es aber so, dass ich darauf achte, auch die Schlüssel in den Tabellen darzustellen bzw. ausgeblendet zur Identifikation der jeweiligen Datensätze (Zeilen) mit zu übergeben.
> Identifizierung ist also gegeben...


Überlegenswert, ob du die Daten, so wie du sie aus der Tabelle bekommst (also: POJO), nicht direkt an deine Applikation weiterleitet, die dann bestimmt, was und wie sie es anzeigt. Das schöne: Du kannst dann auf Basis dieses Objekts agieren und musst nicht umdenken zwischen DB-Tabelle und Java-Repräsentation.



			
				UnkiDunki hat gesagt.:
			
		

> Ok... das ist mir jetzt ein wenig klarer geworden und das mit den "Lazy Updates" war ja auch mein Bedenken...
> Hatte das am Anfang sogar noch so realisiert, dass ich beim Öffnen des Frames und Befüllen der JTable den aktuellen Stand gespeichert hatte, dann alle Änderungen an der Table realtime an die DB gegeben habe und bei "Cancel" dann einfach das Backup zurückgespielt habe, so dass der vorherige Stand erreicht wurde... auch mehr als unglücklich


Yapp, denn die Daten lokal können sich schon wieder von denen in der DB unterscheiden.
Denkbar wäre es, einen globalen Manager zu kreieren, der jeden Zugriff überprüft und Änderungen von/an die Sichten jeweils weitergibt. Sozusagen LOCKs auf Seite der Applikation, aber das kann evtl. dein DBMS noch besser. Wenn ich mich recht entsinne, bietet Hibernate sogar schon ein wenig Funktionalität in diese Richtung, aber es ist zu lange her, um das genau sagen zu können.



			
				UnkiDunki hat gesagt.:
			
		

> Das Arbeiten von mehreren Benutzers an den gleichen Daten ist ja generell immer ein heikles Thema...
> Aber bei z.B. einer Kundenverwaltung einen Kunden so lange zu sperren bis ein User mit der Editierung seiner Daten fertig ist ist ja wie oben schon erwähnt auch nicht möglich... evtl. ne Kaffeepause oder ähnliches macht da einen Strich durch die Rechnung...


Genau! Wenn du aber herausfinden kannst, dass im Hintergrund bereits was stattgefunden hat, was die Daten deiner Sicht betrifft (Nutzer B hat während Nutzer A mit den Daten rumwurschtelt etwas verändert), dann kannst du ggf. eine Strategie entwerfen, was passieren soll (z.B. eine Meldung beim Speichern).

Auch dafür gibt's schon Lösungen (siehe: mehrere Nutzer wollen einen Eintrag in der WikiPedia editieren ODER mehrere Sachbearbeiter sitzen mit ihrer Software an den selben Kundendaten dran).


----------



## UnkiDunki (11. Feb 2009)

Hi "Gast",

vielen Dank schon mal für deine ausführliche "Aus-dem-Bauch-heraus"-Antwort 
Werde mir Hibernate und IBatis mal angucken. Damit hatte ich bis jetzt noch nichts am Hut... also absolutes Neuland...

Habe angeregt durch dich jetzt auch mal im Fachforum für mein DBMS gefragt (H2) und dabei in einem anderen Beitrag folgenden Link gefunden: MVCC

Wenn ich das richtig verstehe, wäre das schon ein Schritt in die richtige Richtung oder was meinst Du? Ist dieses "Feature" zu gebrauchen? 



> If multiple connections concurrently try to update the same row, this database fails fast: a concurrent update exception is thrown.



Das könnte man ja dann entsprechend benutzen um die User auf eben diesen Umstand aufzuklären...


----------



## Kaffeemaschinist (11. Feb 2009)

Hmm.... da war ich aber wieder "Gast"freundlich 

H2 scheint ja ein richtiges DB-System zu sein. Falls du oder dein Kunde das einsetzen wollen, ist das ok, oftmals ist man aber ein bestehendes Backend gebunden, da wäre das dann nix.

Auf jeden Fall musst du dann Transaktionen nutzen und (scheinbar) sofort updaten. Wenn der Nutzer dann auf "Übernehmen" klickt, wird das Commit gesendet und dann bekommt er evtl. die Exceptions von MVCC um die Ohren geschmissen. Das ist das, wie ichs beim Drüberblicken verstanden habe. Müsstest du mal ausprobieren


----------



## MarcB (11. Feb 2009)

Evtl. ist das Tutorial http://www.netbeans.org/kb/60/java/gui-db.html was für dich.
Generell wäre beans binding hier sinnvoll.


----------



## maki (11. Feb 2009)

Hier noch ein paar generelle Schlagworte zu dieser Thematik:
Optimistic Locking
Pessemistic offline Locking


----------



## UnkiDunki (11. Feb 2009)

Vielen Dank schon mal euch allen, bin für jede Anregung dankbar!

@MarcB: 
OT: JavaDB mhmmm... denke mal, dass ich trotzdem noch gut daran tue weiter auf H2 zu setzen oder? Kann da jemand schon was zu sagen?
Das Video ist schon mal sehr interessant...

Bin ja auch noch einer, der ohne GUIBuilder etc. hantiert und sich noch mit GridBagLayout rumschlägt, aber wenigstens kann ich so noch behaupten, dass ich jede Zeile Code selber "geschrieben" habe


----------



## MarcB (11. Feb 2009)

Ich hantiere auch meist ohne GUI Builder, aber die Tutorials finde ich eben nett. Man klickt sich halt schnell was zusammen was funktioniert und kann dann im Quellcode nachsehen was man da gemacht hat. Das ist dann meist recht einfach auf die eigene Anwendung zu transferieren.

Edit: JavaDB (= Apache Derby) ist spitze hat bei mir längst die hsqldb & h2 als Embedded-DB abgelöst. Grundsätzlich ists bei kleineren Projekten aber wurscht was man verwendet.


----------



## UnkiDunki (11. Feb 2009)

MarcB hat gesagt.:
			
		

> Ich hantiere auch meist ohne GUI Builder, aber die Tutorials finde ich eben nett. Man klickt sich halt schnell was zusammen was funktioniert und kann dann im Quellcode nachsehen was man da gemacht hat. Das ist dann meist recht einfach auf die eigene Anwendung zu transferieren.



Die Vorteile liegen natürlich auf der Hand, aber irgendwie weigert sich etwas in mir solche Hilfen zu nutzen... kann das auch nur schwer beschreiben  
Sollte mich aber echt mal dazu zwingen, denn man spart ja nicht nur Zeit...



			
				MarcB hat gesagt.:
			
		

> Edit: JavaDB (= Apache Derby) ist spitze hat bei mir längst die hsqldb & h2 als Embedded-DB abgelöst. Grundsätzlich ists bei kleineren Projekten aber wurscht was man verwendet.



Ja, bei kleinen Projekten gebe ich dir recht.
Muss aber auch sagen, dass mir H2 sehr ans Herz gewachsen ist  Den Support finde ich sehr gut und von der Performance konnte ich auch noch nicht wirklich klagen...
Mein aktuelles Projekt ist eine Multi-User-WaWi (also nicht mit embedded DB), wo ich schon eine sehr gute Performance brauche... H2 geht bei mir schon ein bißchen in die Knie bei einem speziellen Fall, aber das ist dann hier zuuuu OT 

EDIT: Im Vergleich JavaDB - H2 kann da jemand über die jeweiligen Performanceunterschiede sagen?
Hatte mich damals für H2 wegen der angepriesenen sehr guten Perf. entschieden und der Tatsache, dass man H2 auch in kommerziellen Anwendungen nutzen kann...


----------



## MarcB (11. Feb 2009)

http://jars.de/java/embedded-java-datenbanken#more-161


----------

