+ Antworten
Seite 1 von 2 1 2 LetzteLetzte
Ergebnis 1 bis 20 von 28

Thema: Datenbank Verständnisproblem

  1. #1
    Erfahrener Benutzer
    Registriert seit
    16.08.2008
    Ort
    Mecklenburg-Vorpommern
    Beiträge
    375

    Standard Datenbank Verständnisproblem

    Hallo,

    leider ist das Buch, welches ich zum Lernen verwende, gänzlich ungeeignet, wenn man tatsächlich ein Quereinsteiger aus einer Sprache wie PHP ist.

    Ich habe dabei ein paar Verständnisprobleme.

    Mir wird vom Autor erklärt, ich solle doch bitte Class.forName("sun.jdbc.odbc.JdbcOdbcDriver") aufrufen.
    Schön und gut, man hätte ruhig auch noch schreiben können, ob man das noch vor der Klassendeklaration machen soll - sprich: wie bei einer Import-Anweisung - oder in der Klasse an sich.

    In der Treiberklasse sind ein paar Methoden zur Verbindungsherstellung enthalten, so der Autor.

    Aufgrund einiger Installationsprobleme mit dem Treiber (das hätte man hier ebenfalls anfängerfreundlich einfach mal an einem Beispiel demonstrieren können...) habe ich mir im Internet ein fertiges Beispiel herausgesucht.

    Hier der Quellcode:
    Code:
    package MySQL;
    import java.sql.*;
    
    public class MySQL 
    {
    		 
    	    private String Username = "root";
    	    private String Password = "password";
    	    private String Driver = "com.mysql.jdbc.Driver";
    	    private String URL = "jdbc:mysql://localhost:3306/jokes";
    	    private Connection connection;
    	 
    	    public MySQL() {
    	        this.Connect();
    	    }
    	   /* Sollte der Konstruktur ohne argumente aufgerufen werden, werden die in der klasse genutzten Werte genommen. */
    	 
    	 
    	    public MySQL(String user, String pass) {
    	        this.Username = user;
    	        this.Password = pass;
    	        this.Connect();
    	    }
    	 
    	 /* Sollte der Konstruktur mit den Argumenten user und pass aufgerufen werden, werden diese definiert und dann Verbunden. */
    	 
    	    public void Close() {
    	        if (this.connection != null) {
    	            try {
    	                this.connection.close();
    	            } catch (Exception e) {
    	            }
    	        }
    	    }
    	 
    	/* Die Funktion Close() schließt das Query um den Speicher wieder frei zu geben */
    	 
    	    public void Connect() {
    	        try {
    	            Class.forName(this.Driver);
    	            this.connection = DriverManager.getConnection(this.URL,
    	                    this.Username, this.Password);
    	        } catch (Exception e) {
    	            e.printStackTrace();
    	            System.out.println("Error Connecting with User:" + Username + " and Password:" + Password);
    	        }
    	    }
    	 
    	/* Connect registriert den JDBC Treiber und versucht eine Verbindung herzustellen. Sollte dies nicht möglich sein, wird eine Exception ausgelöst */
    	 
    	    public boolean isConnected() {
    	        try {
    	            ResultSet rs = this.ReturnQuery("SELECT 1;");
    	            if (rs == null) {
    	                return false;
    	            }
    	            if (rs.next()) {
    	                return true;
    	            }
    	            return false;
    	        } catch (Exception e) {
    	            return false;
    	        }
    	    }
    	 
    	/* frägt ein einfaches Query ab, welches "1" zurück liefert, falls man verbunden ist */
    	 
    	    public ResultSet ReturnQuery(String query) {
    	        try {
    	            Statement stmt = this.connection.createStatement();
    	            ResultSet rs = stmt.executeQuery(query);
    	            return rs;
    	        } catch (SQLException e) {
    	            System.err.println(e.toString());
    	            return null;
    	        }
    	    }
    	 
    	/* Sendet ein Query und erwartet eine Rückgabe in Form eines ResultSet */
    	 
    	    public boolean RunQuery(String query) {
    	        try {
    	            Statement stmt = this.connection.createStatement();
    	            return stmt.execute(query);
    	        } catch (Exception e) {
    	            //  e.printStackTrace();
    	            return false;
    	        }
    	    }
    	/* Führt das query aus, erwartet aber keine Rückantwort des Servers. */
    
    }
    
    Ich habe - bis auf Passwort, Benutzername und Datenbank NICHTS an dieser Klasse angepasst - und sie läuft.
    Code:
    private String Driver = "com.mysql.jdbc.Driver";
    
    Warum funktioniert das? Es gibt kein Unterverzeichnis im Eclipse-Ordner, dass auf das Verzeichnis passen würde.
    Es gibt allgemein kein solches Verzeichnis C:\com\mysql...

    Mein Treiber liegt im Verzeichnis ...\Java\jre6\lib\ext.
    Wieso funktioniert das hier in diesem Falle?

    Und noch eine Frage zu meinem derzeitigem "Level":
    Sollte man als Anfänger in der Lage sein, so eine MySQL-Klasse schreiben zu können?
    An der obigen Klasse stört mich ein wenig, dass sie noch nicht dokumentiert worden ist - hier werde ich wohl mal Anpassungen vornehmen.

    Anmerkung der Projektleitung

    Ergänzend sei an dieser Stelle erwähnt, dass die Klasse mittlerweile überarbeitet wurde:
    Datenbank Verständnisproblem
    Geändert von Jann Hendrik (06.12.2009 um 19:18 Uhr)

  2. #2
    Erfahrener Benutzer Avatar von $traight-$hoota
    Registriert seit
    15.09.2005
    Ort
    Königreich Flieden
    Beiträge
    724

    Standard

    Java lädt seine Bibliotheken nicht nur von einem Ort, sondern sucht an mehrern Stellen, die im sogenannten Classpath definiert sind. Mehr Infos dazu stehn zum Beispiel in Java ist auch eine Insel, was ich auch generell zum Lernen von empfehlen kann.
    Generell ist das zwar vergleichbar mit einem Import in PHP aber es hat einige Unterschiede. In PHP wird bei Import eine Datei ausgeführt und der darin befindliche Code interpretiert; wenn das ne Klassendefinition ist, wird eben diese Klasse zur Verfügung gestellt. In Java werden dagegen direkt Klassen geladen, keine Dateien. Wo und in welcher Datei diese Klasse liegt ist für die Programmierung ziemlich egal, darum kümmert sich die Virtual Machine.
    In Java gibt es mehrere Wege, eine Klasse zu laden: Der übliche ist, dass man vor Beginn einer Klassendefinition die verwendeten Klassen oder ganze Packages angibt, die zur Laufzeit zur Verfügung stehen sollen (in deinem Beispiel lädt "import java.sql.*;" alle Klassen im Package java.sql). Eine andere Methode ist Class.forName, was einen String übergeben bekommt und somit erst irgendwo im Programmablauf ausgeführt wird. Das nennt man dann dynamisches Nachladen von Klassendefinitionen und diese können dann veränderlich sein (zum Beispiel könnte eine Benutzereingabe den zu verwendenden Treiber erfragen).


    Klar, so ne MySQL-Klasse ist schon ein bischen tiefere Materie, aber wenn du dich mit Objektorientierter Programmierung von PHP her schon ein bischen auskennnst und die Beispielklasse an sich verstehst, was da abläuft, kannst du dich da ruhig rantrauen.
    Was dir noch unklar ist, kannst du ja nachfragen. Wie eben diese Treibergeschichte.

    Noch ein Hinweis: Kommentare schreibt man normalerweise vor den Codeteil, auf den er ich bezieht, sowohl in Java als auch in PHP und so ziemlich jeder anderen Sprache.
    Außerdem ist es (in Java) praktisch, Kommentare, die sich auf Methoden, Klassen oder Eigenschaften beziehen, mit zwei Sternchen einzuleiten: /**
    Dadurch lässt sich dann nämlich bei Bedarf ganz einfach automatisch eine API-Dokumentation generieren.
    Was an der Klasse auch noch stört, ist die Tatsache, dass Eigenschaften und Methodennamen normalerweise klein geschrieben werden. Das ist so Konvention in den meisten gängigen Sprachen und in Java besonders wichtig, um einen Unterschied zu inneren Klassen zu haben (die werden groß geschrieben).
    Geändert von $traight-$hoota (29.11.2009 um 11:43 Uhr)
    Weißt Bescheid - Scheiß wie weit

  3. #3
    Erfahrener Benutzer Avatar von Bleistift
    Registriert seit
    31.12.2006
    Ort
    Zürich
    Beiträge
    424

    Standard

    Zitat Zitat von Sekundentakt Beitrag anzeigen
    Mir wird vom Autor erklärt, ich solle doch bitte Class.forName("sun.jdbc.odbc.JdbcOdbcDriver") aufrufen.
    Schön und gut, man hätte ruhig auch noch schreiben können, ob man das noch vor der Klassendeklaration machen soll - sprich: wie bei einer Import-Anweisung - oder in der Klasse an sich.
    Ausserhalb der Klasse hast du nie etwas, ausser import- und package-Anweisungen.

    Zitat Zitat von Sekundentakt Beitrag anzeigen
    Mein Treiber liegt im Verzeichnis ...\Java\jre6\lib\ext.
    Wieso funktioniert das hier in diesem Falle?
    Ich vermute mal, dass der Treiber eine JAR-Datei ist? In dieser Datei (kannst du mit einem ZIP-Entpacker überprüfen) befindet sich das besagte Package.

    //EDIT: $traight-$hoota war schneller :)
    . <-- This is Punkt. Copy Punkt into your signature to help him on his way to world domination.

  4. #4
    Erfahrener Benutzer
    Registriert seit
    16.08.2008
    Ort
    Mecklenburg-Vorpommern
    Beiträge
    375

    Standard

    Danke euch beiden!

    Noch ein Hinweis: Kommentare schreibt man normalerweise vor den Codeteil, auf den er ich bezieht, sowohl in Java als auch in PHP und so ziemlich jeder anderen Sprache.
    Außerdem ist es (in Java) praktisch, Kommentare, die sich auf Methoden, Klassen oder Eigenschaften beziehen, mit zwei Sternchen einzuleiten: /**
    Richtig. Dahingehend will ich die Datei noch ummodellieren. Wie gesagt: Bis auf ein paar Parameterangaben ist das Ding da oben reines Copy+Paste gewesen. Was darin abgeht, habe ich aber weitestgehend (auch dank meines Lehrbuches) verstanden.

    Aus "Java ist auch eine Insel":
    Ist eine Klasse auch im Erweiterungsverzeichnis nicht zu finden, beginnt die Suche im Klassenpfad (Classpath). Diese Pfadangabe besteht aus einer Aufzählung einzelner Verzeichnisse, Klassen oder Jar-Archive, in der die Laufzeitumgebung nach den Klassendateien sucht. Standardmäßig ist dieser Klassenpfad auf das aktuelle Verzeichnis gesetzt (».«).
    Mit aktuellem Verzeichnis ist das Verzeichnis gemeint, in dem meine Klasse sich befindet, oder auf die der Classpath zeigt? Ich habe den Classpath selbst nie gesetzt - dank Eclipse funktioniert das automatisch.

    Leider kann ich Kommandozeilenbefehle über meinen Windowsaccount nicht ausführen, da er mir beim Aufruf von CMD bereits den Pfad C:\Dokumente...\User... vorgibt.
    Gibt's dafür eigentlich eine alternative Kommandozeile? Dann könnte ich auch mal selbst solche CP-Angaben eingeben.

    Ich vermute mal, dass der Treiber eine JAR-Datei ist? In dieser Datei (kannst du mit einem ZIP-Entpacker überprüfen) befindet sich das besagte Package.
    Es handelt sich um eine .JAR-Datei. Mich hatte lediglich verwundert, dass die aufgeführte Klasse auf ein Verzeichnis zeigt, dass es eigentlich gar nicht gibt und trotzdem funktioniert. Meine Treiber-.JAR-Datei heißt "mysql-connector-java-5.1.10-bin" und liegt im Verzeichnis "C:\Programme\Java\jre6\lib\ext".

  5. #5
    Erfahrener Benutzer Avatar von $traight-$hoota
    Registriert seit
    15.09.2005
    Ort
    Königreich Flieden
    Beiträge
    724

    Standard

    Zitat Zitat von Sekundentakt Beitrag anzeigen
    Mit aktuellem Verzeichnis ist das Verzeichnis gemeint, in dem meine Klasse sich befindet, oder auf die der Classpath zeigt? Ich habe den Classpath selbst nie gesetzt - dank Eclipse funktioniert das automatisch.
    Das aktuelle Verzeichnis ist das, von dem aus die VM gestartet wird. Bei Eclipse ist das automatisch auf das Projekt-Verzeichnis gesetzt.
    Der Classpath kann neben dem aktuellen Verzeichnis auch zusätzliche Orte enthalten, wo Bibliotheken gefunden werden können; das ist ähnlich wie der include_path in PHP.
    Zitat Zitat von Sekundentakt Beitrag anzeigen
    Leider kann ich Kommandozeilenbefehle über meinen Windowsaccount nicht ausführen, da er mir beim Aufruf von CMD bereits den Pfad C:\Dokumente...\User... vorgibt.
    Gibt's dafür eigentlich eine alternative Kommandozeile? Dann könnte ich auch mal selbst solche CP-Angaben eingeben.
    Kannst du nicht sowas wie "cd C:\Programme\Java\bin" machen?? Aber die Java-Executable lässt sich normalerweise von jedem beliebigen Verzeichnis aus ausführen. Per Parameter -cp kannst du dann auch einen eigenen Classpath setzen: "java -cp C:\java\meineKlassen TestKlasse"

    Zitat Zitat von Sekundentakt Beitrag anzeigen
    Es handelt sich um eine .JAR-Datei. Mich hatte lediglich verwundert, dass die aufgeführte Klasse auf ein Verzeichnis zeigt, dass es eigentlich gar nicht gibt und trotzdem funktioniert. Meine Treiber-.JAR-Datei heißt "mysql-connector-java-5.1.10-bin" und liegt im Verzeichnis "C:\Programme\Java\jre6\lib\ext".
    Java behandelt auch .jar-Dateien wie Verzeichnisse, wenn also in einer Datei lib.jar ein Verzeichnis test ist und darin eine Datei Test.class ist diese als test.Test verfügbar (vorausgesetzt lib.jar liegt im Classpath).
    Hier mal ein Beispiel, wie Klassen im Classpath gefunden werden können. Die Bibliothek "mysql-connector-java-5.1.10-bin.jar" hat im Inneren einen verzeichnisartigen Aufbau:
    Code:
    de/
       shoota/
           Test.class
           test/
              TestKlasse.class
    mysql-connector-java-5.1.10-bin.jar
       com/
          mysql/
              jdbc/
                 Driver.class
                 AndereKlasse.class
    
    In diesem Classpath sind also die Klassen com.mysql.jdbc.Driver, com.mysql.jdbc.AndereKlasse, de.shoota.Test und de.shoota.test.TestKlasse bekannt
    Das kannst du dir ganz einfach mit nem (De-)Kompressionsprogramm anschauen, Jar-Dateien sind nichts anderes als Zip-Dateien nur mit einer anderen Endung.
    Geändert von $traight-$hoota (29.11.2009 um 15:08 Uhr)
    Weißt Bescheid - Scheiß wie weit

  6. #6
    Erfahrener Benutzer
    Registriert seit
    16.08.2008
    Ort
    Mecklenburg-Vorpommern
    Beiträge
    375

    Standard

    Jar-Dateien sind nichts anderes als Zip-Dateien nur mit einer anderen Endung.
    Und hier lag der Fehler begraben!
    Ich habe .JAR - Dateien für etwas ganz anderes gehalten ;).

    Das aktuelle Verzeichnis ist das, von dem aus die VM gestartet wird. Bei Eclipse ist das automatisch auf das Projekt-Verzeichnis gesetzt.
    Der Classpath kann neben dem aktuellen Verzeichnis auch zusätzliche Orte enthalten, wo Bibliotheken gefunden werden können; das ist ähnlich wie der include_path in PHP.
    Damit ich das richtig verstehe: Ich verteile irgendwann einmal das ein oder andere Java-Programm. Der User speichert es bei sich in C:\AlfredsOrdner\Sekundentakt\Programm\Java\Java.c lass
    Das würde heißen, dass die VM "HIER" mit dem Ordner "Java" gleichsetzt, richtig?
    Würde ich jetzt noch auf Daten aus dem Programm-Ordner zugreifen wollen, liegt ein Designfehler vor.

    Stattdessen müsste der Pfad
    C:\AlfredsOrdner\Sekundentakt\Java\Programm für den Programmordner und
    C:\AlfredsOrdner\Sekundentakt\Java\Java.class
    für das Programm lauten?

    Ich greife dann auf den Programmordner mit Java.Programm zu?

    An die Kommandozeile wage ich mich morgen. Für heute liegen die Nerven blank.
    Geändert von Sekundentakt (29.11.2009 um 15:43 Uhr)

  7. #7
    Erfahrener Benutzer Avatar von $traight-$hoota
    Registriert seit
    15.09.2005
    Ort
    Königreich Flieden
    Beiträge
    724

    Standard

    Schön dass das mit den JARs schon mal geklärt ist ;)

    Deine erste Pfadangabe deckt sich nicht mit den anderen, ich gehe aber davon aus, dass hier auch C:\AlfredsOrdner\Sekundentakt\Java\Programm\Java.c lass gemeint ist.
    Wichtig: Du kannst ja eine class Datei ja nicht direkt ausführen sondern brauchst dazu den Java-Interpreter. Wenn du diesen aus dem Verzeichnis C:\AlfredsOrdner\Sekundentakt\Java\Programm aufrufst, dann ist das das aktuelle Verzeichnis.

    Du verwechselst glaub ich die Begriffe Ordner und Package. Ein Package kann wie gesagt aus einer "normalen" Verzeichnisstruktur bestehen oder eine Verzeichnisstruktur in einem JAR-Archiv sein. Ordner, die ein Package(-system) darstellen werden i.d.R. nicht wie beliebige andere Ordner verwendet und sollten außer den Class-Dateien nicht viel weiter enthalten (Ausnahme sind andere klassenrelevante Ressourcen).
    Weißt Bescheid - Scheiß wie weit

  8. #8
    fka Gottzilla Avatar von The_S
    Registriert seit
    02.02.2005
    Ort
    Würzburg
    Beiträge
    777

    Standard

    Sorry, falls ich dich hier vor den Kopf stoßen sollte, aber:

    Les ein Buch! Von Anfang bis zum Ende! Lern die Basics! Arbeite am Anfang ohne IDE, damit du ein Grundverständnis für die Sprache bekommst!

    Ich will auch gar nicht weiter auf alle einzelnen, ggf. noch offenen Punkte eingehen, nur soweit:

    1.) Nicht nur Methoden und Variablen/Attribute schreibt man klein, sondern auch Packages. Klassen hingegen groß. Das solltest du dir merken. Wenn du weiter in die Materie einsteigen möchtest, findest du hier eine deutsche Zusammenfassung zu den CodeConventions (mit Link zum englischen Original) : Java Blog Buch : 07.05 Code Conventions

    2.) Datenbanktreiber werden in Standalone Anwendungen meistens in der Klasse, die für den Datenbankzugriff zuständig ist, im static-Block (siehe Java Blog Buch : 04.03.09 Statische Initialisierer ) geladen. Bsp.:

    Code:
    public class MyDBConncetion {
    
      static {
        Class.forName("dbDriver");
      }
    
      // ...
    }
    
    3.) Externe JARs in den JRE-Installationsordner zu kopieren ist nicht gerade die feine englische Art.

    Zum Thema Classpath:

    Java Blog Buch : 07.03 Einbinden von externen Klassen – Classpath

    Zum Thema JAR:

    Java Blog Buch : 07.04.04 jar – Klassen zusammenfassen und ausführen

    In der Windows-Konsole sollten dir folgende Befehle nützen:

    Code:
    cd ..
    
    wechselt ein Verzeichnis zurück

    Code:
    cd dirName
    
    wechselt in das Verzeichnis "dirName"

    Code:
    c:
    
    wechselt auf das Laufwerk mit dem Buchstaben "C"

    Beispiel:

    Code:
    Z:\>c:
    
    C:\>cd dokumente und einstellungen
    
    C:\Dokumente und Einstellungen>cd ..
    
    C:\>
    

  9. #9
    Erfahrener Benutzer
    Registriert seit
    16.08.2008
    Ort
    Mecklenburg-Vorpommern
    Beiträge
    375

    Standard

    Wichtig: Du kannst ja eine class Datei ja nicht direkt ausführen sondern brauchst dazu den Java-Interpreter. Wenn du diesen aus dem Verzeichnis C:\AlfredsOrdner\Sekundentakt\Java\Programm aufrufst, dann ist das das aktuelle Verzeichnis.
    Okay, verstanden. Und ein Verzeichnis höher kann ich nicht wechseln? Also ich könnte nicht einfach ein Package aus AlfredsOrdner laden?
    Ich hatte nämlich mit Eclipse häufig das Problem, dass ich in Projekt A ein Package oder eine Klasse hatte, die ich auch für Projekt B hätte gebrauchen können. Wie The_S hier schon richtig bemerkt, bin ich in den Basics noch nicht so sicher.

    1.) Nicht nur Methoden und Variablen/Attribute schreibt man klein, sondern auch Packages. Klassen hingegen groß.
    Wie gesagt, die obige Klasse stammt nicht von mir und ich wollte sie hinsichtlich der Dokumentierbarkeit der Funktionen überarbeiten.
    Jetzt wo du's sagst, fällt mir aber auch auf, dass die Methodennamen nicht wirklich standardkonform sind und so langsam beginne ich mich zu fragen, ob der Autor nicht auch noch gravierendere Fehler eingebaut hat.

    3.) Externe JARs in den JRE-Installationsordner zu kopieren ist nicht gerade die feine englische Art.

    Zum Thema Classpath:

    Java Blog Buch : 07.03 Einbinden von externen Klassen – Classpath

    Zum Thema JAR:

    Java Blog Buch : 07.04.04 jar – Klassen zusammenfassen und ausführen
    Danke!

    In der Windows-Konsole sollten dir folgende Befehle nützen[...]
    Der Befehl
    Code:
    c:
    
    half leider nicht. Dafür aber
    Code:
    cd ..
    
    Ich werd' mir jetzt mal deine Artikel ansehen und mich noch einmal hier zu Wort melden, falls dann noch Unklarheiten bestehen sollten.

  10. #10
    fka Gottzilla Avatar von The_S
    Registriert seit
    02.02.2005
    Ort
    Würzburg
    Beiträge
    777

    Standard

    Zitat Zitat von Sekundentakt Beitrag anzeigen
    Ich hatte nämlich mit Eclipse häufig das Problem, dass ich in Projekt A ein Package oder eine Klasse hatte, die ich auch für Projekt B hätte gebrauchen können. Wie The_S hier schon richtig bemerkt, bin ich in den Basics noch nicht so sicher.
    Hier haben wir doch ein klassisches Beispiel für das Verständnis von Bibliotheken (anderer Thread von dir). Jedes Projekt ist prinzipiell einmal unabhängig von den anderen Projekten und entspricht einer logischen Gruppierung von Klassen und Funktionen. Dann gibt es ein Master-Projekt, das aus den vielen kleinen Projekten eine großes Programm "verknüpft". Diese Bibliotheken werden in Eclipse über den Build-Path eingebunden und später, wenn du dein Programm "deployest" (oder was auch immer du damit machst) als eigene JAR-Dateien in den Classpath deines Programms (welches üblicherweise auch als JAR vorliegt).

    Mir stellt sich jedoch hier die Frage, warum du es für notwendig hältst, dein Programm auf mehrere Projekte zu verteilen. Sowas ist idR nur bei größeren Projekten notwendig. Zu Übungszwecken :) ?

    Zitat Zitat von Sekundentakt Beitrag anzeigen
    Jetzt wo du's sagst, fällt mir aber auch auf, dass die Methodennamen nicht wirklich standardkonform sind und so langsam beginne ich mich zu fragen, ob der Autor nicht auch noch gravierendere Fehler eingebaut hat.
    Was liest du denn für ein Buch?

    Zitat Zitat von Sekundentakt Beitrag anzeigen
    In der Windows-Konsole sollten dir folgende Befehle nützen[...]
    Der Befehl
    Code:
    c:
    
    half leider nicht. Dafür aber
    Code:
    cd ..
    
    Code:
    c:
    
    wechselt die Festplatte. Wenn du also in der Konsole bspw. von Laufwerk C:\ auf Laufwerk D:\ wechseln willst, gibst du in der Konsole
    Code:
    d:
    
    ein. Wenn du wieder zurück auf C:\ wechseln möchtest, gibst du
    Code:
    c:
    
    ein.

  11. #11
    Erfahrener Benutzer
    Registriert seit
    16.08.2008
    Ort
    Mecklenburg-Vorpommern
    Beiträge
    375

    Standard

    Mir stellt sich jedoch hier die Frage, warum du es für notwendig hältst, dein Programm auf mehrere Projekte zu verteilen. Sowas ist idR nur bei größeren Projekten notwendig. Zu Übungszwecken ?
    Genau! :)
    Ich hab' gestern nämlich festgestellt, dass man gewisse Dinge nunmal häufiger brauchen wird. Einmal habe ich die hier gepostete MySQL-Klasse in ein eigenes Projekt mit eigenem Package getan (höchstwahrscheinlich verschulde ich das dem Halbwissen, dem ich gerade durch das Java-Blog-Buch Abhilfe schaffe) und dabei tat sich dann auch die Frage auf "Mensch, was mach ich eigentlich, wenn ich ein komplexes Projekt mit anderen bearbeite?".
    Die Alternative zum MySQL-Package einbinden wäre "Klasse kopieren" gewesen, was aber definitiv schlechter Stil wäre .

    Was liest du denn für ein Buch?
    "Handbuch der Java Programmierung - Standard Edition Version 6 inkl. eBook >>MasterClass Java EE 5<<" von Guido Krüger und Thomas Stark.
    Ich bin froh, dass das Buch so hochwertig ist, sonst hätte ich es schon mehrfach durch den Raum gefeuert. Es ist ein hervorragendes Nachschlagewerk, aber verglichen mit meinem PHP-Einsteigerbuch ist das hier wirklich sehr anspruchsvoll und an einigen Stellen nicht so selbsterklärend, wie der Autor scheinbar glaubt.

  12. #12
    fka Gottzilla Avatar von The_S
    Registriert seit
    02.02.2005
    Ort
    Würzburg
    Beiträge
    777

    Standard

    Ich kanns einfach nicht lassen, sorry (diesmal aber Kritik auf hohem Niveau, also nichts, was ein Anfänger wissen müsste)

    Zitat Zitat von Sekundentakt Beitrag anzeigen
    Einmal habe ich die hier gepostete MySQL-Klasse in ein eigenes Projekt mit eigenem Package getan
    Deine MySQL-Klasse hat zwei gewaltige Sicherheitslücken, verbunden mit unschönem Code. Und zwar hier:

    Code:
    public ResultSet ReturnQuery(String query)
    
    und hier

    Code:
    public boolean RunQuery(String query)
    
    (mal davon abgesehen, dass die Kommentare irgendwie falsch sind und in der Hoffnung, dass die Klasse mittlerweile an die CodeConventions angepasst ist)

    Die Sicherheitsprobleme bestehen darin, dass du den Query direkt übergibst. An dieser Stelle kann ne ganze Menge unfug getrieben werden. Denn vermutlich wird der Query irgendwie so zusammen gebaut:

    Code:
    String query = "SELECT * FROM tbl_names WHERE name = " + nameVar;
    
    Und genau hier kann problemlos dein Programm durch SQL-Injection manipuliert/gestört werden. Man sollte eigentlich immer PreparedStatements verwenden und die Parameter über Platzhalter (?) setzen (Codebeispiele findest du genug im Netz). Außerdem sind PreparedStatements schneller und können wiederverwendet werden (was sie noch einmal schneller macht).

    Zweite Sache ist, dass du deine Exceptions abfängst, und/oder verschluckst. Wenn du diese Klasse jetzt aber in mehreren Projekten verwendest, haben diese Projekte, die die Klasse aufrufen, keine Chance, auf einen ggf. auftretenden Fehler zu reagieren, weil sie hiervon gar nichts mitbekommen.

  13. #13
    Erfahrener Benutzer
    Registriert seit
    16.08.2008
    Ort
    Mecklenburg-Vorpommern
    Beiträge
    375

    Standard

    Hallo,

    ich kam heute dazu die Klasse annähernd neu zu schreiben. Ich bin gerade dabei eine sichere Query-Methode zu erstellen.
    Dabei möchte ich einmal zwischen einer getResults() Methode unterscheiden, die die Ergebnisse eines Querys zurückgibt (entspricht der Funktionalität von ReturnQuery in der obrigen Klasse) und einer Absicherung des Querys unterscheiden.

    Ich führe eine Methode escape() ein, die Querystring-Argumente auf mögliche bösartige Codewörter hin untersucht.

    Dabei wird dann ein sicherer Querystring nach dem PHP-Vorbild mysql_real_escape_string() aufgebaut.
    Ein einfaches Beispiel:

    Code:
    String sql = "SELECT `name` FROM table WHERE `name` = \" " + mysql.escape(condition) + " AND " + mysql.escape(condition)
    
    Hierbei soll mysql das Datenbank-Objekt darstellen (also das Objekt, welches Datenbankverbindungen öffnet, Querys abschickt etc.).

    Was haltet ihr von dem Konzept?

    Ich möchte dabei mögliche Performanceeinbußen bei hartcodierten Querystrings umgehen, welche nicht auf böswillige Eingaben hin kontrolliert werden müssen.

    Ich kanns einfach nicht lassen, sorry (diesmal aber Kritik auf hohem Niveau, also nichts, was ein Anfänger wissen müsste)
    Nein, Du hast völlig Recht. Ich komme zwar aus dem PHP-Bereich, aber an den Schwachstellen von MySQL hat sich durch Java noch längst nichts geändert ;). Danke für den Hinweis.

    Morgen werde ich dann mal die erstellte Arbeit präsentieren. Jetzt wird erst einmal für die Schule gelernt .
    Geändert von Sekundentakt (01.12.2009 um 19:10 Uhr)

  14. #14
    Erfahrener Benutzer Avatar von $traight-$hoota
    Registriert seit
    15.09.2005
    Ort
    Königreich Flieden
    Beiträge
    724

    Standard

    Zitat Zitat von Sekundentakt Beitrag anzeigen
    Ich komme zwar aus dem PHP-Bereich, aber an den Schwachstellen von MySQL hat sich durch Java noch längst nichts geändert .
    Doch hat sich: Es gibt in Java standardmäßig andere Methoden, bei denen solche einfachen Fehler woe Codeeinschleusung nicht auftreten können. Das ganze nennt sich Prepared Statemens, worauf The_S ja schon hingewiesen hat.
    Weißt Bescheid - Scheiß wie weit

  15. #15
    fka Gottzilla Avatar von The_S
    Registriert seit
    02.02.2005
    Ort
    Würzburg
    Beiträge
    777

    Standard

    $traight-$hoota hat ja schon eigentlich alles gesagt, dennoch ein kleiner allgemeiner Hinweis:

    Zitat Zitat von $traight-$hoota Beitrag anzeigen
    ... wo Codeeinschleusung nicht auftreten können. Das ganze nennt sich Prepared Statemens ...
    Stimmt so nicht. Zumindest, wenn man den Satzteil "nicht auftreten können" hervorhebt. Bei hochkritischen Anwendungen sollte trotzdem eine Überprüfung der Parameter auf bösartigen Code stattfinden. Zwar sollte man davon ausgehen können, dass der DB-Treiber selbstständig alles escaped und reguliert, dem ist aber nicht immer so. Im (ich glaube das war) MSSQL JDBC-Treiber wurden PreparedStatements bis noch vor ein paar Jahren mit einfachen replace-String-Methoden und fast ohne Überprüfung zusammengebaut. Dort war also SQL-Injection trotzd PreparedStatements möglich.

    Das ist natürlich ein extremes Szenario und man kann davon ausgehen, dass bei einigermaßen verbreiteten und modernen Datenbanken solche Probleme nicht mehr auftreten. Aber wenn es sich wirklich um eine extrem kritische Anwendung handelt, sollte man zusätzlich zu PreparedStatements auch noch selbst die Parameter auf schädlichen Code überprüfen.

    Also nochmal: PreparedStatements sind schon sicher, aber einen 100%igen Schutz in jedem Fall bieten auch sie nicht (aber vermutlich immer noch einen deutlich höheren Schutz als eine eigene Überprüfung auf schädlichen Code).

    Ein weiterer Grund für PreparedStatements ist noch, dass du dich nicht darum kümmern musst, in welchem Format die Daten übergeben werden müssen. Bei einfachen Datentypen wie Decimal oder Integer ist das zwar noch kein Problem, bei Char und Varchar kann es jedoch auf Datenbankebene schon unterschiede geben (bspw. bzgl. des escapen von Sonderzeichen). Aber ein Datum oder gar einen Timestamp möchte ich eigentlich nie selbst korrekt für die DB formatieren müssen. Bei PreparedStatements kannst du diese Datentypen einfach setzen und der Treiber kümmert sich um die korrekte Darstellung. Beispiel:

    Code:
    // ps ist ein PreparedStatement
    ps.setInt(1, 234); // Integer setzen
    ps.setString(2, "Simon says: \"Hello World\""); // String mit Sonderzeichen setzen
    ps.setDate(3, new Date()); // Datum setzen
    

  16. #16
    Erfahrener Benutzer
    Registriert seit
    16.08.2008
    Ort
    Mecklenburg-Vorpommern
    Beiträge
    375

    Standard

    Hier einmal die Überarbeitung des Klassenquellcodes.


    Code:
    //Die Nutzung dieser MySQL-Klasse steht jedem nach belieben frei.
    //Der Einsatz geschieht auf eigenes Risiko. Es bestehen keine Garantie- oder Haftungsansprüche gegenüber dem Autoren. 
    //Es ist jedem gestattet, den vorliegenden Quellcode nach eigenem Ermessen zu modifizieren und
    //in fremden Programmteilen auf eigene Verantwortung wiederzuverwenden.
    
    
    import java.sql.*;
    
    
    public class MySQL 
    {	
    	public PreparedStatement psmt;
    	private String username = "root";
    	private String password = "password";
    	private String db		= "jokes";
    	private String URL 		= "jdbc:mysql://localhost:3306/";
    	private Connection connection;
    	private StringBuilder connectingParameters = new StringBuilder(50);
    	
    	
        static {
        			String Driver 	= "com.mysql.jdbc.Driver";
        			try 
        			{
        				Class.forName(Driver);
        			} 
        			catch (ClassNotFoundException e) 
        			{
        				e.printStackTrace();
        			}
        		}
    	/**
    	 * Der parameterlose Konstruktor nutzt die hartcodierten Werte für die Datenbank-
    	 * Anbindung.
    	 * @throws SQLException 
    	 */
    	
    	public MySQL() throws SQLException 
    	{
    		this.connect();
        }
    	
    	/**
    	 * Dieser Konstruktor ruft connect() mit den hier definierten Parametern auf.
    	 * Um eine andere Datenbank, als die Vorgegebene zu definieren, sollte der
    	 * Drei-Parameter-Konstruktor aufgerufen werden, welchem man als zusätzliches Argument
    	 * die Zieldatenbank übergeben kann.
    	 * @param String user
         * @param String pass
    	 * @throws SQLException 
    	 */
        public MySQL(String user, String pass) throws SQLException 
        {
            this.username = user;
            this.password = pass;
            this.connect();
        }
        
        /**
         * Diesem Konstruktor können der Username, das Passwort und die Zieldatenbank übergeben werden.
         * Er ruft die Funktion connect() auf, welche eine Verbindung zur Zieldatenbank herstellt.
         * @param String user
         * @param String pass
         * @param String db
         * @throws SQLException 
         */
        public MySQL(String user, String pass, String db) throws SQLException 
        {
            this.username = user;
            this.password = pass;
            this.db		  = db;
            this.connect();
        }
        
        /**
         * Erstellt eine Verbindung zum Datenbankserver.
         * Sollte ein Fehler auftreten, wird eine Exception ausgelöst.
         * @return void
         * @throws SQLException - sollte ein Fehler beim Verbinden mit der Datenbank auftreten.
         */
        public void connect() throws SQLException
        {
        	//aufbauen des Connecting-Parameter-Strings mit der StringBuilder-Methode append().
            connectingParameters.append(this.URL);
            connectingParameters.append(this.db);
    
            this.connection = DriverManager.getConnection(connectingParameters.toString(), 
            												this.username, this.password);
        }
        
        
        /**
         * Führt einen einfachen Query aus, um zu überprüfen, ob eine Datenbankverbindung noch besteht.
         * @return boolean true, falls eine Verbindung besteht, false, falls nicht
         * @throws SQLException
         */
        public boolean isConnected() throws SQLException 
        {
        	ResultSet rs = this.getResult("SELECT 1;");
            if (rs == null) 
            {
            	return false;
            }
            if (rs.next()) 
            {
            	return true;
            }
            else
            {
            	return false;
            }
        }
     
        /**
         * Setzt den Parameter pstmt.
         * @param String statement - muss ein String sein, welcher Platzhalter innerhalb des Querys für
         * variable Werte enthält.
         * @return void
         * @throws SQLException - falls ein Fehler mit der Datenbank auftritt.
         */
        public void setStatement(String statement) throws SQLException
        {
        	psmt = connection.prepareStatement(statement);
        }
        
        
        /**
         * Diese Funktion gibt das Ergebnis eines Querys zurück.
         * Sollte sich während der Ausführung ein Fehler ereignen, wird eine SQLException ausgelöst.
         * @param String query
         * @return boolean true, falls die Abfrage erfolgreich verlief, false, 
         * 	sollte ein Fehler aufgetreten sein
         * @throws SQLException
         */
        public ResultSet getResult(String query) throws SQLException
        {
           Statement stmt = this.connection.createStatement();
           ResultSet rs = stmt.executeQuery(query);
           return rs;
        }
        
        /**
         * Diese Funktion führt einen Query aus, ohne auf dessen Ergebnis zu warten.
         * @param String query
         * @return boolean - gibt true zurück, wenn der Query ausgeführt werden konnte und false, 
         * falls ein Fehler auftrat
         * @throws SQLException
         */
        public boolean runQuery (String query) throws SQLException
        {
            Statement stmt = this.connection.createStatement();
            return stmt.execute(query);
        }	
        
        /**
         * Der Aufruf von closeConnection() schließt die Datenbankverbindung mit Hilfe der
         * close()-Methode der Connection-Klasse aus java.sql.Connection. Anschließend wird
         * isClosed() aufgerufen.
         * @return boolean - gibt true zurück, falls die Verbindung geschlossen werden konnte.
         * @throws SQLException - sollte ein Datenbankfehler aufgetreten sein.
         */
        public boolean closeConnection() throws SQLException
        {
        	connection.close();
        	
        	if(connection.isClosed())
        	{
        		return true;
        	}
        	else
        	{
        		return false;
        	}
        }
    }
    
    An einigen Stellen sieht es hier falsch eingerückt aus. Für eine bessere Darstellung lässt sich der Code zum Glück in eine IDE einfügen.

    Ich bin offen für jede Kritik :).

    EDIT:
    Jetzt sind kleinere Fehler in der Dokumentation ausgebessert und die Lizenz etwas umfassender geworden, damit auch andere die Klasse nutzen können.
    Geändert von Sekundentakt (02.12.2009 um 23:22 Uhr)

  17. #17
    fka Gottzilla Avatar von The_S
    Registriert seit
    02.02.2005
    Ort
    Würzburg
    Beiträge
    777

    Standard

    Schaut soweit schon besser aus, trotzdem (wie immer ;) ) noch ein paar Anmerkungen:

    1.) Dir ist bewusst, dass man die "Standardverbindungsdaten" ohne große Probleme aus dieser Klasse auslesen kann? Sie sollte deshalb nur intern (auf deinem Rechner, vertrauenswürdige Abteilung, ...) oder auf einen Server ohne Zugriff von Außen eingesetzt werden

    2.) Variablennamen schreibt man noch immer klein (siehe bspw. "Driver")

    3.) Du bietest noch immer die Möglichkeit, nicht validierte Abfragen abzusenden

    4.) Anstatt das Attribute psmt public zu machen, solltest du es lieber nur lokal in der setPreparedStatement-Methode erzeugen und dort auch zurückliefern. Zum Einen ist deine momentane Implementierung schlechter Stil, und zum Anderen bekommst du ganz schnell Probleme, wenn deine Klasse von mehreren Threads verwendet wird, und min. zwei davon gleichzeitig oder kurz hintereinander auf das PreparedStatement zugreifen.

    5.) Exceptions werden jetzt zwar schön weitergeleitet, aber teilweise ist eine Weiterleitung wiederum nicht nötig. Bspw. sollte imho "isConnected" keine Exception werfen, sondern alle möglichen Exceptions selbst abfangen, und am Ende nur "true" oder "false" zurückliefern.

    6.) In closeConnection würde ich ganz am Anfang noch überprüfen, ob die Connection == null ist, und falls ja gar nichts machen (da werden dir unterschiedliche Leute vermutlich aber auch unterschiedliches sagen ;) ).

    7.) Statements, PreparedStatements und ResultSets müssen nach der Verwendung wieder mit close geschlossen werden (siehe bspw. deine isConnected-Methode). Dies geschieht typischerweise in einem Finally-Block.

    So, das war erst einmal das Gröbste :) .

  18. #18
    Erfahrener Benutzer
    Registriert seit
    16.08.2008
    Ort
    Mecklenburg-Vorpommern
    Beiträge
    375

    Standard

    1.) Dir ist bewusst, dass man die "Standardverbindungsdaten" ohne große Probleme aus dieser Klasse auslesen kann? Sie sollte deshalb nur intern (auf deinem Rechner, vertrauenswürdige Abteilung, ...) oder auf einen Server ohne Zugriff von Außen eingesetzt werden
    Richtig, da stimme ich Dir zu. Die Intention dieses Quellcodes ist es aber, Leuten - wie mir - die gerade in Java einsteigen und deren Tutorial/Referenz/whatever nicht wirklich gut erklärt, wie man eine DB-Verbindung aufbaut, etwas "sauberes" zur Verfügung zu stellen.
    Insofern spricht denke ich nichts dagegen, die Werte, so wie sie da stehen, auch beizubehalten.
    Wer sein root-Passwort "password" nennt, ist selber Schuld :).
    Oder ging es Dir um mehr, als den Benutzernamen und das Passwort?

    2.) Variablennamen schreibt man noch immer klein (siehe bspw. "Driver")
    Wird heute Nachmittag geupdatet.

    3.) Du bietest noch immer die Möglichkeit, nicht validierte Abfragen abzusenden
    Damit meinst Du solche, die nicht durch ein preparedStatement gingen?
    Falls ja: Es gibt auch hartcodierte SQL-Queries und ich möchte es dem User der Datenbankverbindung freistellen, ob er das preparedStatement verwendet, oder nicht. Ich selbst würde es jedem empfehlen.
    Oder spielst Du auf etwas anderes an?

    4.) Anstatt das Attribute psmt public zu machen, solltest du es lieber nur lokal in der setPreparedStatement-Methode erzeugen und dort auch zurückliefern. Zum Einen ist deine momentane Implementierung schlechter Stil, und zum Anderen bekommst du ganz schnell Probleme, wenn deine Klasse von mehreren Threads verwendet wird, und min. zwei davon gleichzeitig oder kurz hintereinander auf das PreparedStatement zugreifen.
    Guter Ansatz. Das lasse ich heute Nachmittag mit einfließen.

    6.) In closeConnection würde ich ganz am Anfang noch überprüfen, ob die Connection == null ist, und falls ja gar nichts machen (da werden dir unterschiedliche Leute vermutlich aber auch unterschiedliches sagen ).
    Du meinst damit meine Variable connection?

    7.) Statements, PreparedStatements und ResultSets müssen nach der Verwendung wieder mit close geschlossen werden (siehe bspw. deine isConnected-Methode). Dies geschieht typischerweise in einem Finally-Block.
    Fließt ebenso in's Update :).

    Danke!

  19. #19
    fka Gottzilla Avatar von The_S
    Registriert seit
    02.02.2005
    Ort
    Würzburg
    Beiträge
    777

    Standard

    Zitat Zitat von Sekundentakt Beitrag anzeigen
    Oder ging es Dir um mehr, als den Benutzernamen und das Passwort?
    Nee ... aber wenn es sowieso jeder "personalisieren" muss, sollte es imho auch keine vordefinierten Werte geben.

    Zitat Zitat von Sekundentakt Beitrag anzeigen
    Damit meinst Du solche, die nicht durch ein preparedStatement gingen?
    Falls ja: Es gibt auch hartcodierte SQL-Queries und ich möchte es dem User der Datenbankverbindung freistellen, ob er das preparedStatement verwendet, oder nicht. Ich selbst würde es jedem empfehlen.
    Oder spielst Du auf etwas anderes an?
    Hm ... schwierige Sache ;) . Natürlich kann man für fest eincodierte Strings ohne Parameter auch gefahrlos so etwas verwenden. Auf der anderen Seite läd so ne Klasse geradezu dazu ein, eben nicht auf PreparedStatements zurückzugreifen. Ist Ansichtssache, wenn ich ein Auge zudrück, würd ichs durchwinken ;) .

    Zitat Zitat von Sekundentakt Beitrag anzeigen
    Du meinst damit meine Variable connection?
    jap

    Und wo ist der Kommentar zu 5.) ;) ?

  20. #20
    Erfahrener Benutzer
    Registriert seit
    16.08.2008
    Ort
    Mecklenburg-Vorpommern
    Beiträge
    375

    Standard

    An dieser Stelle einmal der überarbeitete Code-Block.

    Code:
    //Die Nutzung dieser MySQL-Klasse steht jedem nach belieben frei.
    //Der Einsatz geschieht auf eigenes Risiko. Es bestehen keine Garantie- oder Haftungsansprüche gegenüber dem Autoren. 
    //Es ist jedem gestattet, den vorliegenden Quellcode nach eigenem Ermessen zu modifizieren und
    //in fremden Programmteilen auf eigene Verantwortung wiederzuverwenden.
    
    
    import java.sql.*;
    
    
    public class MySQL 
    {	
    	private String username = "root";
    	private String password = "password";
    	private String db		= "jokes";
    	private String URL 		= "jdbc:mysql://localhost:3306/";
    	private Connection connection;
    	private StringBuilder connectingParameters = new StringBuilder(40);
    	
    	
        static {
        			String driver 	= "com.mysql.jdbc.Driver";
        			try 
        			{
        				Class.forName(driver);
        			} 
        			catch (ClassNotFoundException e) 
        			{
        				e.printStackTrace();
        			}
        		}
    	/**
    	 * Der parameterlose Konstruktor nutzt die hartcodierten Werte für die Datenbank-
    	 * Anbindung.
    	 * ------------------------
    	 * ACHTUNG: Es wird empfohlen die hartcodierten Werte nur für Lern- bzw. Übungszwecke zu verwenden,
    	 * da sonst eine Sicherheitslücke entsteht. Es sollten NIEMALS die Zugangsdaten zu einer Datenbank
    	 * hartcodiert in die Parameterliste eingefügt werden!
    	 * In Produktivumgebungen sollte >IMMER< auf den Drei- bzw. Vier-Parameter-Konstruktor zurückgegriffen
    	 * werden!
    	 * ------------------------
    	 * @throws SQLException 
    	 */
    	
    	public MySQL() throws SQLException 
    	{
    		this.connect();
        }
    	
    	/**
    	 * Dieser Konstruktor ruft connect() mit den hier definierten Parametern auf.
    	 * Um eine andere Datenbank, als die Vorgegebene zu definieren, sollte der
    	 * Drei-Parameter-Konstruktor aufgerufen werden, welchem man als zusätzliches Argument
    	 * die Zieldatenbank übergeben kann.
    	 * @param String user
         * @param String pass
    	 * @throws SQLException 
    	 */
        public MySQL(String user, String pass) throws SQLException 
        {
            this.username = user;
            this.password = pass;
            this.connect();
        }
        
        /**
         * Diesem Konstruktor können der Username, das Passwort und die Zieldatenbank übergeben werden.
         * Er ruft die Funktion connect() auf, welche eine Verbindung zur Zieldatenbank herstellt.
         * @param String user
         * @param String pass
         * @param String db
         * @throws SQLException 
         */
        public MySQL(String user, String pass, String db) throws SQLException 
        {
            this.username = user;
            this.password = pass;
            this.db		  = db;
            this.connect();
        }
        
        /**
         * Diesem Konstruktor können der Username, das Passwort und die Zieldatenbank übergeben werden.
         * Er ruft die Funktion connect() auf, welche eine Verbindung zur Zieldatenbank herstellt.
         * Es kann für die Datenbankverbindung zusätzlich die Ziel-URL angegeben werden.
         * @param String user
         * @param String pass
         * @param String db
         * @param String url
         * @throws SQLException 
         */
        public MySQL(String user, String pass, String db, String url) throws SQLException 
        {
            this.username = user;
            this.password = pass;
            this.db		  = db;
            this.URL      = url;
            this.connect();
        }
        
        /**
         * Erstellt eine Verbindung zum Datenbankserver.
         * Sollte ein Fehler auftreten, wird eine Exception ausgelöst.
         * @return void
         * @throws SQLException - sollte ein Fehler beim Verbinden mit der Datenbank auftreten.
         */
        public void connect() throws SQLException
        {
        	//aufbauen des Connecting-Parameter-Strings mit der StringBuilder-Methode append().
            connectingParameters.append(this.URL);
            connectingParameters.append(this.db);
    
            this.connection = DriverManager.getConnection(connectingParameters.toString(), 
            												this.username, this.password);
        }
        
        
        /**
         * Führt einen einfachen Query aus, um zu überprüfen, ob eine Datenbankverbindung noch besteht.
         * @return boolean true, falls eine Verbindung besteht, false, falls nicht
         */
        public boolean isConnected()
        {
        	try
        	{
        		ResultSet rs = this.getResult("SELECT 1;");
        		if (rs == null) 
        		{
        			return false;
        		}
        		if (rs.next()) 
        		{
        			rs.close();
        			return true;
        		}
        		else
        		{
        			rs.close();
        			return false;
        		}
        	}
        	catch (Exception e)
        	{
        		return false;
        	}
        }
     
        /**
         * Setzt den Parameter pstmt.
         * ------------------------
         * ACHTUNG: Es wird empfohlen die Funktionalitäten der PreparedStatement-Klasse für das Absenden
         * von Queries zu verwenden, um möglicherweise bösartige Eingaben zu escapen!
         * ------------------------
         * @param String statement - muss ein String sein, welcher Platzhalter innerhalb des Querys für
         * variable Werte enthält.
         * @return void
         * @throws SQLException - falls ein Fehler mit der Datenbank auftritt.
         */
        public PreparedStatement setStatement(String statement) throws SQLException
        {
        	return connection.prepareStatement(statement);
        }
        
        
        /**
         * Diese Funktion gibt das Ergebnis eines Querys zurück.
         * Sollte sich während der Ausführung ein Fehler ereignen, wird eine SQLException ausgelöst.
         * ------------------------
         * ACHTUNG:
         * Es wird empfohlen diese Funktion NUR für hartcodierte Queries zu verwenden!
         * Abfragen, welche durch Usereingaben spezifiziert werden, sollten immer über
         * ein PreparedStatement.execute() abgeschickt werden.
         * Ein PreparedStatement lässt sich über die Funktion setStatement() erstellen.
         * Näheres dazu finden Sie in der SUN API-Dokumentation für Java SE6.
         * ------------------------
         * @param String query
         * @return boolean true, falls die Abfrage erfolgreich verlief, false, 
         * 	sollte ein Fehler aufgetreten sein
         * @throws SQLException
         */
        public ResultSet getResult(String query) throws SQLException
        {
           Statement stmt = this.connection.createStatement();
           ResultSet rs = stmt.executeQuery(query);
           return rs;
        }
        
        /**
         * Diese Funktion führt einen Query aus, ohne auf dessen Ergebnis zu warten.
         * @param String query
         * @return boolean - gibt true zurück, wenn der Query ausgeführt werden konnte und false, 
         * falls ein Fehler auftrat
         * @throws SQLException
         */
        public boolean runQuery (String query) throws SQLException
        {
            Statement stmt = this.connection.createStatement();
            return stmt.execute(query);
        }	
        
        /**
         * Der Aufruf von closeConnection() schließt die Datenbankverbindung mit Hilfe der
         * close()-Methode der Connection-Klasse aus java.sql.Connection. Anschließend wird
         * isClosed() aufgerufen.
         * @return boolean - gibt true zurück, falls die Verbindung geschlossen werden konnte.
         * @throws SQLException - sollte ein Datenbankfehler aufgetreten sein.
         */
        public boolean closeConnection() throws SQLException
        {
        	if (connection != null)
        	{
        		connection.close();
        	
        		if(connection.isClosed())
        		{
        			return true;
        		}
        		else
        		{
        			return false;
        		}
        	}
        	else
        	{
        		return true;
        	}
        }
    }
    
    Was bisher noch nicht geschah:
    7.) Statements, PreparedStatements und ResultSets müssen nach der Verwendung wieder mit close geschlossen werden (siehe bspw. deine isConnected-Methode). Dies geschieht typischerweise in einem Finally-Block.
    Wie soll ich das gestalten? Ich gebe diese Werte ja an den User zurück.
    Dort kann er ja über die klasseneigenen close-Methoden die Verbindung schließen. Wie soll ich das durch meine Klasse handeln?

    5.) Exceptions werden jetzt zwar schön weitergeleitet, aber teilweise ist eine Weiterleitung wiederum nicht nötig. Bspw. sollte imho "isConnected" keine Exception werfen, sondern alle möglichen Exceptions selbst abfangen, und am Ende nur "true" oder "false" zurückliefern.
    :) Ist ebenso mit eingeflossen. Ich bin mir aber nicht ganz sicher, ob es da nicht noch woanders was zu meckern gäbe. Schaust Du noch mal bitte drüber?

    6.) In closeConnection würde ich ganz am Anfang noch überprüfen, ob die Connection == null ist, und falls ja gar nichts machen (da werden dir unterschiedliche Leute vermutlich aber auch unterschiedliches sagen ).
    Ich habe es mit aufgenommen, wüsste aber gerne, was dagegen sprechen könnte, so zu verfahren, um mir eine eigene Meinung darüber bilden zu können. Meiner Ansicht nach wäre das nämlich nur dann der Fall, wenn gar keine Verbindung mehr existiert, sprich closeConnection() bereits aufgerufen wurde.

    Danke! :)
    Geändert von Sekundentakt (03.12.2009 um 20:30 Uhr)

+ Antworten
Seite 1 von 2 1 2 LetzteLetzte

Aktive Benutzer

Aktive Benutzer

Aktive Benutzer in diesem Thema: 1 (Registrierte Benutzer: 0, Gäste: 1)

     

Ähnliche Themen

  1. suche Programm: visueller Datenbank Designer
    Von Dev-Ad im Forum Datenbanken
    Antworten: 5
    Letzter Beitrag: 30.04.2008, 18:37
  2. Entwurfsproblem: Aus Widgets in Datenbank schreiben?
    Von Basti im Forum PHP-Programmierung
    Antworten: 3
    Letzter Beitrag: 29.03.2007, 13:13
  3. Problem bei Verarbeitung von Templates (Eigene Klassen)
    Von dago im Forum PHP-Programmierung
    Antworten: 21
    Letzter Beitrag: 31.08.2006, 16:02
  4. Datenbank und Sicherheit
    Von sparrow im Forum Datenbanken
    Antworten: 23
    Letzter Beitrag: 05.11.2005, 17:45
  5. Datenbank Verwaltung
    Von taskin73 im Forum Datenbanken
    Antworten: 7
    Letzter Beitrag: 30.07.2005, 17:07

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein

Impressum · Tutorials · Nutzungsbedingungen · thematisch sortierte Linklisten · Spendenaufruf · Team · Partnerprojekte

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48