# Ratschlag zur Umsetzung einer client-server-Kommunikation



## lalas (12. Dez 2012)

Hallo,

ich würde gerne einen Ratschlag bekommen wie ich etwas angehen soll:

Ich habe eine laufende Anwendung welche derzeit komplett lokal arbeitet. Es handelt sich dabei um eine Hotel-Software, also Kundenstamm, Buchungen, Zimmerpläne, etc.
Entsprechend gibt es viele Methoden z.B. zum Erstellen von reports (Belegungsplan aller Zimmer für Zeitraum X).
Die Methoden arbeiten mit aller Arten von Übergabeparametern und Rückgabewerten, also strings, int, aber auch Objekte und arrays/Listen, etc.

Die Daten liegen derzeit _nicht_ in einer Datenbank, sondern werden in Dateien abgespeichert.

Ich würde nun gerne die Daten (Zimmerliste, Kundenstamm, etc.) und die Methoden auf einem server laufen lassen.
Der client soll primär also die GUI laufen lassen und nur die Eingaben an den server reichen (dort soll die jeweilige Methode aufgerufen werden) und das Ergebnis (Rückgabewerte) auf client-Seite verarbeitet werden.

Nun scheint mir RMI wohl die professionelle Lösung zu sein, allerdings habe ich damit keinerlei Erfahrung. Die Einarbeitung und code-Menge scheint mir auch relativ hoch auszusehen.

Gibt es Alternativen dazu ? Wäre es sinnvoll sämtlich Parameter, Objekte, etc. einfach per stream über den socket zu schieben und dann die Methode aufzurufen ? Danach den Rückgabewert per stream zurück an den client.
Oder sollte man sich ein Protokoll überlegen und per "Befehl" (einfach ein string?) den server veranlassen die Methoden aufzurufen ?
Oder doch alles auf client laufen lassen und dafür die Stammdaten in eine SQL-Datenbank ablegen ?

Wie würdet ihr da rangehen ?

Das ganze ist übrigens nicht im kommerziellen Einsatz und dient lediglich mir persönlich um meine Java-Kenntnisse zu vertiefen und Erfahrung zu sammeln.

Ich freue mich auf Eure Vorschläge


----------



## ARadauer (12. Dez 2012)

Ich persönlich verwende gerne Spring Remoting... aber in einer nicht Spring Umgebung wird es etwas aufwändiger...

Kannst dir ja mal Simon ansehen... soll anscheinend auch gut funktionieren. Overview - SIMON - root1.de - Software Engineering


----------



## Empire@Work (13. Dez 2012)

Also gegen einen normalen Swing cleint mit db verbindung spricht eigentlich nichts.
Speicher die logindaten (für die db) nicht im Porgramm ab, sondern frag die am anfang von dem benutzer ab, dann ist das sicherheitstechnisch auch vernünftig.

Ansonsten prepared statements benutzen (ausschlieslich), sollte bei der art von software keinen grund geben dynamsich querys zu generieren, damit sind dann auch evtl. probleme mit steuerzeichen, sonderzeichen und injektions ect gelöst. 

Offensichtliche Problem ist lediglich dass mehere Angemeldete personen gleichzeitig den selben Datensatz bearbeiten könnten. 

eine mögl lösung isses, in jede Tabelle die veränderich ist eine row version einzuführen. Wenn jetzt geschreiben werden soll, vorher vergeleichen ob die version der geänderten Zeilen noch zusammenpasst mit der version der zuvor gelesenen daten. (also bei updates und bei deletes, inserts sind unproblematisch)

Aka
Kunde Datum Version
1        11.11  1

Nun bearbeiten zwei personen das datum gleichzeitig, die erste wird erfolg haben, bei der zweiten ist die erwartete version (1) nichtmehr vorhanden, stattdessen hat die letzte änderung die version auf 2 erhöht, und es gibt eine Fehlermeldung dass die datten zwischenzeitlich bereits verändert wurden.


----------



## FArt (13. Dez 2012)

Ich persönlich ziehe EJB3 Spring vor.

Von pure RMI rate ich ab. Nimm lieber ein gutes Framework (eben wie Spring) bzw. einen Applicationserver.
Sobald du dich um die eigentliche Kommunikation gekümmert hast, steigt das Bedürfnis nach Ressourcenhandling, Sicherheit, Authentifizierung / Authorisierung usw. Mach so was nicht zu Fuß, sondern verlasse dich auf Standards (Java EE) oder Frameworks (Spring, ...).


----------



## lalas (13. Dez 2012)

Danke für die Antworten.

Habe mich dazu entschieden die Daten in einer mySQL-Datenbank abzulegen und per vorgefertigten statements alles abzuwickeln.
Listen, Objekte, etc. sind dann immer nur temporär auf dem client um entsprechend user-Eingaben aufzunehmen, Methoden aufzurufen und anschliessend das Resultat in die Datenbank zu schreiben.

Wegen der Möglichkeit das mehrere Personen einen Datensatz simultan bearbeiten dachte ich an folgende Lösung:
In jede table kommt eine column mit einem boolean Feld. Wenn der Datensatz bearbeitet werden soll, wird zunächst dieses boolean geholt und auf client-Seite geprüft.
Wenn FALSE dann wird damit gearbeitet, wenn TRUE wird ein Fehler ausgegeben ("Die Daten sind in Verwendung").

Gespannt bin ich auf die Performanz, aber ich werde es einfach mal antesten.

Zur Nutzung eines frameworks:
Ich weiss man braucht das Rad nicht neu erfinden, aber genau da liegt der Reiz für mich. Außerdem habe ich noch nie ein framework implementiert und habe ehrlich gesagt auch keine Lust mich damit auseinanderzusetzen.
Ich möchte lieber in plain-java arbeiten und die mitgelieferten Funktionen nutzen.


----------



## Bernd Hohmann (13. Dez 2012)

lalas hat gesagt.:


> Wegen der Möglichkeit das mehrere Personen einen Datensatz simultan bearbeiten dachte ich an folgende Lösung: In jede table kommt eine column mit einem boolean Feld. Wenn der Datensatz bearbeitet werden soll, wird zunächst dieses boolean geholt und auf client-Seite geprüft. Wenn FALSE dann wird damit gearbeitet, wenn TRUE wird ein Fehler ausgegeben ("Die Daten sind in Verwendung").



Das geht leider so nicht weil zwischen dem Lesen, prüfen und schreiben des Flags eine gewisse Zeit vergeht in der sich ein anderer Client reinklemmen kann.

Du brauchst eine Datenbank, die Transaktionen unterstützt bzw. bei MySQL den Tabellentyp "InnoDB". 

Wobei ich jetzt nicht weiss, wie das Locking bzw. Transaktionen in der JDBC-API realisiert ist.

Ansonsten:

MySQL :: MySQL 5.5 Reference Manual :: 13.3 MySQL Transactional and Locking Statements
MySQL :: MySQL 5.5 Reference Manual :: 14.3.9 The InnoDB Transaction Model and Locking

Bernd


----------



## lalas (13. Dez 2012)

Hey Danke, dass ist schonmal hilfreich. Meine Testdatenbank ist zufällig eine mySQL InnoDB.

Ich hoffe ich hab das richtig verstanden, auf der verlinkten doku steht (Zitate):

A locking read, an UPDATE, or a DELETE generally set record locks on every index record that is scanned in the processing of the SQL statement[...]

[...]If you have no indexes suitable for your statement and MySQL must scan the entire table to process the statement, every row of the table becomes locked[...]

Klingt für mich so, als ob ein Eintrag automatisch gelockt wird, sobald ein Zugriff drauf erfolgt und zwar bei einem query genauso wie bei einem UPDATE oder DELETE.

Dann müsste doch vermutlich bei einem simultanen Zugriff eine sqlException geworfen werden ?

Eine weitere Fragestellung ist dann beim ersten Rumtesten noch aufgekommen;
Ich hatte mir überlegt, dass ich eine Methode baue der ich einen string übergebe (das SQL statement) und die mir eine ArrayList per get-Methode mit den einzelnen Werten aus der Datenbank zur Verfügung stellt.
Das sieht aktuell so aus:

```
public class dbconnect {

	private String message;
	private List<String> retlist;

//getter/setter hier

public dbconnect(String query) {

// diverse Variablen hier, DB Treiber laden, Verbindung herstellen, etc. 

sqlstatement = conn.createStatement();
            result = sqlstatement.executeQuery(query);
   
            resultMeta = result.getMetaData();
                        
            //Create empty list for storing the db-result values
            List<String> query_return = new ArrayList<String>();
            
            //Go through each row and store column value to list TODO: new list for each column
            while (result.next()) {
				
				int n = result.getMetaData().getColumnCount();
				
				for (int i=1; i<=n; i++) {
					
					String temp_value = result.getString(i);
    			    query_return.add(new String(temp_value));
				}
				retlist = query_return;
            }
```

Funktioniert auch wunderbar soweit, die Ergebnisse werden in eine ArrayList geschrieben. Die widerrum kann ich mit einem getter holen und verarbeiten. 
TODO hier ist noch:
- Bei mehr als einer row als Ergebnis, ein array von ArrayList erzeugen (getter/setter anpassen)

Nun die Fragestellungen bzw. Empfehlungen:

- Macht das so Sinn wie ich es mache (Kritikpunkte) ? Macht der Rückgabe-Typ so Sinn ? 
- Ich könnte per setter eine arrayList erzeugen, welche dann ausgelesen wird um die einzelnen Werte in die dazugehörige Tabelle auf der DB einzufügen/ändern;
Ich vermute das macht nur Sinn, wenn ich einen gesamten Eintrag erstellen/ändern möchte.

Mir fehlt leider komplett die Erfahrung im Umgang mit Datenbanken in diesem Zusammenhang. Ich weiss wohl das ich tendentiell soviel wie möglich direkt in der Datenbank ausführen lassen sollte. Aber irgendwie kommt es mir so vor, dass ich dann im code meines clients hunderte Zeilen pro Methode schreiben muss um kleinste Anpassungen in der Datenbank vorzunehmen.


----------

