# MySQL-Dumpdatei ohne Batchdatei wieder einlesen



## babuschka (27. Jan 2010)

Hallo!

Irgendwie überrascht mich, dass ich als geübter Suchmaschinenbenutzer nach einem ganzen Tag Recherche noch keine Lösung gefunden habe. Ich habe in Java mit Swing eine Client-Server-Anwendung entwickelt. Der Server ist ein entfernter MySQL-Server. Dass der Server nicht lokal auf demselben Rechner liegt, scheint beim Einlesen einer Dumpdatei scheinbar eine wichtige Rolle zu spielen.

Das Einlesen der Dumpdatei soll nicht über den Umweg einer Batch- (Windows) oder Shelldatei (Linux) erfolgen. Es muss doch auch direkt aus Java möglich sein.
Wichtiger Hinweis noch einmal: Der MySQL-Server, der die Daten aus der Dumpdatei bekommen soll, liegt woanders. Lösungen für lokale Server habe ich gefunden. Dabei wurde das Tool mysql mit den Parametern "-e" und "source" gerufen.

Das Schreiben einer Dumpdatei verwirkliche ich wie folgt und es funzt:

```
String[] cmd = { "mysqldump", "--default-character-set=utf8",
    "--host=" + "<hostname>", "--port=" + "<portnummer>",
    "--user=" + "<username>", "--password=" + "<password>",
    "--database", "<database>" };
Runtime shell = Runtime.getRuntime();
Process process;
Writer fileWriter = null;
try {
    fileWriter = new FileWriter( "<path_of_dumpfile>" );
    process = shell.exec( cmd );
    BufferedReader br = new BufferedReader( new InputStreamReader( process.getInputStream() ) );
    String line = "";

    while ( ( line = br.readLine() ) != null ) {
        fileWriter.write( line );
    }
    br.close();
}
catch ( IOException ex ) {
    new File( "<path_of_dumpfile>" ).delete();
}
finally {
    if ( fileWriter != null ) {
        try {
            fileWriter.close();
        }
        catch ( IOException ex ) {
            Exceptions.printStackTrace( ex );
        }
    }
}
```

Nun muss es umgekehrt doch auch möglich sein, die erstellte Dumpdatei wieder auf einem ähnlichen Wege einzulesen!? Ich wäre echt dankbar, wenn jemand einen weiterbringenden Hinweis nennen kann.

Viele Grüße
JAR


----------



## Firestorm87 (27. Jan 2010)

Achtung: Hier kommt fundiertes Halbwissen 

Soweit mir derzeit bekannt, lässt sich ein richter MySQL-Dump nur ausführen, wenn du direkten Zugriff auf die Datenbank hast.
Dann kannst du mittels des Programms mysqldump.exe die besagte Dump-Datei erzeugen und mittels mysql.exe das dumpFile wieder einspielen...

Jedoch erfordert dieses direkten Zugriff auf den Server und dessen mysql DB.
Weswegen dieses Verfahren auch insgesamt nicht dafür gedacht ist auf entfernten Rechnern Sicherungen zu erzeugen und wieder einzuspielen!

Alternativ kann man sich die gesamte Datenbank in ein SQL-File exportieren und diese dann auch wieder importieren, aber ein Dump ist hier meiner Meinung nach nicht das Mittel der Wahl....

/EDIT: Irre Ich mich eigentlich, oder enthält ein Dump-File eigentlich nicht nur eine große Anzahl von SQL-Statements, die als komplette Transaktion einfach so absetzen können müsste?


----------



## babuschka (27. Jan 2010)

Hallo!

Also das stimmt so nicht. Ich habe die MySQL-Client (Kommandozeilen-) Programme 
mysql und mysqldump auf meinem Rechner und kann prima per Shell Dumpdateien von 
einem entferneten Server auf meine Festplatte schreiben lassen und diese auch wieder einlesen.

Das sieht dann innerhalb von Shellskripten wie folgt aus:

Backup (Dump):

```
#!/bin/bash

TEMP_PATH=/home/<username>/temp
FILE_NAME=backup.sql

HOST='<hostname>'
USER='<username>'
PASSWD='<password>'

mysqldump --default-character-set=utf8 --host=${HOST} --port=3306 --user=${USER} --password=${PASSWD} --database afv --result-file="${TEMP_PATH}/${FILE_NAME}";
```

und Einlesen (Restore):

```
#!/bin/bash

TEMP_PATH=/home/<username>/temp
FILE_NAME=backup.sql

HOST='<hostname>'
USER='<username>'
PASSWD='<password>'

mysql --host=${HOST} --port=3306 --user=${USER} --password=${PASSWD} < "${TEMP_PATH}/${FILE_NAME}";
```


----------



## Firestorm87 (27. Jan 2010)

Ich habe zwar hin und wieder mit Datenbanken zutun, jedoch bisher nie von Seiten der Administration.

Außerdem setzen wir hier in der Firma eher auf DB2 als auf MySQL....
Aber... Ich habe mir gerade mal so ein Dump-File angesehen... das erzeugen scheint ja bei dir zu funktionieren.

Dadrin sind doch "nur" SQL-Befehle enthalten, welche man an der Datenbank absetzen müsste, um diese wieder herzustellen.

Und SQL-Befehle an den Server zu übertragen dürfte eigentlich nicht das Problem darstellen.

Jedoch ein einfachen "ReDump" auf einen entfernten Rechner ist eben nicht machbar, solange du nicht auf die lokale mysql.exe zugreifen kannst, wie du es eben hier tust:


JAR hat gesagt.:


> und Einlesen (Restore):
> 
> ```
> #!/bin/bash
> ...



/Nochmal EDIT:
Du greifst doch auch beim Dump auf eine lokale exe zu.
Dann tu das doch auch umgekehrt,...

Oder versteh Ich einfach dein Problem gerade nicht? Dachte du wolltest das alles lösen, ohne externe Programme zu verwenden ?

Ich bin verwirrt...


----------



## thom (27. Jan 2010)

Also wenn Du Tools für nen lokalen mySQL-Server gefunden hast, dann sollten die auch so mit einem entfernten Server funktionieren. Du mußt dann mysql nur noch den Parameter --host=HOSTNAME mitgeben. Aber ich frag mich, weshalb Du Java benutzt um dann irgendein anderes Programm zu starten. mysql und mysqldump  kannst Du doch direkt benutzen.

Btw.: Ich denke jedoch, dass Du mit Deinen Dumps beim Importieren Probleme bekommst, weil der Dump auch die primären und andere unique-Schlüssel und Felder enthält. Du muß also vor dem Import die Tabellen leeren --- oder steht das schon im Dumpfile mit drin?


----------



## babuschka (27. Jan 2010)

Firestorm87 hat gesagt.:


> /EDIT: Irre Ich mich eigentlich, oder enthält ein Dump-File eigentlich nicht nur eine große Anzahl von SQL-Statements, die als komplette Transaktion einfach so absetzen können müsste?



So ist es. Die Dumpdatei ist bei mir 3,5 MB groß. Ich weiß nicht, ob die Lösung ist, dass man sie in einen String einliest und die ganzen Statements in diesem String irgendwie an mysql mit dem Parammeter "-e" (steht soweit ich weiß für "Execute") weiter gibt.

Ich bitte um funktionierende Lösungen. Wenn wir hier rumraten wird das am Schluss ein ellenlanger möglicherweise auch uninteressanter/ unübersichtlicher Thread im Forum. Für Hilfe und Aufmerksamkeit bin ich natürlich sehr dankbar und wenn jemand meint, einen wirklich hilfreichen Tipp zu haben, dann freue ich - und sicher auch Andere - mich darüber!

Grüße
JAR


----------



## Firestorm87 (27. Jan 2010)

Dann sage Ich es dir in einem Satz:

*DUMP und REDUMP sind ohne externe tools nicht machbar!*

/EDIT: als einzelne sql-statements schon


----------



## babuschka (27. Jan 2010)

Hi!

Also die rasche Reaktion freut mich 



Firestorm87 hat gesagt.:


> Dann sage Ich es dir in einem Satz:
> *DUMP und REDUMP sind ohne externe tools nicht machbar!*
> /EDIT: als einzelne sql-statements schon


Das ist mir bekannt.

Ich erkläre es mal ausführlich:
Die Client-Swingapplikation hat lokal Zugriff auf die externen MySQL-Tools "mysql" und "mysqldump", welche auf dem Rechner installiert sind, auf dem die Applikation läuft.
Der MySQL-Server liegt woanders.
Die oben geposteten Shellskripte zeigen, wie ich Backup (Dump)/ Restore unabh. von meiner Javaapplikation per Shellkommando mache.

Das Backup klappt in Java prima, wobei ich per shell.exec() das lokale Tool mysqldump aufrufe. Siehe mein Codebeispiel. Dasselbe muss auch umgekehrt bei einem Wieder einlesen der Dumpdatei aus Java heraus (und unter Verwedung des externen Tools "mysql") gehen. Nur wie?


----------



## thom (27. Jan 2010)

Firestorm87 hat gesagt.:


> Achtung: Hier kommt fundiertes Halbwissen
> 
> Soweit mir derzeit bekannt, lässt sich ein richter MySQL-Dump nur ausführen, wenn du direkten Zugriff auf die Datenbank hast.
> Dann kannst du mittels des Programms mysqldump.exe die besagte Dump-Datei erzeugen und mittels mysql.exe das dumpFile wieder einspielen...
> ...



Nein, Du benötigts keinen direkten Zugang zum Server. Du benötigst lediglich ausreichende (mysql-) Datenbank- und Tabellenrechte um mysql und mysqldump mit 'nem entfernten Server arbeiten zu lassen.


----------



## Firestorm87 (27. Jan 2010)

Naja bei einer Client<->Server Anwendung rechne ich damit, dass eben kein direkter Zugang über Port 3306(Standart mysql port soweit mir bekannt) zur verfügung steht.
[EDIT]: Da diesen Part dann normal der Server übernimmt, dass "Server = DB (also NUR EINE DB)" gilt, ist mir so noch nie unter gekommen  denn dann ist das die DB und kein Server 
Es gilt normal immerhin: Keine businesslogik aufm clienten [/EDIT]

Wie möchtest du dann direkt den Server connecten?

Vll habe Ich den TE da auch einfach missverstanden und bitte vielmals um entschuldigung!

So nun haben wir alle das gleiche Level an ausganginfos 

Fangen wir doch mal an mit der Frage: "Welchen Fehler bekommst du denn, wenn du Java per exec genau das aufrufen lässt, was du ansonsten in der Shell aufruft also mysql --host myDBHost [...]"?


----------



## babuschka (27. Jan 2010)

Hi!

Also für Hilfe bin ich dankbar und deshalb bereite ich jetzt ein Ausprobierbeispiel mit Zugangsdaten zu meinem eigenen MySQL-Server vor und melde mich in den nächsten Minuten zurück. Muss das halt kurz vorbereiten...


----------



## thom (27. Jan 2010)

Ok, jetzt ist mir klar, wo Dein Problem liegt. Um einen Dump über mysql wieder in die DB einzulesen, mußt Du mysql entweder interaktiv benutzen oder alle SQL-Statements über stdin einlesen. Genau das macht das 

< "${TEMP_PATH}/${FILE_NAME}"

in Deinem Beispiel. 

So wie ich das sehe, kommst Du um java.sql nicht herum.


----------



## Firestorm87 (27. Jan 2010)

OK nun weiß Ich endlich wo die Problematik liegt:

```
Runtime shell = Runtime.getRuntime();
String server = "dbhost";
String file = "backup.sql";
String user = "user ";
String password = "password ";
String database = "database ";
String cmd = "mysqldump --default-character-set=utf8 --host=" + server + " --port=3306 --user="
+ user + " --password=" + password + " --database " + database + " --result-file=" + file;
shell.exec(cmd);
```
Dies funktioniert ohne Probleme, während:

```
Runtime shell2 = Runtime.getRuntime();
String cmd2 = "mysql --host=" + server + " --port=3306 --user=" + user + " --password="
+ database + " --database=" + database + " <" + file;
shell2.exec(cmd2);
```
zwar ohne fehler durchläuft, aber nichts in die DB schreibt, obwohl der identische befehl (cmd2) in der eingabeforderung das gewünschte Ergebnis liefert....

Merkwürdig,....

/EDIT: OK aus irgend einem grund hängt am Befehl der übermittelt wird kein parameter ?!?...
Auf jedenfall gibt mysql als antwort stets nur ne Übersicht über alle möglichen parameter, die man benutzen könnte....


----------



## babuschka (27. Jan 2010)

Okay, Sorry, hat etwas gedauert. Also ich sehe, dass ihr mein Problem nachvollziehen könnt.
Und nach einem Tag googlen weiß ich, dass viele die gleiche Frage stellen und Lösungen irgendwie immer
durch die Brust ins Auge über den Umweg einer Batch-/ Shelldatei vorgeschlagen werden bzw. nur bei einem
lokalen DB-Server funktionieren. Aber ich kann mir nicht vorstellen, dass mein gewünschter Weg ähnlich dem Dump in der Methode backup() des folgenden Codes nicht gehen soll...

So, hier mein Beispiel:

```
package tests;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Writer;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class BackupRestore {

    private static final String host = "jar.homelinux.org";

    private static final String port = "3306";

    private static final String user = "javaforum";

    private static final String password = "javaforum";

    private static final String database = "BackupRestoreDB";

    private static final String backupFilePath = System.getProperty( "java.io.tmpdir" ) + System.getProperty( "file.separator" ) + "backup.sql";
    //--------------------------------------------------------------------------

    private static void backup() {
        String[] cmd = { "mysqldump", "--default-character-set=utf8",
            "--host=" + host, "--port=" + port,
            "--user=" + user,
            "--password=" + password,
            "--database", database };
        Runtime shell = Runtime.getRuntime();
        Process process = null;
        Writer fileWriter = null;
        try {
            fileWriter = new FileWriter( backupFilePath );
            process = shell.exec( cmd );
            BufferedReader br = new BufferedReader( new InputStreamReader( process.getInputStream() ) );
            String line = "";

            System.out.println( "\n\nSchreibe Backup nach \"" + backupFilePath + "\"...");
            while ( ( line = br.readLine() ) != null ) {
                fileWriter.write( line );
            }
            br.close();
            System.out.println( "Backup nach \"" + backupFilePath + "\" geschrieben!");
        }
        catch ( IOException ex ) {
            System.err.println( ex.getLocalizedMessage() );

            if ( fileWriter != null ) {
                try {
                    fileWriter.close();
                }
                catch ( IOException e ) {
                    System.err.println( e.getLocalizedMessage() );
                }
            }
            new File( backupFilePath ).delete();
        }
        finally {
            if ( fileWriter != null ) {
                try {
                    fileWriter.close();
                }
                catch ( IOException ex ) {
                    System.err.println( ex.getLocalizedMessage() );
                }
            }
        }
    }
    //--------------------------------------------------------------------------

    private static void restore() {
        String[] cmd = { "mysql", "--default-character-set=utf8",
            "--host=" + host, "--port=" + port,
            "--user=" + user,
            "--password=" + password,
            "--database", database,
            "-e source " + backupFilePath };
        Runtime shell = Runtime.getRuntime();
        Process process = null;
        try {
            System.out.println( "\n\nVersuche Restore mit \"" + backupFilePath + "\"...");
            process = shell.exec( cmd );
            System.out.println( "Restoreversuch fertig!\n\n");
        }
        catch ( IOException ex ) {
            System.err.println( ex.getLocalizedMessage() );
        }

    }
    //--------------------------------------------------------------------------

    /**
     * schaut sich den Inhalt der Tabellenspalte model in der Tabelle kmm_air_aircrafts an und
     * switcht immer zwischen NULL oder "dummywert" hin und her, damit ein Restore überhaupt eine
     * sichtbare Änderung in der DB bewirken kann.
     */
    private static void changeData() {
        java.sql.Connection con = null;
        String connectionString = "jdbc:mysql://" + host + ":" + port + "/" + database + "?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8";
        try {
            con = DriverManager.getConnection( connectionString, user, password );

            PreparedStatement statement = con.prepareStatement( "SELECT model FROM kmm_air_aircrafts LIMIT 1" );
            ResultSet rs = statement.executeQuery();
            boolean modelIsNull = false;
            while ( rs.next() ) {
                String model = rs.getString( "model" );
                modelIsNull = model == null;
            }

            statement = con.prepareStatement( "UPDATE kmm_air_aircrafts SET model = ?" );
            statement.setString( 1, modelIsNull ? "dummywert" : "NULL" );
            statement.executeUpdate();

            con.close();
        }
        catch ( SQLException ex ) {
            System.err.println( ex.getLocalizedMessage() );
        }
    }
    //--------------------------------------------------------------------------

    public static void main( String[] args ) {
        BackupRestore.backup();
        BackupRestore.changeData();
        BackupRestore.restore();
    }
}
```

Ich denke es ist nachvollziehbar. Funktionieren kann es nur, wenn die Pfade zu den Kommandozeilentools mysql und mysqldump in der Umgebungsvariablen PATH bekannt gemacht wurden. Ich habe es unter Unix ausprobiert. Unter Windows müsste es eigentlich auch gehen.

Das Dumpen über die Methode backup() klappt wunderbar. Das Einlesen des Dumps in der Methode restore() funktioniert leider nicht. Jetzt bin ich gespannt, ob es jemanden gibt, der das hinbekommt 

Nachtrag: Mit folgendem Befehl könnt ihr euch per Linuxshell oder DOS-Kommando mit der DB auf meinem Server verbinden und die zwei Testtabellen durchforsten:

```
mysql --host=jar.homelinux.org --port=3306 --user="javaforum" --password="javaforum" --database="BackupRestoreDB"
```


----------



## babuschka (27. Jan 2010)

thom hat gesagt.:


> ...So wie ich das sehe, kommst Du um java.sql nicht herum.


Ich fänd das ganz im Gegenteil sogar prima, wenn Backup (Dump) und Restore komplett über java.sql ginge. Dann muss ich
nicht dafür sorgen, dass irgendwelche externen MySQL-Tools beim Anwender auf dem Rechner vorhanden sind.


----------



## mvitz (27. Jan 2010)

Nur eine Vermutung. Hast du das einspielen des Dumbs auch einmal unter Windows versucht?
Weil < ist ja ein Shell Zeichen, dass dafür sorgt, dass der Inhalt des Files dem Kommando hinzugefügt wird. Der Process unter Java kennt dieses jedoch nicht. Evtl. müsste man also irgendwie den Inhalt des Files dem mysql Befehl übergeben.

Ansonsten, wenn es richtig ist, dass der Dump eine Ansammlung von SQL Befehlen ist. Einfach das File einlesen und über ein oder mehrere Statements die SQL Befehle ausführen.


----------



## babuschka (27. Jan 2010)

Hi!

Dein erster Ansatz ist das, was ich mir wünsche und passt zu dem Hinweis von thom. So wie ich beim Dump den OutputStream vom process nutze, um die Dumpdatei zu schreiben kann ich mir vorstellen, dass es einen umgekehrten Weg gibt. Ein Weg, bei dem die Ansammlung an Statements der Dumpdatei eingelesen und dann an mysql weiter geleitet wird. Das es so oder so ähnlich gehen müsste, ist jetzt denke ich klar. Die Frage ist nur, wer weiß, wie es geht. Ich bekomme es leider nicht hin :-(


----------



## homer65 (27. Jan 2010)

Guck dir doch mal den Sourcecode von:
http://www.edv-ehm.de/mysqlbackuptool
an.


----------



## thom (28. Jan 2010)

Vorab mal 'ne persönliche Meinung: Externe Programme via exec() aufzurufen und dann stdin, stderr und stdout  irgendwie abzuhören oder zu füttern finde ich ausgesprochen unsauber. Aber wenn Du wirklich auf dieser Schiene bleiben möchtest, dann sieh Dir mal das zweite Codebeispiel auf Execute an external program - Real's Java How-to an. Für Dich würde das bedeuten, dass im mysql-Aufruf dann der Teil 
	
	
	
	





```
" <" + file;
```
 entfällt. Stattdessen musst Du das Dumpfile öffnen und dann Zeilenweise an stdin reichen. Also etwa 
	
	
	
	





```
for( int i =0; i<Zeilen_im_Dump; i++)
{
      String line = Zeile[i];
      stdin.write(line.getBytes() );
      stdin.flush();
}
```

Um dann das Tool mysql zu beenden mußt Du dann noch ein 'q' und '\n' über stdin senden.


----------



## thom (28. Jan 2010)

thom hat gesagt.:


> Um dann das Tool mysql zu beenden mußt Du dann noch ein 'q' und '\n' über stdin senden.


Pardon, Du mußt natürlich 'quit' und nicht 'q' schicken!


----------



## babuschka (28. Jan 2010)

Hi!

Danke für die Hinweise!

@thom:
Wie würde es denn sauber gehen? Hast du eine bessere Lösung, die funktioniert?


----------



## thom (28. Jan 2010)

Wie ich schon erwähnt habe über java.sql. Du baust halt eine Verdindung zum mySQL-Server auf, öffnest die Dumpdatei und sendest diese Zeile für Zeile an den mySQL-Server.


----------



## babuschka (28. Jan 2010)

Hi, danke!

Ehrlich gesagt weiß ich nicht, wie man da vorgeht. Ein kleines funktionierendes Beispiel wäre
klasse. Oben habe ich Beispielcode gepostet. Die Methode restore() bzw. die geschriebene Klasse enthält ein Grundgerüst. 

Würdest du lauffähigen Code da rein schustern? Ich vermute, dass es nur ein paar Zeien sind.

Viele Grüße
JAR


----------



## thom (29. Jan 2010)

Hier ein Beispiel. Um diese Klasse benutzen zu können mußt Du dafür sorgen, daß eine mysql-connector-java-x.x.x-bin.jar in Deinem Klassenpfad aufgenommen ist. 


```
package demo;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 *
 * @author thom
 */
public class MySQLDemo {
    private Connection theConnection;
    private Statement theStatement;
    private String theQuery = "INSERT INTO `einDatenbank`.`eineTabelle` (`id`, `date`, `time`, `owner_id`, `tn_list_id`, `title`, `abstract`, `ort`, `done`, `owner`) VALUES (NULL, '20100204', '1600', '0', '0', 'SQLDemo', 'Ich fühle mich ein bisschen verkohlt', 'http://www.java-forum.org/', 'true', 'JAR');";

    void simpleQuery()
    {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            theConnection = DriverManager.getConnection("jdbc:mysql://hostname/Address", "user", "passwort");
            theStatement = theConnection.createStatement();
            theStatement.executeUpdate(theQuery);
        } catch (SQLException ex) {
            Logger.getLogger(MySQLDemo.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(MySQLDemo.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
```


----------



## Hannes23 (2. Feb 2010)

Hallo zusammen,

mich würde interessieren, ob für das Problem mittlerweile eine Lösung gefunden wurde. Falls ja, wäre ich sehr interessiert daran.

Viele Grüße, Hannes


----------



## thom (3. Feb 2010)

Was meinst Du damit? Ich hab die, oder besser eine Lösung doch skizziert.


----------



## babuschka (3. Feb 2010)

Hallo!

Soeben habe ich mir den Code mal durchgelesen und frage mich, ob ich da
richtig gucke... 'Ich fühle mich ein bisschen verkohlt' etc. !?!?!

Tut mir leid, dass ich mich erst jetzt melde.
Ich liege seit ein paar Tagen richtig ausgeschaltet flach.

Bis dahin viele Grüße
JAR


----------



## Hannes23 (3. Feb 2010)

@thom:
Tut mir leid, ich habe versehentlich nur die erste Seite gelesen und mir ist deshalb Dein Beitrag nicht mehr aufgefallen - mein Fehler!

Abgesehen davon: Meine Frage war eigentlich, ob eine Lösung außerhalb von JDBC gekommen ist, wo Backup und Restore also direkt per mysqldump ausgeführt werden?

Eine Lösung über JDBC wäre mir ansonsten aber auch sehr recht, nur wie könnte man damit Backup und Restore durchführen? Wie man einzelne SQL-Statements ausführt (wie Du es am Beispiel mit INSERT gezeigt hast) ist mir klar, aber wie kann ich auf diesem Weg eine komplette Datenbank sichern?


----------



## ice-breaker (3. Feb 2010)

neija du liest alle Daten einer Tabelle aus und formatierst diese wieder als MySQL-Insert-Statement.
Und an alle Tabellen und Spalten der Tabellen kommst du über das INFORMATION_SCHEMA


----------



## thom (3. Feb 2010)

@JAR,

irgendwie finde ich es etwas seltsam, das jemand der _geübter Suchmaschinenbenutzer_ ist und sich an die Programmierung von Swing-Anwendungen wagt, nach 'nem einfachen Beispiel für java.sql fragt. Selbst hier im Forum gibt es doch 'ne Menge Posting zu genau diesem Thema.


----------



## babuschka (3. Feb 2010)

Hallo!

@thom
Ich bin absolut kein Freund von persönlichen Angriffen oder dergleichen.

Dass und ob ich eine Anwendung mit Java/ Swing auf die Beine gestellt habe und noch stelle,
ist alleinig meine Sorge.

Meine Problemstellung und Hilfesuche habe ich bereits sehr ausführlich dargelegt.
Dass es nicht um ein einfaches INSERT INTO eines Datensatzes geht, dürfte auch weniger geübten Lesern
aufgefallen sein und weshalb der Satz "Ich fühle mich ein bisschen verkohlt" in deinem INSERT INTO-Beispiel vorkommt, brauchst du niemandem näher zu erklären.

Wenn du weißt, wie man das vorliegende Problem löst und dieses Wissen nicht in Form eines Codebeispiels mit anderen oder mir teilen möchtest, kann ich daran nichts ändern.

Ich bin sicherlich kein Java-MySQL-oder-wie-auch-immer-Profi, entwickle aber nun seit gut 10 Jahren erfolgreich Anwendungen und dies auch schon länger unter Verwendung von MySQL. Google benutze ich wie wohl die meisten hier mit einem zufriedenstellenden Geschick. Dabei habe ich festgestellt, dass es bis jetzt keine funktionierende Antwort auf meine Frage gibt (oder ich noch nicht darauf gestoßen bin).
Da du scheinbar  eine Lösung kennst oder ergoogelt hat, kannst du dir überlegen, ob du uns/ mir da weiterhelfen magst.

Die Dumpdatei enthält die Fülle an Statements nicht schön säuberlich zeilenweise oder sonst wie geartet sortiert, als dass man diese Stück für Stück einzeln einlesen und jeweils als Befehl senden könnte. So sauber wie ein kompletter Dump rausgespielt werden kann (oben zeige ich das ja auf) suche ich nach einem Pendant, das Rausgeschriebene auf einem ähnlichen Wege wieder einzulesen. 

Dein Ansatz


thom hat gesagt.:


> Ok, jetzt ist mir klar, wo Dein Problem liegt. Um einen Dump über mysql wieder in die DB einzulesen, mußt Du mysql entweder interaktiv benutzen oder alle SQL-Statements über stdin einlesen. Genau das macht das
> 
> < "${TEMP_PATH}/${FILE_NAME}"
> 
> ...



klang nach Wissen und richtigem Weg.
Entweder ist mein Fieber zu hoch oder dein Codebeispiel hat mit deinem hier zitierten Weg, wie ich ihn im Zusammenhang mit meiner erläuterten Problemstellung interpretiert habe, wenig zu tun.

So long
JAR


----------



## thom (4. Feb 2010)

> Wenn du weißt, wie man das vorliegende Problem löst und dieses Wissen nicht in Form eines Codebeispiels mit anderen oder mir teilen möchtest, kann ich daran nichts ändern.



Was glaubst Du, weshalb ich Dich in 
http://www.java-forum.org/datenbank...atei-ohne-batchdatei-einlesen.html#post606382 auf Execute an external program - Real's Java How-to hingewiesen habe? Oder ist Dir das nicht Codebeispiel genug?



> Die Dumpdatei enthält die Fülle an Statements nicht schön säuberlich zeilenweise oder sonst wie geartet sortiert, als dass man diese Stück für Stück einzeln einlesen und jeweils als Befehl senden könnte. So sauber wie ein kompletter Dump rausgespielt werden kann (oben zeige ich das ja auf) suche ich nach einem Pendant, das Rausgeschriebene auf einem ähnlichen Wege wieder einzulesen.




Dann ist wohl der Dump schon nicht korrekt geschrieben worden. Mysqldump erzeugt jedenfalls 
Dateien, in denen die einzelnen Statements schön Zeile für Zeile gespeichert sind.



> Da du scheinbar eine Lösung kennst oder ergoogelt hat, kannst du dir überlegen, ob du uns/ mir da weiterhelfen magst.



Ich habe nie behauptet eine Lösung zu kennen sondern versucht Dir einen Weg aufzuzeigen, wie das Einlesen eines Dumps über java.sql durchgeführt werden kann. Und das bedeutet nun einmal, dass der Dump zeilenweise eingelesen wird.

Alternativ habe ich Dich auf eine URL hingewiesen. Hier wird demonstriert  wie externe Programme von Java aus angesprochen werden können. 

Damit solltest Du doch alle nötigen Werkzeuge für Deine Anwendung zusammen haben.


----------



## babuschka (4. Feb 2010)

thom hat gesagt.:


> Ich habe nie behauptet eine Lösung zu kennen sondern versucht Dir einen Weg aufzuzeigen, wie das Einlesen eines Dumps über java.sql durchgeführt werden kann. Und das bedeutet nun einmal, dass der Dump zeilenweise eingelesen wird.


Na dann ist das alles ja bloß ein Missverständnis gewesen und ich danke dir für deine viel versprechenden Hinweise. Möglicherweise kommt man so weiter, auch wenn es nicht die gesuchte Lösung ist.
Nach Erhalt privater Nachrichten weiß ich, dass andere ebenfalls noch "auf der Suche" sind.

Nebenbei:
Die Dumpdatei, wie ich sie in der Methode backup() meines Codebeispiels (siehe mein Post 27.01.2010, 18:25) mit mysqldump der Version 10.11 (Distribution 5.0.75) erzeuge, enthält alle Statements in einer einzigen Zeile. 

So long
JAR


----------



## thom (4. Feb 2010)

Wie schon erwähnt. Der Dump ist 'falsch'. Sieh Dir doch mal 'nen Dump an, den mysqldump erzeugt!


----------



## babuschka (4. Feb 2010)

Hi!

Der Dump wurde mit mysqldump erzeugt.

Und zwar in der Methode backup so hier:

```
private static void backup() {
        String[] cmd = { "mysqldump", "--default-character-set=utf8",
            "--host=" + host, "--port=" + port,
            "--user=" + user,
            "--password=" + password,
            "--database", database };
        Runtime shell = Runtime.getRuntime();
        Process process = null;
        Writer fileWriter = null;
        try {
            fileWriter = new FileWriter( backupFilePath );
            process = shell.exec( cmd );
            BufferedReader br = new BufferedReader( new InputStreamReader( process.getInputStream() ) );
            String line = "";
 
            System.out.println( "\n\nSchreibe Backup nach \"" + backupFilePath + "\"...");
            while ( ( line = br.readLine() ) != null ) {
                fileWriter.write( line );
            }
            br.close();
            System.out.println( "Backup nach \"" + backupFilePath + "\" geschrieben!");
        }
        catch ( IOException ex ) {
            System.err.println( ex.getLocalizedMessage() );
 
            if ( fileWriter != null ) {
                try {
                    fileWriter.close();
                }
                catch ( IOException e ) {
                    System.err.println( e.getLocalizedMessage() );
                }
            }
            new File( backupFilePath ).delete();
        }
        finally {
            if ( fileWriter != null ) {
                try {
                    fileWriter.close();
                }
                catch ( IOException ex ) {
                    System.err.println( ex.getLocalizedMessage() );
                }
            }
        }
    }
```


----------



## thom (4. Feb 2010)

Schon klar soweit. Es fehlen halt die Newlines zwischen den einzelnen Statements.


```
while ( ( line = br.readLine() ) != null ) {
                fileWriter.write( line+"\n" );
            }
```


----------



## Hannes23 (4. Feb 2010)

Tatsächlich, mit der kleinen Änderung "\n" funktioniert es bei mir tatsächlich! Ich konnte einen dump erstellen und sauber wieder restoren! Vielen Dank!


----------



## Hannes23 (9. Feb 2010)

Hallo zusammen,

nach der vielleicht etwas voreiligen Erfolgsmeldung im letzten Post würde ich dieses Thema gerne doch nochmal weiterführen 

Viele Dinge sprechen ja doch dafür, das ganze wirklich über JDBC zu machen und nicht per "externen Aufruf von mysqldump". Wie würden denn die Schritte dabei aussehen?

BACKUP:
1. DB-Verbindung aufbauen (wurde ja hier von thom schon skizziert)
2. Alle Tabellen durchlaufen und alle Datensätze auslesen und in Datei schreiben (oder in temporäre Tabelle?)
3. Verbindung beenden

Wie aber komme ich an die Tabellen- und Feldernamen? Und um eine SQL-Datei zu erzeugen, die ich später auch wieder einspielen kann, benötige ich ja auch die CREATE-Befehle? D. h. ich müsste auch die Tabellenstruktur auslesen?

Wie ihr seht, hab ich noch keine genaue Vorstellung davon, wie das mit JDBC laufen soll  Könnt ihr mir da vielleicht weiterhelfen?


----------



## thom (10. Feb 2010)

Hannes23 hat gesagt.:


> Hallo zusammen,
> 
> nach der vielleicht etwas voreiligen Erfolgsmeldung im letzten Post würde ich dieses Thema gerne doch nochmal weiterführen



Was genau war denn voreilig. Gibt es Fehlermeldungen, Exceptions? Werden die Daten fehlerhaft in die Datenbank eingelesen?




Hannes23 hat gesagt.:


> Viele Dinge sprechen ja doch dafür, das ganze wirklich über JDBC zu machen und nicht per "externen Aufruf von mysqldump". Wie würden denn die Schritte dabei aussehen?
> 
> BACKUP:
> 1. DB-Verbindung aufbauen (wurde ja hier von thom schon skizziert)
> ...



Den Ablauf hast Du schon richtig skizziert. Die Metadaten der Datenbank (Tabellenstruktur, Felddefinitionen etc.) kannst Du aus dem INFORMATION_SCHEMA auslesen (MySQL :: MySQL 5.1 Referenzhandbuch :: 22 Die Datenbank INFORMATION_SCHEMA).

Aber bevor Du Dich an diese mehr als abendfüllende Aufgabe machst, solltest Du einen Dump über mysqldump erstellen und über mysql wieder einlesen. Wenn das geht, dann mach Dich daran den Dump über JDBC wieder einzulesen. Wenn Du auch damit Erfolg hast, kannst Du den letzten Schritt wagen und den Dump über JDBC erzeugen.

Aber wie schon erwähnt: Das ist eine umfangreiche Aufgabe.


----------



## Hannes23 (15. Feb 2010)

Hallo thom,

das "voreilig" bezog sich nur darauf, dass ich dachte, das Thema wäre für mich erledigt, wenn ich "\n" bei der Lösung von JAR ergänze. Da mir aber die Lösung, mysqldump über Java aufzurufen Probleme bereitet, sobald ich nicht mehr lokal arbeite, tendiere icht jetzt eben doch zur JDBC-Lösung.

Ich habe es jetzt etwas vereinfacht, in dem ich auf dem Datenbankserver eine Kopie der kompletten Datenbank - ohne Datensätze - abgelegt habe. Bevor ich dann meine "datenbankkritischen Operationen" ausführe, durchlaufe ich alle Tabelle und alle Datensätze meiner Original-DB und schreibe sie in die Backup-DB. Nach Abarbeitung der "datenbankkritischen Operationen" lösche ich alle Datensätze aus der Original-DB und schreibe wieder die Datensätze aus der Backup-DB in meine Original-DB.

Soweit funktioniert das auch ganz gut, ich bekomme jetzt nur ein Problem mit den Fremdschlüsseln. Wenn ich die Datensätze aus der Backup-DB wieder in die Original-DB einlesen will (ich durchlaufe einfach die Tabellen/Datensätze und bastle mir einen String mit einem INSERT-Statement zusammen, das ich dann einzeln an die DB schicke), bekomme ich folgende Fehlermeldung:


```
Cannot add or update a child row: a foreign key constraint fails
```

Mein folgender Versuch:


```
PreparedStatement pstmt_foreignkey_check= connection.prepareStatement("SET FOREIGN_KEY_CHECKS=0");
```

hat leider auch nichts an dem Problem geändert.

Kann mir da jemand weiterhelfen?


----------



## thom (15. Feb 2010)

Hallo,


Hannes23 hat gesagt.:


> Hallo thom,
> 
> das "voreilig" bezog sich nur darauf, dass ich dachte, das Thema wäre für mich erledigt, wenn ich "\n" bei der Lösung von JAR ergänze. Da mir aber die Lösung, mysqldump über Java aufzurufen Probleme bereitet, sobald ich nicht mehr lokal arbeite, tendiere icht jetzt eben doch zur JDBC-Lösung.



das Problem mit JAR's-Lösung bei 'Nichtlokalen Datenbanken' liegt sicher nicht ans JAR's Lösung, sonder eher daran, das Du entweder nicht die richtigen Zugriffsrechte auf die Datenbanken/Tabellen auf dem Server hast, oder mySQL auf dem Server einen anderen Port verwendet oder mindesten eine Firewall den Zugriff blockiert.



> Ich habe es jetzt etwas vereinfacht, in dem ich auf dem Datenbankserver eine Kopie der kompletten Datenbank - ohne Datensätze - abgelegt habe. Bevor ich dann meine "datenbankkritischen Operationen" ausführe, durchlaufe ich alle Tabelle und alle Datensätze meiner Original-DB und schreibe sie in die Backup-DB. Nach Abarbeitung der "datenbankkritischen Operationen" lösche ich alle Datensätze aus der Original-DB und schreibe wieder die Datensätze aus der Backup-DB in meine Original-DB.
> 
> Soweit funktioniert das auch ganz gut, ich bekomme jetzt nur ein Problem mit den Fremdschlüsseln. Wenn ich die Datensätze aus der Backup-DB wieder in die Original-DB einlesen will (ich durchlaufe einfach die Tabellen/Datensätze und bastle mir einen String mit einem INSERT-Statement zusammen, das ich dann einzeln an die DB schicke), bekomme ich folgende Fehlermeldung:
> 
> ...



Wenn ich Dich also richtig verstehe, dann bist Du an einem Punkt an dem Du den Dump über eine selbst geschriebene Lösung erzeugst und dann ebenfalls über eine selbstgeschriebene Lösung wieder einlesen willst. 

Hast Du Dein Programm zum restaurieren der Datenbank mal mit einem Dump arbeiten lassen, der von mysqldump erzeugt worden ist?

Die Fehlermeldung verstehe ich so, daß Du versucht einen Datensatz mit einem von einer anderen Tabelle abhängigen Feld in die Datenbank zu schreiben, bevor der entprechende Wert in das zugehörige Feld  der andere Tabelle geschrieben wurde.


----------



## Hannes23 (15. Feb 2010)

thom hat gesagt.:


> Hallo,
> 
> Hast Du Dein Programm zum restaurieren der Datenbank mal mit einem Dump arbeiten lassen, der von mysqldump erzeugt worden ist?
> 
> Die Fehlermeldung verstehe ich so, daß Du versucht einen Datensatz mit einem von einer anderen Tabelle abhängigen Feld in die Datenbank zu schreiben, bevor der entprechende Wert in das zugehörige Feld  der andere Tabelle geschrieben wurde.




Nein, ich habe noch keinen Dump über mein Programm eingelesen, der von mysqldump erzeugt worden ist, weil mein Programm die Datensätze nicht aus einer *.sql - Datei einliest, sondern aus einer Kopie der Datenbank.

Ja, die Fehlermeldung verstehe ich auch so. Aber verwundern tut sie mich trotzdem. In einem Dumpfile ist es ja auch so, dass der Reihe nach von jeder Tabelle alle Datensätze eingelesen werden, auch wenn für einen Datensatz vielleicht der Fremdschlüsselwert noch garnicht eingelesen wurde...


----------



## thom (15. Feb 2010)

Ich hab mir mal 'nen Dump von mysqldump angesehen. Außer
dem 

```
SET FOREIGN_KEY_CHECKS=0
```

gibt es noch 


```
LOCK TABLES `tabellenname` WRITE;
/*!40000 ALTER TABLE `tabellenname` DISABLE KEYS */;

INSERT INTO `tabellenname` VALUES ( .... );

/*!40000 ALTER TABLE `tabellenname` ENABLE KEYS */;
UNLOCK TABLES;
```
.

Ohne das getestet zu haben und ohne Detail zu kennen: Vielleicht ist es das, was Dir fehlt.

Thom


----------



## Hannes23 (15. Feb 2010)

Hallo thom,

danke für die schnelle Antwort. Den gleichen Gedanken hatte ich auch schon  Aber damit hat es leider auch nicht geklappt...

Viele Grüße, Hannes


----------



## thom (16. Feb 2010)

Ich fürchte, daß Du Dich da etwas tiefer in die MySQL-Doku einlesen mußt.
Da ich vermute, daß Du es mit InnoDB-Tabbelen zu tun hast ist das richtige Dokument wohl:
MySQL :: MySQL 5.1 Referenzhandbuch :: 14.2.6.4 Fremdschlüssel-Beschränkungen

Thom


----------



## Hannes23 (16. Feb 2010)

Ok, dann werd ich da mal schaun. Vielen Dank für den Hinweis!!


----------



## Hannes23 (20. Feb 2010)

Hallo zusammen,

entschuldigt das "Hin und Her Springen" zwischen JDBC und mysqldump, aber da der Weg über JDBC bei mir nach wie vor ziemliche Probleme bei der Verarbeitung von Fremdschlüsseln bereitet, versuche ich parallel noch eine Lösung per mysqldump zu realisieren. Ich war im Posting #37 eigentlich der Meinung, das ganze würde bei mir mit der Lösung von JAR durch Hinzufügen von "\n" (Tip von thom) funktionieren. Leider ist das nun doch nicht der Fall: Ich kann ein mysqldump erstellen, den ich auch problemlos z.B. über phpMyAdmin importieren kann - klappt wunderbar. Wenn ich den dump jedoch über das Progarmm einlese, passiert das gleiche wie bei JAR: Nämlich garnichts. Es erscheint keine Fehlermeldung, aber es wird auch keine Datenbank angelegt. (Der CREATE DATABASE - Befehl ist aber natürlich im Dump und wie gesagt funktioniert es ja auch, den dump per phpMyAdmin einzulesen).

Hier nochmal der Code:


```
package Backup;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Writer;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
 
public class BackupRestore {
 
    private static final String host = "localhost";
    private static final String port = "3306";
    private static final String user = "root";
    private static final String password = "passwort";
    private static final String database = "datenbankname";
    private static final String backupFilePath="/home/user/dump.sql";
    

 
    private static void backupsql() {
        String[] cmd = { "mysqldump", "--default-character-set=utf8",
            "--host=" + host, "--port=" + port,
            "--user=" + user,
            "--password=" + password,
            "--database", database };
        Runtime shell = Runtime.getRuntime();
        Writer myWriter = null;
        Process process = null;
        try {
            myWriter = new FileWriter( backupFilePath );
            process = shell.exec( cmd );
            BufferedReader myBufferedReader = new BufferedReader( new InputStreamReader( process.getInputStream() ) );
            String line = "";
 
            System.out.println( "\n\nStarte Backup...");
            while ( ( line = myBufferedReader.readLine() ) != null ) {
                myWriter.write( line +"\n");
            }
            myBufferedReader.close();
            System.out.println( "Backup erfolgreich angelegt!");
        }
        catch ( IOException ex ) {
            System.err.println( ex.getLocalizedMessage() );
 
            if ( myWriter != null ) {
                try {
                    myWriter.close();
                }
                catch ( IOException e ) {
                    System.err.println( e.getLocalizedMessage() );
                }
            }
            new File( backupFilePath ).delete();
        }
        finally {
            if ( myWriter != null ) {
                try {
                    myWriter.close();
                }
                catch ( IOException ex ) {
                    System.err.println( ex.getLocalizedMessage() );
                }
            }
        }
    }

 
    private static void restoresql() {
        String[] cmd = { "mysql", "--default-character-set=utf8",
            "--host=" + host, "--port=" + port,
            "--user=" + user,
            "--password=" + password,
            "--database", database,
            "-e source " + backupFilePath };
        Runtime shell = Runtime.getRuntime();
        Process process = null;
        try {
            process = shell.exec( cmd );
            System.out.println( "Ursprünglicher Datenbankzustand wiederhergestellt.\n");
        }
        catch ( IOException ex ) {
            System.err.println( ex.getLocalizedMessage() );
        }
 
    }


    public static void main( String[] args ) {
        //BackupRestore.backupsql();
        BackupRestore.restoresql();
    }
}
```

Weiß jemand, woran das liegen könnte?

Vielen Dank und ein schönes Wochenende,

Hannes


----------



## Hannes23 (20. Feb 2010)

Ich hab jetzt zum Test auch mal 


```
process = shell.exec("mysql --default-character-set=utf8 --host=localhost --port=3306 --user=root --password=mypw --database=dbname < /home/user/db1.sql");
```



versucht, aber das klappt leider auch nicht. Exakt dieser Befehl funktioniert aber wenn ich ihn auf die Konsole kopiere...


----------



## thom (23. Feb 2010)

Moin,



Hannes23 hat gesagt.:


> Ich hab jetzt zum Test auch mal
> 
> 
> ```
> ...



das kann und wird so nicht funktionieren. Der Knackpunkt ist das unscheinbare '<' im mysql Aufruf. Ihr (JAR und Du) versteht das immer als Anweisung an das Programm mysql den SQL-Dump irgendwie einzulesen. Dem ist aber nicht so. Das '<' ist vielmehr eine Anweisung an die aufrufende Shell ( sh, ksh, csh, bash oder von mir aus auch cmd.exe ) nach dem Starten von mysql den Inhalt der Datei /home/user/db1.sql auf den Standardeingabe-Stream (stdin) zu legen. Ihr findet dazu sicherlich mehr Information wenn Ihr mal nach dem Stichwort 'Eingabeumlenkung' recherchiert. Im Klartext: Euer Aufruf startet das Programm mysql (alles ab < /ho....) wird ignoriert. Mysql verbindet sich mit der Datenbank und wartet auf Benutzereingaben und weil eben niemand den Inhalt des Dumps auf stdin legt (also keine Eingaben macht) wartet mysql bis zu St. Nimmmerleinstag. Über eine Shell gestartet sieht das etwas anders aus: Hier wird wieder mysql gestartet und eine Verbindung zur Datenbank aufgebaut. Mysql wartet wieder auf Benutzereingaben. Die Shell 'übernimmt jetzt die Rolle des Benuzters' und legt den Inhalt des Dumps zeilenweise auf stdin. Vereinfacht könnte man sagen, das die Shell die SQL-Befehle 'eintippt'. Wenn der Dump abgearbeitet wurde, legt die Shell noch ein CTRL-D auf stdin. Dies ist das Signal an mysql, daß jetzt keine weiteren Eingaben erfolgen werden, woraufhin sich das Programm mysql beendet.

Grüße

Thom


----------

