# Tabellen automatisch erstellen wenn sie nicht existieren



## FawKes100 (22. Jun 2019)

Hallo zusammen,
ich arbeite im Augenblick an einem kleinen Chatbot für Discord bzw. Twitch.
Dabei habe ich allerdings ein kleines Problemchen: Ich möchte die Datenbank die dazu erforderlich ist nicht manuell neu anlegen müssen, wenn ich z.B den Bot auf einen anderen Server ziehe.
Deswegen wollte ich dass der Bot am Anfang beim "Hochfahren" überprüft ob die Datenbank die gewünschte Tabelle enthält und wenn dies nicht der Fall ist, soll er eine neue Tabelle anlegen. Problem ist allerdings, dass er keine Tabelle anzulegen scheint, denn wenn ich mir die Datenbank über phpmyadmin anschaue, beinhaltet die Datenbank nach wie vor keine Tabelle. Führe ich den Befehl zum Einfügen der Tabelle direkt in phpmyadmin aus, funktioniert er und legt eine Tabelle an..
Zunächst frage ich also die Daten der Tabelle ab:


```
private static String[] getAllTablesOnDatabase(DatabaseConnector databaseConnector)
    {
        databaseConnector.executeStatement("SHOW TABLES");
        String[] tables;
        try
        {
            String[][] data = databaseConnector.getCurrentQueryResult().getData();
            tables = new String[data.length];
            for(int i=0; i<data.length; i++)
            {
                tables[i] = data[i][0];
            }
        }catch(NullPointerException e)
        {
            warn("No Tables exists in Database..");
            tables = new String[0];
        }
        return tables;
    }
```

Anschließend überprüfe ich ob die Tabelle vorhanden ist und wenn nicht erstelle ich sie neu:


```
private static void checkBotNutzer(DatabaseConnector databaseConnector, String[] tabels)
    {
        boolean isRegistrated = false;
        for(String string : tabels)
        {
            if(string.equalsIgnoreCase("botnutzer"))
            {
                isRegistrated = true;
            }
        }

        if(!isRegistrated)
        {
            info("Database does not exist correctly. Create Bot-Nutzer-Table..");
            databaseConnector.executeStatement("CREATE TABLE `phoeniixheart`.`botnutzer` ( `BotNutzerID` INT NOT NULL , `NutzerName` TEXT NOT NULL , `Pass` TEXT NOT NULL , `EMail` TEXT NOT NULL ) ENGINE = InnoDB;");
            String[] updatedTabels = getAllTablesOnDatabase(databaseConnector);
            for(String table : updatedTabels)
            {
                if(table.equalsIgnoreCase("botnutzer"))
                {
                    info("Table 'botnutzer' exist now correctly..");
                    return;
                }
            }
        }
    }
```

warn und info sind nur kleine DEBUG-Methoden, die eine schöne System.out bzw. System.err Ausgabe machen..
Hier noch den Inhalt der Klasse DatabaseConnector, die die Verbindung zur Datenbank aufbaut und Abfragen tätigt:

```
package database;

import java.sql.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DatabaseConnector{
    private Connection connection;
    private QueryResult currentQueryResult = null;
    private String message = null;

    private String ip;
    private int port;
    private String databaseName;
    private String userName;
    private String password;

 
    public DatabaseConnector(String pIP, int pPort, String pDatabase, String pUsername, String pPassword){
        try {
        

            Class.forName("com.mysql.jdbc.Driver");

          
            connection = DriverManager.getConnection("jdbc:mysql://"+pIP+":"+pPort+"/"+pDatabase + "?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=CEST", pUsername, pPassword);

        } catch (Exception e) {
            message = e.getMessage();
        }
        this.ip = pIP;
        this.port = pPort;
        this.databaseName = pDatabase;
        this.userName = pUsername;
        this.password = pPassword;
    }


    public void connect()
    {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        try {
            connection = DriverManager.getConnection("jdbc:mysql://" + ip + ":" + port + "/" + databaseName + "?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Europe/Berlin", userName, password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public boolean isConnected()
    {
        try {
            if(connection == null || !connection.isClosed()  )
            {
                return false;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return true;
    }

    public void disconnect()
    {
        if(connection != null)
        {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            connection = null;
        }
    }

    public void executeStatement(String pSQLStatement){
        debug("Querry: " + pSQLStatement);
        //Altes Ergebnis loeschen
        currentQueryResult = null;
        message = null;

        try {
            //Neues Statement erstellen
            Statement statement = connection.createStatement();

            //SQL Anweisung an die DB schicken.
            if (statement.execute(pSQLStatement)) { //Fall 1: Es gibt ein Ergebnis

                //Resultset auslesen
                ResultSet resultset = statement.getResultSet();

                //Spaltenanzahl ermitteln
                int columnCount = resultset.getMetaData().getColumnCount();

                //Spaltennamen und Spaltentypen in Felder uebertragen
                String[] resultColumnNames = new String[columnCount];
                String[] resultColumnTypes = new String[columnCount];
                for (int i = 0; i < columnCount; i++){
                    resultColumnNames[i] = resultset.getMetaData().getColumnLabel(i+1);
                    resultColumnTypes[i] = resultset.getMetaData().getColumnTypeName(i+1);
                }

                //Queue fuer die Zeilen der Ergebnistabelle erstellen
                Queue<String[]> rows = new Queue<String[]>();

                //Daten in Queue uebertragen und Zeilen zaehlen
                int rowCount = 0;
                while (resultset.next()){
                    String[] resultrow =  new String[columnCount];
                    for (int s = 0; s < columnCount; s++){
                        resultrow[s] = resultset.getString(s+1);
                    }
                    rows.enqueue(resultrow);
                    rowCount = rowCount + 1;
                }

                //Ergebnisfeld erstellen und Zeilen aus Queue uebertragen
                String[][] resultData = new String[rowCount][columnCount];
                int j = 0;
                while (!rows.isEmpty()){
                    resultData[j] = rows.front();
                    rows.dequeue();
                    j = j + 1;
                }

                //Statement schließen und Ergebnisobjekt erstellen
                statement.close();
                currentQueryResult =  new QueryResult(resultData, resultColumnNames, resultColumnTypes);

            } else { //Fall 2: Es gibt kein Ergebnis.
                //Statement ohne Ergebnisobjekt schliessen
                statement.close();
            }

        } catch (Exception e) {
            //Fehlermeldung speichern
            message = e.getMessage();
        }
    }

 
    public QueryResult getCurrentQueryResult(){
        return currentQueryResult;
    }

    public String getErrorMessage(){
        return message;
    }

    public void close(){
        try{
            connection.close();
        } catch (Exception e) {
            message = e.getMessage();
        }
    }

    private void debug(String out)
    {
        LocalDateTime localDateTime = LocalDateTime.now();
        String date = localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE);
        String time = localDateTime.format(DateTimeFormatter.ISO_LOCAL_TIME);
        System.out.println("[" + date + " " + time + "] [DEBUG] [Database-Connector] " +  out);
    }

}
```


----------



## mihe7 (22. Jun 2019)

Schau Dir mal Flyway oder Liquibase an.


----------



## M.L. (22. Jun 2019)

Das verwendete DBMS (wohl MySQL) kann auch Fuss abfragen, ob eine Tabelle (nicht) existiert: https://dev.mysql.com/doc/refman/5.5/en/create-table-select.html


----------



## Dompteur (23. Jun 2019)

Hast du dir die Exception in DatabaseConnector.executeStatement schon angesehen ?
Wird die geworfen ? Was steht in der Exception ?


----------



## FawKes100 (23. Jun 2019)

Dompteur hat gesagt.:


> Hast du dir die Exception in DatabaseConnector.executeStatement schon angesehen ?
> Wird die geworfen ? Was steht in der Exception ?


Exceptions werden im DatabaseConnector keine geworfen. Wenn ich mir die Errormessage ausgeben lasse, nach der Abfrage der Tabellen, erhalte ich ein "null". Passt auch zur NullPointer-Exeption, die geworfen wird, denn das 2-Dimensionale String Array scheint auf null zuverweisen.. Wenn ich versuche durch zu iterieren bekomme ich die NullPointer-Exception..
Problem Nr. 1 scheint also zu sein, dass der Befehl "SHOW TABLES" nicht vernünftig ausgeführt wird, bzw. fehlerhafte Daten übermittelt. Seltsamerweise funktioniert der Befehl in PhpMyAdmin problemlos..

Zum Datenbanksystem, welches ich verwende kann ich nicht viel sagen. Ich benutze aber XAMPP um es zustarten. Daher nehme ich mal an, dass es sich um MySQL handelt.


----------



## Dompteur (23. Jun 2019)

Ich meinte diese Stelle:

```
public class DatabaseConnector{
    private String message = null;
...
    public void executeStatement(String pSQLStatement){
        message = null;
...
        try {
...
        } catch (Exception e) {
            //Fehlermeldung speichern
            message = e.getMessage();
        }
    }
```

Wird diese Exception wirklich nicht ausgelöst ?
Ich würde zum Testen da ein "e.printStackTrace()" reinschreiben oder eine Ausgabe für das Logfile.

Ich würde dir generell empfehlen, in allen Catch-Blöcken eine Ausgabe ins Logfile zu machen. So wie dein Code aussieht, werden Fehlercodes gesetzt und später (möglicherweise) ignoriert. Du bekommst also den Fehler erst mit einer gewissen Verspätung mit.


----------



## FawKes100 (24. Jun 2019)

Jo hab das gerade mal getestet mit der Ausgabe des Stacktraces.. Wird tatsächlich schon im Database-Connector geworfen..
Die Klasse vom Database-Connector hatte ich noch von der Schule, hab da nicht viel dran geändert - warum auch immer die den Stacktrace da nicht mit ausgeben lassen..

Mache eigentlich immer in catchblöcken Ausgaben in die Konsole, nur da hab ich es übersehen 

Jedenfalls wird die NullpointerException hier geworfen:
`Statement statement = connection.createStatement();`
Schätze mal dass die connection auf null verweist. Folglich ist also keine Verbindung aufgebaut..

*Edit: Frage jetzt vor dem Ausführen des Statements ab, ob der DatabaseConnector auch wirklich ne Verbindung zur Datenbank hat. Ist letzteres nicht der Fall, bau ich eben schnell ne neue Verbindung auf, sodass sich das Problem nun geklärt haben sollte..

Vielen Dank aufjedenfall für die Hilfe und den Tipp mit der Ausgabe in den Catch-Blöcken!


----------

