# Informationen für Datenbankverbindung wo/wie speichern?



## Danloc (13. Jan 2018)

Hallo zusammen,

ich habe eine Oberfläche, in der ich alle Informationen für die Datenbankverbindung eintrage um mich mit der Datenbank zu verbinden.
Jetzt würde ich die Verbindungsinformationen gerne speichern, sodass beim nächsten Start des Programmes die Verbindungsinformationen automatisch abgerufen werden können.
Außerdem soll meine Anwendung nicht nur auf Windows rechnern laufen.

Folgende Ansätze und Ideen habe ich im Kopf durchgespielt:

- Speichern in der Datenbank (ohne Verbindungsinformationen keine Verbindung und keine Daten Henne-Ei Problem.)

- Speichern direkt in den Textfeldern (Wird mit Neustart des Programmes gelöscht?!)

- Speichern in einer Textdatei (Problem mit Pfaden, da Windows und Linux anders adressiert werden müssen?)

Die Informationen sollen automatisch von meinem Programm hinterlegt werden. Der User soll keinerlei Berührungspunkt zu dieser Datei haben, wie z.B. Pfadangabe treffen o.Ä.

Hat jemand einen Tipp wie man so etwas "üblicherweise" löst. Was ist good and bad practice?

Danke schonmal

Grüße Danloc


----------



## tommysenf (13. Jan 2018)

https://docs.oracle.com/javase/8/docs/technotes/guides/preferences/index.html


----------



## Danloc (13. Jan 2018)

genau so etwas habe ich gesucht! Mir fehlten bei meiner Suche scheinbar die richtigen Schlagwörter.
Jedenfalls funktioniert das Tadelos und bietet nebenbei noch viele features wie export/import usw. 

Dankeschön tommysenf, für die schnelle Antwort.


----------



## Danloc (13. Jan 2018)

So, falls es jemanden nach mir interessiert: 
Hier mal mein Lösungsansatz als kleines Beispiel, Ergänzend zum Post von tommysenf.

Über ein Button wird ein Eingabefenster geöffnet und die Inhalte der Textfelder gesetzt. Der default Wert ist "", somit werden nur Daten geladen, die auch gespeichert sind (sonst eben ein "Leerstring").


```
private void mniOptionenDatenbankverbindungActionPerformed(java.awt.event.ActionEvent evt) {                                                              
       
        Preferences Pref = Preferences.userNodeForPackage(ErstellenMainForm.class);
       
        String prfDatenbankIpAdresse = Pref.get("DatenbankIP", "");
        String prfDatenbankPort = Pref.get("DatenbankPort", "");
        String prfDatenbankName = Pref.get("DatenbankName", "");
        String prfDatenbankBenutzer = Pref.get("DatenbankBenutzer", "");
        String prfDatenbankPassworttext = Pref.get("DatenbankPasswort", "");
       
        tfdIpadresse.setText(prfDatenbankIpAdresse);
        tfdPort.setText(prfDatenbankPort);
        tfdDatenbankName.setText(prfDatenbankName);
        tfdBenutzername.setText(prfDatenbankBenutzer);
        pfdPasswort.setText(prfDatenbankPassworttext);
               
        OptionDatenbankVDialog.setLocationRelativeTo(this.CenterPanlelErstellen);
        OptionDatenbankVDialog.setVisible(true);
}
```

In diesem Sinne...

Vielen Dank und bis dahin 

Über einen Weiteren Button "Testen" innerhalb des zuvor geladenen Fensters, würden dann die Werte gespeichert, eine Datenbankverbindung mit diesen werten hergestellt und eine Ausgabe erzeugt. Alles innerhalb einer try-catch Anweisung - Somit wird immer nur die letzte funktionierende Verbindung gespeichert!


```
private void btnOptionsDatenbankvTestenActionPerformed(java.awt.event.ActionEvent evt) {                                                          
        try {
           
                Connection SQLVerbindung = null;
                Class.forName("com.mysql.jdbc.Driver");

                String strDatenbankIpAdresse = tfdIpadresse.getText();
                String strDatenbankPort = tfdPort.getText();
                String strDatenbankName = tfdDatenbankName.getText();
                String strDatenbankBenutzer = tfdBenutzername.getText();
                String strDatenbankPassworttext = String.valueOf(pfdPasswort.getPassword());
               


                if(!strDatenbankIpAdresse.isEmpty() && !strDatenbankPort.isEmpty() && !strDatenbankName.isEmpty()
                   && !strDatenbankBenutzer.isEmpty() && !strDatenbankPassworttext.isEmpty()){
                       
                        Preferences Pref = Preferences.userNodeForPackage(ErstellenMainForm.class);

                        //Preferences Speichern. Pref "DatenbankIP" = strDatenbankIpAdresse
                        Pref.put("DatenbankIP", strDatenbankIpAdresse);
                        Pref.put("DatenbankPort", strDatenbankPort);
                        Pref.put("DatenbankName", strDatenbankName);
                        Pref.put("DatenbankBenutzer", strDatenbankBenutzer);
                        Pref.put("DatenbankPasswort", strDatenbankPassworttext);  
                  }

              
                SQLVerbindung = DriverManager.getConnection("jdbc:mysql://" + strDatenbankIpAdresse + ":" + strDatenbankPort
                                                            + "/" + strDatenbankName, strDatenbankBenutzer, strDatenbankPassworttext);
               
                lblVerbindungsInfo.setText("Die Verbindung wurde Erfolgreich hergestellt!");
                txaVerbindungsOutput.append(SQLVerbindung.toString());
                InfoDatenbankVDialog.setLocationRelativeTo(this.OptionDatenbankVDialog);
                InfoDatenbankVDialog.setVisible(true);
           
          
          
        }catch (Exception exc) {
           
            lblVerbindungsInfo.setForeground(Color.red);
            lblVerbindungsInfo.setText("Die Verbindung konnte nicht hergestellt werden!");
            txaVerbindungsOutput.append(exc.toString());
            InfoDatenbankVDialog.setLocationRelativeTo(this.OptionDatenbankVDialog);
            InfoDatenbankVDialog.setVisible(true);
            exc.printStackTrace();
            }
```

In diesem Sinne, bis dahin.


----------



## JStein52 (13. Jan 2018)

Ich weiss nicht ob das in deinem Anwendungsfall ein Problem ist, aber du weisst schon dass du auf diese Art deine Anmeldedaten unverschlüsselt auf deinem Rechner speicherst


----------



## mrBrown (13. Jan 2018)

BTW:


Danloc hat gesagt.:


> Alles innerhalb einer try-catch Anweisung - Somit wird immer nur die letzte funktionierende Verbindung gespeichert!


Du speicherst vor dem Herstellen der Verbindung, es würden also auch falsche Anmelde-Daten gespeichert werden


----------



## Danloc (13. Jan 2018)

Ja, soweit ich es verstanden habe, werden die Preferences (String Variablen) in eine XML datei geschrieben oder?

Mein gedanke war also Folgender:
Ich nehme den Inhalt der Variable "strDatenbankPassworttext " und "verwüste" ihn mit einem Algorithmus. In meinem Anwendungsfalls würde es ausreichen, wenn das Passwort nicht im Klartext in der XML Datei Landet. Um also nicht allzugroßen Aufwand betreiben zu müssen, würde ich MD5 oder vielleicht SHA-1 nehmen.

Allerdings sehe ich das als eine andere Baustelle an. Sprich, ich würde eine Methode zum endcoden und eine decoden hinzufügen und diese jeweils vor dem Speichern bzw. nach dem auslesen der Variable verwenden. Soweit mein Plan, bin aber gerne für andere Anregungen offen .

Jetzt habe ich allerdings noch ein Bug in meinem Oben gesposteten Code endeckt, den ich mir noch nicht so 100% erklären kann, vielleicht kann jemand dazu noch einen Tipp geben (Ich versuche es mal zu erklären).

Wenn ich jetzt Das Fenster, in der die Informationen eintragen werden öffne, und ich von der Letzten Verbindung noch korrekte Informationen vorliegen haben, klappt die erneute Verbindung ohne Probleme.

Tippe ich dann falsche Informationen in meine Anmeldemaske ein, klappt die Verbindung Logischerweise nicht und die Fehlermeldung erscheint, wie erwartet.

Wenn ich dann, dass Fenster erneut öffne, stehen hier aber nicht die Informationen der letzten korrekten Verbindung, sondern die Falschen Informationen drin (also die, die ich zuletzt eingetragen habe. Ganz egal ob die Verbindung erfolgreich war oder nicht).
Auch nach erneuerter Eingabe kann ich keine Vernindung aufbauen, erst sobald ich das ganze Programm einmal neu gestartet habe.

Mir ist also nicht ganz klar, wie meine Try-Catch hier arbeitet. Es scheint so, als würde innerhlabe des Try
folgendes ausgeführt (Die Felder sind auch nicht leer, also die if Bedinung arbeitet wie erwartet)

```
if(!strDatenbankIpAdresse.isEmpty() && !strDatenbankPort.isEmpty() && !strDatenbankName.isEmpty()
                   && !strDatenbankBenutzer.isEmpty() && !strDatenbankPassworttext.isEmpty()){
                       
                        Preferences Pref = Preferences.userNodeForPackage(ErstellenMainForm.class);

                        //Preferences Speichern. Pref "DatenbankIP" = strDatenbankIpAdresse
                        Pref.put("DatenbankIP", strDatenbankIpAdresse);
                        Pref.put("DatenbankPort", strDatenbankPort);
                        Pref.put("DatenbankName", strDatenbankName);
                        Pref.put("DatenbankBenutzer", strDatenbankBenutzer);
                        Pref.put("DatenbankPasswort", strDatenbankPassworttext);  
                  }
```

obwohl folgende Verbindung nicht aufgebaut werden kann

```
SQLVerbindung = DriverManager.getConnection("jdbc:mysql://" + strDatenbankIpAdresse + ":" + strDatenbankPort
                                                            + "/" + strDatenbankName, strDatenbankBenutzer, strDatenbankPassworttext);
```


----------



## mrBrown (13. Jan 2018)

Danloc hat gesagt.:


> Ich nehme den Inhalt der Variable "strDatenbankPassworttext " und "verwüste" ihn mit einem Algorithmus. In meinem Anwendungsfalls würde es ausreichen, wenn das Passwort nicht im Klartext in der XML Datei Landet. Um also nicht allzugroßen Aufwand betreiben zu müssen, würde ich MD5 oder vielleicht SHA-1 nehmen.
> 
> Allerdings sehe ich das als eine andere Baustelle an. Sprich, ich würde eine Methode zum endcoden und eine decoden hinzufügen und diese jeweils vor dem Speichern bzw. nach dem auslesen der Variable verwenden. Soweit mein Plan, bin aber gerne für andere Anregungen offen .


MD5 und SHA-1 sind Hash-Funktionen, und nur in die eine Richtung praktikabel  "Entschlüsseln" kannst du die nicht...

Du solltest das Passwort entweder vor dem Speichern Verschlüsseln, oder das Passwort nicht speichern.




Danloc hat gesagt.:


> Mir ist also nicht ganz klar, wie meine Try-Catch hier arbeitet. Es scheint so, als würde innerhlabe des Try
> folgendes ausgeführt (Die Felder sind auch nicht leer, also die if Bedinung arbeitet wie erwartet)


Das arbeitet genau so, wie du es geschrieben hast 

Die Verbindung wird erst nach dem if ausgeführt - wenn die Verbindung fehl schlägt, ist der Code zum Speichern schon ausgeführt worden (so wie ich ja grad auch schon sagte).

Wenn nur gespeichert werden soll, wenn die Verbindung erfolgreich ist, solltest du auch erst dann speichern, wenn die Verbindung erfolgreich ist


----------



## Danloc (13. Jan 2018)

@mrBrown, ich habe das Speichern dahinter gesetzt, jetzt werden nurnoch korrekte Daten gespeichert.

Der Fehler den ich Oben beschrieben habe ist aber nach wie Vor vorhanden.

Mit Korrekten Informationen:
 

Mit Falschen Informationen:


```
com.mysql.jdbc.JDBC4Connection@1894b26ccom.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
```

Erneuter Versuch mit Richtigen Informationen:
 

```
com.mysql.jdbc.JDBC4Connection@b7fc982com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.com.mysql.jdbc.JDBC4Connection@25b7bf8ecom.mysql.jdbc.JDBC4Connection@3195f73f
```
 
Also Die Anzeige stimmt schonmal, es erscheint auch die Meldung, die Verbindung hätte funktioniert aber die SQL Meldung sagt etwas anderes ..

Jetzt bin ich etwas verwirrt


----------



## JStein52 (13. Jan 2018)

Danloc hat gesagt.:


> Ja, soweit ich es verstanden habe, werden die Preferences (String Variablen) in eine XML datei geschrieben oder?


Auf Windows-Systemen werden die in der Registry gespeichert


----------



## Danloc (13. Jan 2018)

So, ich habe mich mal in der registry umgesehen und meine Daten sind auch alle korrekt hinterlegt.
Obwohl ich mittels preferences nur auf den user Bereich zugreife, erhalte ich eine Fehlermeldung:

```
run:
Jan 13, 2018 2:47:22 PM java.util.prefs.WindowsPreferences <init>
WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs at root 0x80000002. Windows RegCreateKeyEx(...) returned error code 5.
BUILD SUCCESSFUL (total time: 5 seconds)
```

Bei stackoverflow habe ich gelesen, dass dies wohl ein Bug des JDK sei.


> Diving into the JDK source code you'll see that 0x80000002 means HKLM, i.e. the place in the Win Registry that shouldn't be touched. Your code never references the system tree and yet you still see this warning !?? (At this point you must be ripping all your hair out ... as I did)
> 
> Well, this is one of the rare occasions where there really is a JDK bug..


Link:https://stackoverflow.com/questions/5354838/java-java-util-preferences-failing

Nichts destro trotz, werden von meine Programm alle Daten richtig "ausgelesen" - Ich kann also auch an dieser Stelle keinen Fehler erkennen.

Mein oben beschriebenes Problem besteht also nach wie vor


----------



## Danloc (14. Jan 2018)

Morgen zusammen,

ich habe den Fehler entdeckt. Es ist schon fast peinlich. 

Das Fenster, auf welchem ich das SQL Result der Verbindung ausgebe, habe ich über einen Button geschlossen.
Das ausgelöste Event enthielt den Methoden Aufruf:

```
InfoDatenbankVDialog.dispose();
```
Ich setzte den Text des feldes nun zusätzlich auf Leerstring und siehe da, es funktioniert! Ich hatte also nie ein Problem mit der Verbindung, es wurde ledeglich die Meldung der vorherigen action mit ausgegeben. 

Naja das ist wohl eine dieser Erfahrungen, die man machen muss und welche man nie mehr vergisst.

Danek für eure Hilfe!

Gruß Danloc


----------

