Datenbankverbindung herstellen / beenden

port29

deus.Server
Hallo Leute,

ich habe gerade ein Problem mit Java Servlet und der Datenbankanbindung.

Ich habe eine statische Klasse DB, die von anderen Klassen benutzt wird, es sind ca. 200 Stellen, wo DB.Connect() sowie eine weitere Funktion aus der DB Klasse aufgerufen wird. Das Problem ist jetzt bei mir die eben erwähnte Connect Methode:

Code:
	public static void Connect(){
		try {
			Class.forName("com.mysql.jdbc.Driver");
			
			String url = "jdbc:mysql://" + Config.get_mysql_host() + ":3306/"+Config.get_mysql_dbname();
			
			  if(con!=null)
				  con.close();
			  
			 Connection con = DriverManager.getConnection(url, Config.get_mysql_username(), Config.get_mysql_pwd());
			 stmt = (Statement) con.createStatement();

			 
	    }catch( Exception e ) {
	        e.printStackTrace();
	    }
	}

Das Problem bei mir ist, dass es keinen festen Einstiegspunkt in das Servlet gibt. Man landet direkt in einer Methode (RPC). Deshalb kann jede Methode potentiell die erste Methode sein, die seit dem Restart des Servlets läuft. Deshalb habe ich auch in jede dieser Methoden den Connect Aufruf hinzugefügt. Am Anfang dachte ich eigentlich, dass die Servlets ähnlich laufen, wie ein PHP Script, wo bei jedem Aufruf ein Grundzustand herrscht. Deshalb war am Anfang die con.close(); nicht drinn. Aber irgendwann hat der MySQL wegen zu vielen Verbindungen dicht gemacht. Deshalb habe ich vor jedem Verbindungsaufbau die Verbindung geschlossen.

Eine Zeit lang lief das auch ganz gut, nur dann kam das Problem der Running Conditions. Denn die RPC Methoden sehen in etwa so aus:

Code:
DB.Connect();
DB.checkLogin(key);
DB.Foo();

Jetzt sind sagen wir mal zwei User online. Für den Ersten läuft die Methode durch, er ist jetzt bei DB.Foo(). In diesem Moment ruft ein zweiter User die Methode auf. In diesem Moment reißt con.close(); des zweiten Users dem ersten User die Datenbank Verbindung unter den Füßen weg und das ganze läuft in eine Exception rein.

Deshalb nun dir Frage, wie man so etwas anders realisieren könnte, damit eine Verbindung nur dann aufgebaut wird, wenn eine "alte" abgebrochen wurde, weil z.B. der DB Server mal ein paar min nicht erreichbar war.
 
Da sind halt jede Menge Fehler drin. Ne Datenbank mit statischen Zugriffsmethoden zu Abstrahieren ist ein NOGO. Bau dir ne vernünftige Klasse, instantiier die in Servlet.init() und halte ne instanz der Klasse im Servlet.

Weiterhin solltest du einfach die Ressourcen sauber handlen. Wenn in deinem Code ne Exception fliegt wir weder das Statement noch die Connection sauber geschlossen.

Noch zwei weitere Tipps: die JavaAPI sagt nicht umsonst zum DriverManager: "Bitte DataSource benutzen!". Evtl das JDBCTemplate von Spring anschauen, das löst genau diese Probleme (korrektes Resourcenhandling, Exceptionübersetzung usw.).

Gruß
Ollie
 
Vielleicht ne kleine Anmerkung zum DAO Pattern. Ein DAO ist NICHT dazu da, Datenbanken zu abstrahieren. Es abstrahiert den Datenzugriff für Entitäten, kann also mit dem Repository Pattern aus Eric Evans' "Domain Driven Design" gleichgestellt werden. Du kannst allerdings ohne mühe in einem DAO jede Art von Lowlevel Datenbankzugriffscode implementieren, wohlgemerkt auch beliebig schlecht ;).

Sehr schön ist diese Trennung in Spring zu erkennen. Das JDBCTemplate abstrahiert den Datenbankzugriff (das wo in diesem Fall hier wohl das Problem liegt). Darüber hinaus gibt es abstrakte Basisklassen, die dann eine Instanz des JDBCTemplate anbieten um recht einfach DAOs zu implementieren.

Man muss also vorsichtig sein, das DAO Pattern als Lösung für Probleme mit Datenbanken anzupreisen.

Gruß
Ollie

PS: Ich glaub ich schreib mal nen Tutorial zum JDBCTemplate zusammen ;).
 
Naja, ich finde das DAO halt nicht schlecht. Da man so die ganze Datenhaltungsschicht austauschen kann. Ich finde es auch als Beispiel gut, wie man Datenbankzugriffe aus dem restlichen Code raushält.

Da port29 das ganze mit einer statischen Klasse gemacht hat, wollte ich halt zeigen, wie man es eher machen sollte. :-)

MFG

Sascha
 
Ich sag ja auch nix gegen ein DAO. Nur erschlägt das Pattern nicht die Problematik die er hat. Grundsätzlich ist es natürlich auch in seinem Fall sinnvoll DAOs zu implementieren, aber man muss glaub ich erstmal den ersten Schritt machen und die Datenbank korrekt abstrahieren, bevor man dann ein DAO davor baut. Sonst hast du ein DAO und den gleichen Müllcode von oben drin ;).

REINHAUN!
 
Das stimmt schon, aber dazu hattest du schon was gesagt und ich dachte, das muss ich ja nicht wiederholen. ;-)

Er hatte ja geschrieben, dass er es mit einer statischen Klasse gemacht hat, deshalb hatte ich das gepostet. :-)
Hätte ich vielleicht dazu schreiben sollen. ;-)

MFG

Sascha
 
Da sind halt jede Menge Fehler drin. Ne Datenbank mit statischen Zugriffsmethoden zu Abstrahieren ist ein NOGO. Bau dir ne vernünftige Klasse, instantiier die in Servlet.init() und halte ne instanz der Klasse im Servlet.

Dürfte ich mal bitte fragen, wieso ich keine statischen Methoden für den Datenbankzugriff verwenden darf? Okay, es wäre eigentlich kein Problem, die Datenbank Verbindung aus der DB Klasse rauszuholen, in jeder RPC Methode zunächst eine Verbindung herzustellen und anschließend die Verbindung den statischen Methoden als Argument zu übergeben.

Aber: habe ich dann nicht das gleiche Problem, wie ich am Anfang hatte? Nehmen wir mal an, dass auf der Seite ein großer Load herrscht. 100 User wollen zur gleichen Zeit einen RPC absetzen, also werden auch 100 Verbindungen gleichzeitig zum Server hergestellt. => Der Server wird einige der Verbindungen verweigern, da es bereits viele existieren.
 
Weil statische Methoden an der Klasse hängen und nicht am Objekt. Somit ist das ding nicht threadsafe, was u.a. dein Problem verursacht, dass sich alle Clientinstanzen 1 Verbindung teilen und die erste Clientinstanz, die die Verbindung schließt damit alle anderen Anfragen kaputt macht. Eine starke Verwendung von statischen Methoden ist darüber hinaus ein OO Antipattern, vermindet Testbarkeit, erhöht die Kopplung, macht die Klasse zu einer globalen Variable - alles Designflaws. D.h. im Klartext: statische Methoden soweit wie möglich vermeiden. Vor allem gehen sie halt gar nicht, wenn die Klasse eigentlich inherenten Zustand hält.

@viele Verbindungen. Aus diesem Grund benutzt man eine Implementierung des DataSource Interfaces. Viele Vorbereitete findest du z.B. in commons-dbcp. Da gibts Implementierungen, die für jede Anfrage eine neue Verbindung aufmachen, welche, die Connections poolen usw. Es ist eine ziemliche dumme Idee, Connectionhandling selbst zu implementieren. Zum einen weil sowas nicht trivial ist und sich dementsprechend sehr schnell Fehler einschleichen. Zum anderen gibt es bereits vorgefertigte Implementierungen, die millionenfach im Einsatz und vor allem getestet sind.

Gruß
Ollie
 
Vielen Dank für deine Antwort. Zu meiner Verteidigung muss ich sagen, dass gerade in der Uni beigebracht wird, dass man eher zu statischen Methoden greifen sollte, da man von diesen keine Instanz benötigt. Somit wird nicht für jede Instanz Speicher verbraucht.
 
Zurück