# Wie seine Datenbankklassen anlegen?



## Guest (14. Jun 2007)

Ich werde demnächst mit einer Datenbank arbeiten und habe eine Frage welche Klassen ich dazu alles brauchen könnte. Ich meine eine Art Struktur um nicht in einer Sackgasse zu landen. Ich weiss, dass die Frage etwas schwierig ist, ohne jeglichen Hintergrund, aber den habe ich bisher auch noch nicht. Ich sammle einfach ein paar Ideen was ihr alles an Klassen anlegen würdet und was ihr alles in eine einzige Klasse packen würdet usw. Danke euch.


----------



## kama (14. Jun 2007)

Hallo,

die wichtigste Frage dabei ist, was möchtest Du erreichen?

MfG
Karl Heinz Marbaise


----------



## Guest (14. Jun 2007)

Mit der Datenbank kommunizieren. Also Tables erschaffen, manipulieren, abfragen, etc. Alles was SQL hergibt. Die Frage war mehr nach Anregungen wie fein man seine Klassenstruktur anlegen sollte. Ich weiss, dass die Frage eigentlich nicht zu beantworten ist, wenn man keine genaueren Angaben machen kann. Was ich wollte waren daher auch nur ein paar Anregungen also ein wenig euerer Fantasie, um selbst einen Startpunkt zu haben. Danke.


----------



## ARadauer (15. Jun 2007)

ich kann ja mal meine grundlegende vorgehensweise beschreiben. ich bin mir sicher, dass viele leute anders arbeiten und dass das best practice ist. bin auch für vorschläge offen.

Also ich schreib mir ein Connection Pool, ein Singleton von dem ich eine offene Verbindundung anfordern und wieder zurück geben kann. Dieser pool macht bei bedarf eine neu verbindung auf oder gibt mir eine bereits vorher verwendete zurück. dann muss nicht bei jedem query eine neue verbindung aufgebaut werden.

Für den Zugriff verwende ich DAOs, also Objekte die den Connection Pool verwenden um auf die DB zuzugreifen und mir bestimmte Objekte zurück liefertn und die gängigsten operationen anbieten (CRUD, Create, Read, update, Delete)
ich nehm für ein ein Objekt ein Dao, also Dao für Benuzter, Bestellungen usw... Meine DAOs sind auch meistens Singletons.

Wenn ich nun mein MVC Pattern habe und das Modell muss zb Benuzter laden, verwendet es das BenuzterDAO um, die Benuzter laden aufzurufen und bekommt die Benutzer zurück. 

Noch ein Paar interfaces, damit alles schön austauschbar ist und fertig.

Hibernate hab ich schon mal versucht, konnte mich damit aber nicht richtig anfreunden, ich werde mich beim nächsten Projekt nochmal damit beschäftigen. Aber ich bin der meinung, dass bei Datenbank lastigen Anwendungen, sehr viel Performance Gewinn durch SQL Optimierungen machbar ist. Ich schreib meine SQL querys lieber selber.

Also als Beispiel mein Connection Pool:


```
package com.bmw.bedarfsplanung.tools;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.Vector;

import javax.sql.DataSource;

import com.bmw.bedarfsplanung.main.Globals;

import oracle.jdbc.pool.OracleDataSource;

/**
 * DbHandler ist ein einfacher Connection Pool, der nach Bedarf Verbindungen zu Datenbanken aufmacht,
 * aber aus performance Gründen, diese nicht gleich wieder schließst sondern, bei erneuter anfrage, bereits
 * offene Verbindungen zurück gibt.
 * @author Q240764
 *
 */
public class DbHandler {

	public static int MIS2 = 0;
	public static int LEMO = 1;

	private ConnectionData[] connData = {new MIS2Db(), new LemoDb()};
	
	//die offenen verbindungen
	private Connection[] connections;
	//ist die verbindung in benützung
	private boolean[] inUse;
	//damit ich weiß welche datenbank eine verbindung gehört
	private int[] database;

	private static DbHandler instance;
	
	//anzahl der maximal offenen verbindunge
	private int maxConnections = 10;

	public static synchronized DbHandler getInstance(){
		if(instance == null){
			instance = new DbHandler();
		}
		return instance;
	}

	/**
	 * Standard konnstruktor, registriert den jdbc Treiber und legt das array für die offenen verbindunge an
	 *
	 */
	private DbHandler(){
		connections = new Connection[maxConnections];
		inUse = new boolean[maxConnections];
		database = new int[maxConnections];
		try {
			DriverManager.registerDriver(new oracle.jdbc.OracleDriver());
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
	}

	/**
	 * liefert eine offene Verbindung, für die angeforderte Datenbank aus dem Connection Pool
	 * ist keine offene vorhanden, wird eine Verbindung aufgebaut
	 * Es muss sicher gestellt sein, dass eine angeforderte Verbindung über returnConnection wieder zurück gegeben wird
	 * @param db die Datenbank für die die Verbindung angeforderd wird
	 * @return eine offenen JDBC Connection
	 * @throws SQLException wenn ein verbindungsaufbau fehlschlägt
	 */
	public synchronized Connection getConnection(int db)throws SQLException {

		//wenn die verbindung noch null ist: anlegen
		for(int i = 0; i < maxConnections; i++){
			if(connections[i]==null){
				connections[i] = DriverManager.getConnection(connData[db].getHost(), connData[db].getUser(), connData[db].getPass());
				inUse[i] = true;
				database[i] = db;
				return connections[i];
				//ist die verbindung, nicht in benützung und von der selben datenbank: zurück geben
			}else if((!inUse[i]) && (database[i]==db)){
				//ist sie geschlossen muss vorher verbunden werden
				if(connections[i].isClosed()){
					connections[i] = DriverManager.getConnection(connData[db].getHost(), connData[db].getUser(), connData[db].getPass());
				}
				database[i] = db;
				inUse[i] = true;
				return connections[i];
			}

		}
		System.out.println("Max Connections überschritten");
		return null;
	}

	/**
	 * über getConnection angeforderte Verbindungen, MÜSSEN über diese Methode wieder zurück gegeben werden
	 * die verbindungen werden nicht geschlossen, sondern über inUse zu Wiederverwendung freigegeben
	 * @param con
	 */
	public synchronized void returnConnection(Connection con){
		for(int i =0; i < connections.length;i++){
			if(con.equals(connections[i])){
				inUse[i] = false;
				return;
			}
		}
		System.out.println("Connection war nicht vorhanden");
	}


	/**
	 * um ein update auszuführen, Connection anfordern, updaten, connection zurück geben
	 * spart im grunde nur Schreibarbeit
	 * @param query, die auszuführende abfrage
	 * @param db die Datenbank, in der die Abfrage ausgeführt werden soll
	 * @throws SQLException
	 */
	public static void executeUpdate(String query, int db) throws SQLException{
		Connection con = getInstance().getConnection(db);
		try {
			Statement stmt = con.createStatement();
			if(Globals.debug){
				System.out.println(query);
			}
			stmt.executeUpdate(query);
		} catch (SQLException e) {
			throw e;
		}finally{
			getInstance().returnConnection(con);
		}		
	}
}
```


Ein DAO sieht auszugsweise beim mir immer so aus (ich weiß mehr prepared Statements benutzen):


```
package com.bmw.bedarfsplanung.benutzer;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Vector;

import com.bmw.bedarfsplanung.main.Globals;
import com.bmw.bedarfsplanung.tools.DbHandler;


public class BenutzerDao implements IBenutzerDAO{
	
	private static  BenutzerDao instance;
	public static BenutzerDao getInstance(){
		if(instance == null){
			instance = new BenutzerDao();
		}
		return instance;
	}
	
	private void BenuzterDao(){
		
	}	
	
	

	
	/**
	 * lädt einen Benutzer mit einer bestimmen qnummer, aus der DB
	 * @param qnummer die Q-Nummer des zu ladenden Benutzers
	 * @return der geladene Benutzer
	 * @throws SQLException 
	 */
	public Benutzer load(String qnummer) throws SQLException {
		//verbindung besorgen
		Connection con = null;
		try {
			con = DbHandler.getInstance().getConnection(DbHandler.MIS2);

			//statement besorgen
			Statement stmt  = con.createStatement();
			String query = "SELECT QNUMMER, NAME, VORNAME, RECHTE FROM "+Globals.BENUTZER_TABLE+" WHERE QNUMMER ='"+qnummer+"'";
			//query absetzen
			ResultSet rset = stmt.executeQuery(query);
			Benutzer b = null;
			if(rset.next()){
				b = new Benutzer();
				b.setQnummer(qnummer);
				b.setName(rset.getString("NAME"));
				b.setVorname(rset.getString("VORNAME"));
				b.setRecht(rset.getInt("RECHTE"));
			}
			return b;
		} catch (SQLException e) {
			throw e;
		}finally{
			DbHandler.getInstance().returnConnection(con);
		}
	}

	/**
	 * ladet alle vorhanden Benutzer aus der Datenbank
	 * @return ein Vector mit allen Benutzern
	 * @throws SQLException 
	 */
	public Vector loadAll() throws SQLException {
//		verbindung besorgen
		Connection con = null;
		try {
			con = DbHandler.getInstance().getConnection(DbHandler.MIS2);

			//statement besorgen
			Statement stmt  = con.createStatement();
			String query = "SELECT QNUMMER, NAME, VORNAME, RECHTE FROM "+Globals.BENUTZER_TABLE+" ORDER BY NAME";
			//query absetzen
			ResultSet rset = stmt.executeQuery(query);
			Vector result = new Vector();
			while(rset.next()){
				Benutzer b = new Benutzer();
				b.setQnummer(rset.getString("QNUMMER"));
				b.setName(rset.getString("NAME"));
				b.setVorname(rset.getString("VORNAME"));
				b.setRecht(rset.getInt("RECHTE"));
				result.add(b);
			}

			return result;
		} catch (SQLException e) {
			throw e;
		}finally{
			DbHandler.getInstance().returnConnection(con);
		}
	}
....
}
```

in einem Model, kommt bei mir dann sowas vor:


```
private Vector<Benutzer> geladene = new Vector<Benutzer>();
	public void reloadBenutzer(){
		geladene = BenutzerDao.getInstance().loadAll();
		this.fireTableDataChanged();
	}
```


also, so arbeite ich meistens. Konstruktive Kritik ist gerne willkommen. Aber bei Kritik bitte mir Verbesserunsvorschlägen.


----------



## Guest (15. Jun 2007)

Ich bedanke mich erstmal bei dir für die vielen mit Code unterstützten Vorschläge. Werde mir bestimmt nach genauerer Analyse das eine oder andere abschauen.


----------



## bronks (2. Jul 2007)

ARadauer hat gesagt.:
			
		

> ... also, so arbeite ich meistens. Konstruktive Kritik ist gerne willkommen. Aber bei Kritik bitte mir Verbesserunsvorschlägen.


Macht einen netten und soliden Eindruck.  :toll: 

Könntest Du bitte ein bissl von der Technik zeigen, wie Du bei Deiner Architektur mit Transaktionen umgehst.

Danke!


----------



## ARadauer (3. Jul 2007)

habe im moment noch auf transaktionen verzichtet. hab da aber schon mal was, gemacht. ich meld mich am abend nochmal, hab den code zu hause

zu meiner vorgehensweise muss ich sagen, dass ich auf einer oracle datenbank probleme mit zu wenigen coursor bekommen habe. ich hab mich dann entschlossen, die Resultsets und Statements  zu schließen, wobei ich aber davon ausgegangen bin, dass er das sowieso nach beendigung der methode machen sollte. bin ich wohl falsch gelegen



```
public Foo bar() throws SQLException { 
          Connection con = null;
          try { //mach hier db zeugs
                con = DbHandler.getInstance().getConnection(DbHandler.MIS2);
           } catch (SQLException e) { 
         throw e; 
      }finally{ 

            if(rset!=null)
	rset.close();
            if(stmt!=null)
	stmt.close();
         DbHandler.getInstance().returnConnection(con); 
      } 
   }
```


----------



## Guest (3. Jul 2007)

@ARadauer
Und was sagt das BMW Java-Messplatz dazu.   :autsch:


----------

