# Datenbank-Abstraktion



## thomas86 (11. Jul 2011)

Hallo,

ich arbeite gerade an einem kleinen Programm für die Uni. Als DB soll dieses CSV-Files verwenden, jedoch will ich die Möglichkeit haben später leicht auf SQL, SQLite oder Ähnliches umzusteigen. Also versuche ich gerade die Datenbank Geschichte so weit wie möglich zu abstrahieren.
Meine "CSV-Datenbank-Treiber"-Klasse hat Methoden wie getAllRows() oder deleteRow(). Diese Methoden soll natürlich später auch meine anderen Datenbank-Klasse bereitstellen. Über ein Interface lege ich fest das beide die benötigten Methoden liefern.
In meinem Programm soll nun einfach festgelegt werden, welche Datenbank-Klasse verwendet wird. Also zB:

[Java]
DB DBDriver = new DBDriver("CSV", "test.csv"); 
DBDriver.getAllRows();
[/Java]

Wobei also Attribute der Datenbank-Typ sowie ein Connection-String übergeben wird. Bei CSV ist das halt der Pfad zur CSV-Datei.

Die Klasse DB macht dann nix anderes als ein Object der Klasse DBDriverCSV zu erstellen und den Connectionstring "test.csv" dem Konstruktor zu übergeben. Zusätzlich wird dann über das Object DBDriverCSV.getAllRows() aufgerufen.

Ich hoffe ich habe meine Problematik etwas verständlich dargestellt. Wie würdet ihr so etwas ansatzweise realisieren?


mfg


----------



## Pippl (11. Jul 2011)

Stichwort DAO hilft dir wahrscheinlich weiter.

Mfg


----------



## SlaterB (11. Jul 2011)

> Ich hoffe ich habe meine Problematik etwas verständlich dargestellt. Wie würdet ihr so etwas ansatzweise realisieren?

das Thema ist vielleicht verständlich, nicht deine Problematik, 
grundsätzlich klingen deine Überlegungen gut und du hast doch anscheinend schon diverse Interface/ Klassen,
klappt es oder hast du ein bestimmtes Problem? 

aber noch Stilkritik:
Variablen klein schreiben, und möglichst nicht wie Klassen benennen,

die Zahl der Klassen begrenzen, was ist denn nun DB, was ist DBDriver und dann auch noch DBDriverCSV?
2 statt 3 reichen sicher,


----------



## thomas86 (11. Jul 2011)

Ich schreibe Variablen auch sonst klein, habe es hier aber eigenartiger Weise nicht getan.



> die Zahl der Klassen begrenzen, was ist denn nun DB, was ist DBDriver und dann auch noch DBDriverCSV?



DB soll hier die abstrakte Datenbank-Klasse sein, welche auf die einzelnen Datenbankklassen (Wie z.B.: DBDriverCSV, DBDriverSQL, DBDriverSQLite) zugreift. Ich hatte mir gedacht das über Reflections zu lösen, was soweit funktioniert. Nur ist das so üblich?

Hier etwas Code:

```
public class DB implements DBInterface {
    private Object oDBDriver = new Object();
    private Class<?> clDBDriver = null;
    private Constructor<?> conDBDriver = null;
    
    public DB(String strDBType, String strConnectionString, String strAdditional) {        
        
        // get class by name
        try {
            this.clDBDriver = Class.forName("database.DBDriver" + strDBType);
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
        
        // get constructor
        try {
            this.conDBDriver = this.clDBDriver.getConstructor(String.class, String.class);
        } catch (NoSuchMethodException ex) {
            ex.printStackTrace();
        } catch (SecurityException ex) {
            ex.printStackTrace();
        }
        
        // create an object
        try {
            this.oDBDriver = (DBDriverCSV) this.conDBDriver.newInstance(strConnectionString, strAdditional);
        } catch (InstantiationException ex) {
            ex.printStackTrace();
        } catch (IllegalAccessException ex) {
            ex.printStackTrace();
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
        } catch (InvocationTargetException ex) {
            ex.printStackTrace();
        }
    }
    
    @Override
    public ArrayList<String[]> getAllRows() {        
        Object oReturn = null;
        Method m = null;
        
        // get method
        try {
            m = this.clDBDriver.getMethod("getAllRows");            
        } catch (NoSuchMethodException ex) {
            ex.printStackTrace();
        } catch (SecurityException ex) {
            ex.printStackTrace();
        }
        
        // invoke methode and asign data
        try {
            oReturn = m.invoke(this.oDBDriver);
        } catch (IllegalAccessException ex) {
            ex.printStackTrace();
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
        } catch (InvocationTargetException ex) {
            ex.printStackTrace();
        }
        
        return (ArrayList<String[]>)oReturn;
    }
}
```

Aufruf der DB-Klasse

```
DB dbhandler = new DB("CSV", "document.csv", ";");
        
        ArrayList<String[]> alStrRows = dbhandler.getAllRows();
```


----------



## turtle (11. Jul 2011)

Ich würde den JDBC-ODBC-Treiber ansehen, weil dieser auf Excel/CSV-Dateien arbeiten kann. 

Habe ich aber noch nicht ausprobiert.

PS: Habe ich grad mal ausprobiert. Zumindest Select/Insert gehen ohne Probleme. Ein Tool wie Squirrel kann auch darauf zugreifen. Also ich würde keine eigene DB-Abstraktion schreiben!


----------



## SlaterB (11. Jul 2011)

Konstruktor über Reflection ist strenggenommen möglich, wenn es dem Aufrufer nicht zuzumuten ist, die Klasse direkt zu benenen oder gar den Konstruktor direkt aufzurufen,

aber erstens kann man das vereinfachen:

```
try {
  Zeile 1;
  Zeile 2;
  Zeile 3;
} catch(Exception e) {
   throw new RuntimeException(e);
}
```
und zweitens hat nicht wenigsten DB selber den Überblick über alle vorhanden Klassen? 
bei Plugin-Entwicklung oder vergleichbar führt kein Weg dran vorbei, deshalb bei JDBC-Treibern auch in etwa so gemacht,

bei deinen eigenen Klassen ginge evtl.

```
if ("CSV" eq type) {
 new DBDriverCSV(x,y);
} else if { ..
```

--------

während Instanziierung so umständlich bleiben mag gibts für getAllRows() nun keine Ausrede, diese Methode kann gar nicht anders als

```
public ArrayList<String[]> getAllRows() { 
    return this.oDBDriver.getAllRows();
}
```
lauten, oDBDriver muss in einer Variablen vom Interface gespeichert werden,
ein Interface welches die Methoden vorgibt, dann ist der Aufruf direkt möglich


alle Methoden derart doppelt zu implementieren ist natürlich nicht schön,
besser sollte DB wohl eine Factory sein und oDBDriver zurückgeben,
welches sich der Aufrufer dann unter dem Interface speichert und dort direkt getAllRows() usw. aufruft,
oder hat DB noch irgendwelche weiteren Funktionen?
je nach Verhältnis von Zusatzcode zu Durchreichen kann es das oDBDriver-Objekt auch weiter verstecken


----------



## thomas86 (11. Jul 2011)

Schonmal danke für deine Antwort. Leider bin ich nach den Exceptions ausgestiegen. 

Wie bekommt denn DB Überblick über alle vorhanden Klassen? Einfach im Verzeichnis nach entsprechenden Klassen schauen? 

Mein Interface sieht momentan so aus:

```
package database;

import java.util.ArrayList;

interface DBInterface {
    public ArrayList<String[]> getAllRows();
    public ArrayList<String[]> getRowsByValue(int iColumn, String strValue);    
    public ArrayList<Integer> findRowByValue(int iColumn, String strValue);
    public boolean insertRow(String[] aStrRow);
    public void removeRow(int iWhereColumn, String strWhereValue);    
    public void updateRow(int iWhereColumn, String strWhereValue, int iColumn, String strValue);    
    public void setRowLimit(int iRowLimit);
    public int getAffectedRows();
}
```


----------



## SlaterB (11. Jul 2011)

nein, im übertragenen Sinne,
wie schon geschrieben direkt programmieren

```
if ("CSV" eq type) {
 new DBDriverCSV(x,y);
} else if { ..
```
das ist doch eingänglicher als Reflection, oder?


----------



## thomas86 (11. Jul 2011)

Ok ich glaube nun hab ich es.


```
public class DB implements DBInterface {
    public DBInterface oDBDriver = null;
    
    public DB(String strDBType, String strConnectionString, String strAdditional) {        
        if(strDBType.equals("CSV")){
            oDBDriver = new DBDriverCSV(strConnectionString, strAdditional);
        }
    }
    
    @Override
    public ArrayList<String[]> getAllRows() {        
        return this.oDBDriver.getAllRows();
    }
}
```

Das ist doch jetzt aber nicht Factory Pattern oder? Weil ich ja das Object über den Konstruktor erstelle.


----------



## SlaterB (11. Jul 2011)

klassisch Factory wäre etwas anderes, ja, eine Methode die das DBInterface-Objekt zurückgibt, 
habe ich als Alternative zu DBInterface intern in DB genannt,

übrigens ist 'Interface' im Namen eines Interfaces ziemlich dämlich,
und generell sollten eher die Interface kurze Namen bekommen, da idealerweise häufig verwendet,
vergleiche List zu ArrayList, 
DB wäre vielleicht guter Name fürs Interface, 

auch solltest du auf wichtige Begriffte wie Driver als Bestandteil des Variabennamens verzichten, wenn die Klasse/ das Interface nicht auch DBDriver oder so heißt, dann doch lieber db, auch wenn es wie die Klasse heißt, aber nicht künstlich verlängern,
noch 'natürlicher' sehe ich die Situation andersrum, die Variable kürzer als der Klassenname, [c]DBDriver db[/c] z.B.


aber vielleicht machst du das bisher provisorisch oder hast eigene Gründe bzw. so wichtig ist das auch nicht


----------



## thomas86 (11. Jul 2011)

Für die Datenbank-Klassen (SQL, CSV ...) habe ich jetzt ein eigenes package driver erstellt:

database
database.DB​database.DBProvider​database.driver​database.driver.CSV​database.driver.SQL​
Das Interface heißt nun DB und die Klasse, welche die Methodenbereitstellt DBProvider.

Die Provider-Klasse sieht nun so aus:

```
public class Provider {
    public DB db = null;
    
    public Provider(String strDBType, String strConnectionString, String strAdditional) {        
        if(strDBType.equals("CSV")){
            db = new CSV(strConnectionString, strAdditional);
        }
    }
    
    public DB getdb(){
        return this.db;
    }
}
```

Der Aufruf:

```
DBProvider db = new DBProvider("CSV", "document.csv", ";");
ArrayList<String[]> alStrRows = db.getdb().getAllRows();
```

Wie findest du das jetzt, könnte man das als solide Lösung betrachten?

Wie wäre das wenn jetzt zB die SQL-Klasse zusätzliche Methoden hätte, welche die CSV-Klasse nicht bereitstellt. Wie könnte man auf diese zugreifen.


----------



## SlaterB (11. Jul 2011)

statt 

```
DBProvider db = new DBProvider("CSV", "document.csv", ";");
ArrayList<String[]> alStrRows = db.getdb().getAllRows();
```
ist als Alternative zu überlegen

```
DB db = new DBProvider("CSV", "document.csv", ";").getdb(); // bzw. Factory statische Methode create(..);
ArrayList<String[]> alStrRows = db.getAllRows();
```
für zusätzliche Methoden wäre ein erweitertes Interface denkbar,
so wie wiederum als Beispiel List von Collection erweitert,
Cast dann aber nötig, als Test vielleicht eine boolean-Methode, schöner als instanceof-Test


----------

