Datenbankverbindungen werden nicht geschlossen

Moritz123

Erfahrenes Mitglied
Hallo!

ich habe hier folgendes Problem:
Ich habe einen Webservice über Axis2(welcher als Servlet in einem Tomcat 5.5) laufen.
Dieser Webservice besteht aus POJOs die wie folgt aufgebaut sind:
Es gibt eine Container-Klasse, von der alle Klassen abgleitet sind. Dieser Container stellt die Datenbankverbindung her und hält eine Variable vom Typ "Connection".
Die Methoden der Kindklassen holen sich diese über die Methode "getConnection()":
Code:
	public synchronized Connection getConnection(){
		try {
			if(this.con == null || this.con.isClosed()){
				this.connect();
				log.info("Made a DB reconnect.");
			}
			return this.con;
		} catch (Exception e) {
			log.error("Unable to reconnect,", e);
		}
		return null;
	}
Zur Information hier noch der Code der Methode connect():
Code:
private void connect(){
		/*
		 * LOCAL
		 */
		try{
			PropertyConfigurator.configure("/usr/share/tomcat5.5/webapps/axis2/WEB-INF/classes/log4j.properties");
			Class.forName("com.mysql.jdbc.Driver");
			this.con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db?autoReconnect=true", "user", "pass");
			log.debug("Database-Connection established");
		}catch (Exception e) {
			log.error("Connection failed!",e);
		}
		
		/*
		 * CONNECTION POOLING FROM TOMCAT
		 */
		if(dataSource == null){
			try {
				InitialContext initCtx = new InitialContext();
				Context ctx = (Context) initCtx.lookup("java:comp/env");
				//Context ctx = new InitialContext();
				if (ctx == null) {
					log.error("No InitialContext available");
				} else {
					dataSource = (DataSource) ctx.lookup("jdbc/MysqlPool");
				}
			} catch (NamingException ne) {
				log.error(ne);
			}
		}
		try {
			this.con = dataSource.getConnection();
		} catch (SQLException e) {
			log.error(e);
		}
		
	}
Ich habe beide Teile getestet - einmal mit direkter DB-Verbindung und einmal mit dem DBCP von Tomcat gestet. Das Ergebnis war jeweils das gleiche:
Bei Version 1 macht die Datenbank irgendwann wegen zu vieler Verbindungen dicht. Bei Version 2 leert sich der Pool schnell und ich bekomme keine Verbindung mehr.

Ich gehe also davon aus, dass die Verbindungen nicht geschlossen werden, weshalb ich folgendes gemacht habe:
Um den Service "nach außen" zu "präsentieren" gibt es eine Fassadenklasse, die im Prinzip alle Servicemethoden beinhaltet. Eine Methode sei hier exemplarisch gegeben (Login):
Code:
public String login(String apikey){
		String session = this.session.login(apikey);
		// ensure connection is returned to pool!
		this.session.freeConnection();
		return session;
	}
wobei freeConnection() aus dem Container stammt und wie folgt aussieht:
Code:
	public synchronized void freeConnection(){
		try {
			if(!this.con.isClosed())
				this.con.close();
			log.info("Closed db-Connection");
		} catch (Exception e) {
			log.error("Unable to close db-Connection", e);
		}
	}

Leider scheint dies absolut nix zu bringen und ich frage mich, wie ich es wohl geschickter anfangen kann.
Macht es Sinn, jede Methode der Kindklassen so umzuschreiben, dass sie eine eigene Connection halten und vor Rückgabe des Wertes diese schließen?

Ich bin mittlerweile wirklich ratlos und brauche schnell Hilfe, da der Termin immer näher rückt. Ich hoffe auf Eure Hilfe.

Vielen Dank!
 
Such doch einfach in deinem Code wo eine Connection verwendet wird, und ob diese nach Benutzung wieder an den Pool zurückgegeben oder geschlossen wird.

Man muss schon sehr ordentlich arbeiten wenn man direkt mit den Datenbanken arbeitet und das nicht von einem Framework kapseln lässt. Für zukünftige Projekte würde ich dir daher empfehlen sowas nicht mehr zu machen.

Eine Möglichkeit für dich wäre nun noch einen Profiler zu verwenden. Da müßtest du entsprechend auch sehen können, wo eine Connection herkommt. Ich habe das immer mit AspectJ gemacht, aber da muss man ein wenig Ahnung von haben.
 
Wenn eine Exception fliegt, wird die DB Verbindung nicht geschlossen. Daher wird der/die close Aurufe in einem finally Block untergebracht.
 
Hi,

Anime hat recht. Überall dort wo Du mit der Connection arbeitest, musst du eventuelle Exceptions behandeln und ggf. die Verbindung schließen. Das schließen sollte immer in einem finally block untergebracht sein und falls Du nicht sicher bist, ob die Verbindung vorher geschlossen worden sein könnte, vorher prüfen, ob sie noch aktiv ist.
Java:
try {
// ... dein Code der etwas mit der Connection macht.
} catch(Exception eConnectivityException) {
  Logger.severe("An unexpected database exception occured when working with the database..");
  Logger.logThrowable(eConnectivityException);
} finally {
  if(connection != null && connection.isOpen()) {
    try {
      connection.close();
    } catch(Exception eDbCloseException) {
      Logger.severe("An unexpected database exception occured when closing the connection.");
      Logger.logThrowable(eDbCloseException);
    }
  }
 
Hallo!

zunächst vielen lieben Dank für die Antworten. Ich habe meinen Code nun so umgebaut, dass sich jede Methode zu Beginn über die getConnection eine eigene Verbindung holt, auf der sie arbeitet. Diese wird vor einem jeweiligen return explizit durch freeConnection geschlossen. Damit geht es eigentlich ganz gut.
Ich werde das aber bei Gelegenheit in einen finally-Block ziehen, ich hatte nur den Eindruck, dass ich dann nicht mehr kontrollieren kann, wann die Verbindung geschlossen wird und es so zu Fehlern kommt. Das hat aber vielleicht einfach damit zuzammengehangen, dass ich immer auf der statischen Vaterconnection gearbeitet habe. Ich bin leider überhaupt kein Java-Pro - das ist mein erstes größeres Projekt.

Aber trotzdem vielen Dank nochmal für Eure Hilfe!
 
Zeja hats schon angesprochen... solchen Code schreibt man nicht selbst. Das ist erstens eine Aufgabe vor der nahezu jeder steht, der mit JDBC arbeiten will und es daher zweiten schon zig Lösungen gibt, die alle schon weitaus besser getestet sind. Ich verweise hierzu gern auf mein Tutorial zum JdbcTemplate von Spring, dass es dir eben die ganze Arbeit des Resourcenhandlings abnimmt.

Gruß
Ollie
 
Hallo!

vielen Dank nochmal für die Beantwortung der Frage. Ich werde das dann mal auf deine Sprin-Lösung umstellen. Wie gesagt, bin ich nicht so Java-bewandert weshalb ich von dieser Möglichkeit nichts wusste. Lässt diese sich auch problemlos mit dem DatabaseConnectionPooling des tomcat vereinbaren?

Vielen Dank und beste Grüße,

Moritz
 
Pooling ist eine Sache der DataSource. Wie das Tutorial zeigt, bekommt das JdbcTemplate genau so eine DataSource. Woher die kommt ist prinzipiell egal. Wenn du die per JNDI aus einem Tomcat holst, prima. Programmatisch erzeugen ist auch kein Thema. Der sauberst Mittelweg ist IMHO deklarative Konfiguration per Springkonfig, aber das kannst du für dich entscheiden.

Gruß
Ollie
 
Da stimme ich Oliver zu. Ist auch nett, wenn man zwischen Entwicklungs-, Test- und Produktivsystem umschalten können will. Da wechseln wir auch gerne mal die Datenbankart. Lokal ist häufig HSQL, Test und Prod sind dann Oracle.
 

Neue Beiträge

Zurück