# Zuviele DB Connection



## sence (25. Aug 2009)

Hallo,

ich habe bei meinem Web projekt ein Connectionpool für die DB Verbindungen eingebaut.


```
package database;

import java.sql.*;
import java.util.*;

/**
 * Diese Klasse reserviert, recyclet und regelt die 
 * Datenbankverbindungen über das Speichern der 
 * verschiedenen Connects in Vectoren
 */

public class ConnectionPool implements Runnable 
{
	/** Enthält den Datenbanktreiber */
	private String driver;
	
	/** Enthält die Datenbankurl */
	private String url;
	
	/** Enthält dem Usernamen für die Datenbank */
	private String username;
	
	/** Enthält das Passwort für die Datenbank */
	private String password;
	
	/** Enthält die maximale Anzahl der DbConnections */
	private int maxConnections;
	
	/** Gibt an ob über einen Thread auf freie Connections gewartet
	 * werden soll, oder eine SQL Exception geworfen wird, wenn alle
	 * Connects belegt sind
	 */
	private boolean waitIfBusy;
	
	/** Enthält die verfügbaren Connections */
	private Vector availableConnections;
	
	/** Enthält die Verbindungen, die in Betrieb sind */
	private Vector busyConnections;
	
	/** 
	 * Gibt an, ob im Hintergrund Connects gebildet werden sollen
	 * oder nicht
	 */
	private boolean connectionPending = false;

	public ConnectionPool(String driver, String url,
						String username, String password,
						int initialConnections,
						int maxConnections,
						boolean waitIfBusy)
	  throws SQLException
	  {
		this.driver = driver;
		this.url = url;
		this.username = username;
		this.password = password;
		this.maxConnections = maxConnections;
		this.waitIfBusy = waitIfBusy;
		
		if (initialConnections > maxConnections) 
		{
	  		initialConnections = maxConnections;
		}
		
		availableConnections = new Vector(initialConnections);
		busyConnections = new Vector();
		for(int i=0; i<initialConnections; i++) 
		{
	  		availableConnections.addElement(makeNewConnection());
		}
  	}

	/**
	 * Wenn eine Connection aus der Liste geschlossen ist
	 * oder ueber einen Timeout beendet, wird die Connection aus
	 * der Liste der gerade benutzten Connection entfernt und 
	 * evtl. wartende Threads werden benachrichtigt, dass jetzt
	 * wieder eine Connection frei ist, falls das maximale Limit
	 * an Connections bereits erreicht wurde
	 * @return Connection Der eigentliche Connect zur Datenbank
	 * @throws SQLException
	 */
	public synchronized Connection getConnection()
	throws SQLException 
	{
		if (!availableConnections.isEmpty()) 
		{
	  		Connection existingConnection = (Connection)availableConnections.lastElement();
	  		int lastIndex = availableConnections.size() - 1;
	  		availableConnections.removeElementAt(lastIndex);
			
			if (existingConnection.isClosed()) 
			{
				notifyAll(); // Freed up a spot for anybody waiting
				return(getConnection());
	  		} else 
	  		  {
				busyConnections.addElement(existingConnection);
				return(existingConnection);
	  		}
		} else
		  {
		
		/* Hier gibt es drei moegliche Faelle:
		 * 1. Die Anzahl maximaler Connections ist erreicht
		 *  und es wird eine neue im Hintergrund eingerichtet,
		 * 
		 * 2.  Die Anzahl maximaler Connections ist erreicht und der waitifBusy
		 * Flag steht auf false. Die SQL Exception geworfen.
		 *  
		 * 3. Die Anzahl maximaler Connections ist erreicht, aber der waitifBusy
		 * flag steht auf true. Dann wird gewartet, bis eine Connection frei
		 * ist.
		 */
	  	if ((totalConnections() < maxConnections) &&
		!connectionPending) 
		{
			makeBackgroundConnection();
	  	} else if (!waitIfBusy)
	  	  {
			throw new SQLException("Connection limit reached");
	  	  }
	  	  
		try 
		{
			wait();
	  }catch(InterruptedException ie) {}
	  	return(getConnection());
	 }
  }
	
	/**
	 * Ein Connect zur Datenbank kann nur hergestllt werden
	 * wenn eine Verbindung frei ist. Deshalb wird hier
	 * ein Thread gestartet, der eine Verbindung im 
	 * Hintergrund herstellt und dann auf eine freie Verbindung
	 * bzw. eine Nachricht wartet, dass eine Verbindung 
	 * freigegeben wurde.
	 */
	private void makeBackgroundConnection() 
	{
		connectionPending = true;
		try
		{
			Thread connectThread = new Thread(this);
	  		connectThread.start();
		} catch(OutOfMemoryError oome) 
		{
			System.out.println("Fehler in database.ConnectionPool: " + oome.getMessage());
		}
  }

	public void run()
	{
		try
		{
			Connection connection = makeNewConnection();
	  		synchronized(this)
	  		{
				availableConnections.addElement(connection);
				connectionPending = false;
				notifyAll();
	 	 }
		 } catch(Exception e) 
		 {
		 	System.out.println("Fehler in database.ConnectionPool run(): " + e.getMessage());
		 }
	}


	/**
 	* Stellt den eigentlichen Connect zur Datenbank her.
 	* @return Connection Die Datenbankverbindung
 	* @throws SQLException
 	*/
  	private Connection makeNewConnection()
	  throws SQLException 
	  {
	 	try 
	 	{
		// DbTreiber laden  
	  	Class.forName(driver);
	  
	  	// Netzwerkverbindung zur Datenbank herstellen
	  	Connection connection = DriverManager.getConnection(url, username, password);
	  	return(connection);
		}catch(ClassNotFoundException cnfe) {
	  	// Exception werfen, falls der Treiber nicht gefunden werden kann
	  	throw new SQLException("Can't find class for driver: " +
							 driver);
		}
  	}

	/**
	 * Hier werden Connections frei gegeben und aus dem busyConnections Vector
	 * rausgenommen, um beim Vector für freie Connects (availableConnections)
	 * eingefügt zu werden
	 * @param connection Die augenblickliche Connection
	 */
	public synchronized void free(Connection connection) 
	{
		busyConnections.removeElement(connection);
		availableConnections.addElement(connection);
		// Allen Threads Bescheid sagen, dass ein Connect frei geworden ist
		notifyAll();
  	}
	
	/**
	 * Gibt die Anzahl aller Connections zurück
	 * @return int Die Anzahl der Connection im Vector
	 */
	public synchronized int totalConnections()
	{
		return(availableConnections.size() +
		   busyConnections.size());
	}

  /**
   * Bietet die Moeglichkeit, alle Connections explizit zu schliessen.
   * Dies Funktion ist aber mit Vorsicht zu geniessen, wenn noch 
   * Connects zur Datenbank bestehen. Auserdem werden die ja 
   * automatisch geschlossen, wenn der GC laeuft. 
   * Die Funktion bietet jedoch die Moeglichkeit, einer groesseren 
   * Kontrolle.
   */
  public synchronized void closeAllConnections() 
  {
	closeConnections(availableConnections);
	availableConnections = new Vector();
	closeConnections(busyConnections);
	busyConnections = new Vector();
  }

  /**
   * Wird von closeAllConnections() automatisch aufgerufen
   * und ist deshalb privat
   * @param connections Die Anzahl der Verbindungen
   */
  private void closeConnections(Vector connections) 
  {
	try {
	  for(int i=0; i<connections.size(); i++) {
		Connection connection =
		  (Connection)connections.elementAt(i);
		if (!connection.isClosed()) 
		{
		  connection.close();
		}
	  }
	} catch(SQLException sqle) 
	{
	 	System.out.println("Fehler in database.ConnectionPool: " + sqle.getMessage());
	}
  }

  /**
   * Debug Ausgabe für alle Objektparameter
   * @see java.lang.Object#toString()
   */
  public synchronized String toString() {
	String info =
	  "ConnectionPool(" + url + "," + username + ")" +
	  ", available=" + availableConnections.size() +
	  ", busy=" + busyConnections.size() +
	  ", max=" + maxConnections;
	return(info);
  }
}
```

dort wo ich ein Object benötige hole ich mir ein Connect wie folgt:

```
String dbDriver = "secret";
String dbUrl = "secret";
String dbUser = "secret";
String dbPass = "secret";
	  		CON = new ConnectionPool(dbDriver,dbUrl,dbUser,dbPass,5,10,true);
	  		// Den eigentlichen Connect herstellen
	  		connect = CON.getConnection();
connect.executeQuery("TEXT");
```

ich schließe die connection, sobald ich diese nicht mehr benötige, jedoch gibt es folgende Probleme:

A) Es werden sehr schnell mehr als nur die max anzahl an db Connect
, also mehr als 10, obwohl die geschlossen sind.
UND wenn ich ein Statement mache, sehe ich im sql aber gleich 12 oder mehr SQL Querys (Mysql Admin)

B) wenn ich mansche Connects schließe, komme ich auf einer anderen Seite nicht weiter, deshalb muss ich ihn offen lassen, obwohl kein connect per Parameter an eine funktion oder ähnliches übergeben wird, wofür er den brauchen könnte. oO

C) 
würde ich dies gerne mit einer Datasource und der context.xml realisieren, jedoch nicht im Tomcat selber, da dies dann zu gebunden ist an den Tomcat.
Hat jemand eine schnelle implementierung ggf dafür ?

vielen herzlichen Dank!


----------



## tfa (25. Aug 2009)

Warum selber programmieren? Das gibt's doch alles schon: Apache DBCP, C3PO.


----------



## sence (25. Aug 2009)

Hey, jo die sind aber leider wie ich lese in der Server.xml aufzunehmen, aber wenn ich das projekt nun nicht unter tomcat deploye, da ich eine unabhänige benötige, da diese für weitere Webseiten auch verwendet wird, und nicht weiß auf welchem Server der Kunde das Projekt deployen möchte, sollte es intigriert sein 

greets


----------



## tfa (25. Aug 2009)

Du kannst das natürlich auch alles von Hand machen, wenn es unbedingt sein muss. Hier ein Beispiel mit DBCP: http://www.java-forum.org/blogs/tfa/22-snippet-database-connection-pool.html


----------



## maki (25. Aug 2009)

> Hey, jo die sind aber leider wie ich lese in der Server.xml aufzunehmen


Da hast du dich verlesen 

Man kann natürlich Tomcat auch eine Pooled DS verwalten lassen, aberjede webapp kann auch ihr eigenes Ding machen.

Jedenfalls wäre es mehr als überflüssig einen eigenen CP  zu bauen..


----------

