de.svws_nrw.db.utils.schema.DBBackupManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of svws-db-utils Show documentation
Show all versions of svws-db-utils Show documentation
Diese Bibliothek unterstützt bei dem Zugriff auf Datenbanken für die Schulverwaltungssoftware in NRW
package de.svws_nrw.db.utils.schema;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import de.svws_nrw.config.SVWSKonfiguration;
import de.svws_nrw.core.logger.LogLevel;
import de.svws_nrw.core.logger.Logger;
import de.svws_nrw.db.Benutzer;
import de.svws_nrw.db.DBConfig;
import de.svws_nrw.db.DBDriver;
import de.svws_nrw.db.DBEntityManager;
import de.svws_nrw.db.DBException;
import de.svws_nrw.db.dto.current.schema.DTOSchemaStatus;
import de.svws_nrw.db.schema.Schema;
import de.svws_nrw.db.schema.SchemaTabelle;
/**
* Diese Klasse stellt Methoden für den Export in eine SQLite-Datenbank und
* den Import aus einer SQLite-Datenbank zur Verfügung.
*/
public class DBBackupManager {
/** Der Schema-Manager, welcher für den Import bzw. Export verwendet wird */
private final DBSchemaManager schemaManager;
/** Ein Logger, um die Abläufe bei dem Update-Prozess zu loggen */
private final Logger logger;
/**
* Erzeugt einen neuen {@link DBBackupManager}.
*
* @param schemaManager der Schema-Manager, welcher verwendet wird
*/
DBBackupManager(final DBSchemaManager schemaManager) {
this.schemaManager = schemaManager;
this.logger = schemaManager.getLogger();
}
/**
* Diese Methode führt einen import dieser SQLite-Datenbank
* in die SVWS-Datenbank aus. Das Schema in der Zieldatenbank wird dabei neu angelegt
*
* @param tgtConfig die Datenbank-Konfiguration für den Zugriff auf die SVWS-Server-Datenbank
* @param tgtRootUser der Benutzername des Benutzers der mit den benötigten root-Rechten zur Schema-Verwaltung ausgestattet ist
* @param tgtRootPW das root-Kennwort für den Zugriff auf die Zieldatenbank
* @param maxUpdateRevision die Revision, bis zu welcher die Zieldatenbank aktualisiert wird
* @param devMode gibt an, ob auch Schema-Revision erlaubt werden, die nur für Entwickler zur Verfügung stehen
* @param logger ein Logger, welcher den Export loggt.
*
* @return true, falls der Import erfolgreich durchgeführt wurde
*/
public boolean importDB(final DBConfig tgtConfig, final String tgtRootUser, final String tgtRootPW, final long maxUpdateRevision, final boolean devMode,
final Logger logger) {
try (DBEntityManager conn = schemaManager.getUser().getEntityManager()) {
boolean success = true;
final long timeStart = System.currentTimeMillis();
logger.logLn("Exportiere aus der SQLite-Datenbank " + conn.getDBLocation());
logger.modifyIndent(2);
final String tgtSchema = tgtConfig.getDBSchema();
if ((tgtSchema == null) || "".equals(tgtSchema.trim())) {
logger.logLn("-> Import fehlgeschlagen! (Schemaname darf nicht null oder leer sein)");
logger.modifyIndent(-2);
return false;
}
if (!SVWSKonfiguration.get().lockSchema(tgtSchema)) {
logger.logLn("-> Import fehlgeschlagen! (Schema ist aktuell gesperrt und kann daher nicht überschrieben werden)");
logger.modifyIndent(-2);
return false;
}
try {
if (((tgtConfig.getDBDriver() == DBDriver.MARIA_DB) || (tgtConfig.getDBDriver() == DBDriver.MYSQL)) && ("root".equals(tgtConfig.getUsername())))
throw new DBException("Der Benutzer \"root\" ist kein zulässiger SVWS-Admin-Benutzer für MYSQL / MARIA_DB");
if ((tgtConfig.getDBDriver() == DBDriver.MSSQL) && ("sa".equals(tgtConfig.getUsername())))
throw new DBException("Der Benutzer \"sa\" ist kein zulässiger SVWS-Admin-Benutzer für MS SQL Server");
if (!DBRootManager.recreateDB(tgtConfig, tgtRootUser, tgtRootPW, logger))
throw new DBException("Fehler beim Anlegen des Schemas und des Admin-Benutzers");
importDBInternal(conn, tgtConfig, maxUpdateRevision, devMode, logger);
logger.logLn("-> Speicherbelegung (frei/verfügbar/gesamt): " + (Math.round(Runtime.getRuntime().freeMemory() / 10000000.0) / 100.0) + "G/"
+ (Math.round(Runtime.getRuntime().totalMemory() / 10000000.0) / 100.0) + "G/"
+ (Math.round(Runtime.getRuntime().maxMemory() / 10000000.0) / 100.0) + "G");
logger.logLn("-> Import erfolgreich in " + ((System.currentTimeMillis() - timeStart) / 1000.0) + " Sekunden abgeschlossen.");
} catch (final DBException e) {
logger.logLn("-> Import fehlgeschlagen! (" + e.getMessage() + ")");
success = false;
}
if (!SVWSKonfiguration.get().unlockSchema(tgtSchema)) {
logger.logLn("-> Fehler beim Freigeben des Datenbank-Schemas. Schema ist nicht gesperrt - dies wird an dieser Stelle nicht erwartet!");
success = false;
}
logger.modifyIndent(-2);
return success;
}
}
/**
* Diese Methode führt einen import dieser SQLite-Datenbank in die SVWS-Datenbank aus.
* Das Schema in der Zieldatenbank wird dabei komplett geleert und die Tabellen, etc. werden
* neu angelegt.
*
* @param tgtConfig die Datenbank-Konfiguration für den Zugriff auf die SVWS-Server-Datenbank
* @param maxUpdateRevision die Revision, bis zu welcher die Zieldatenbank aktualisiert wird
* @param devMode gibt an, ob auch Schema-Revision erlaubt werden, die nur für Entwickler zur Verfügung stehen
* @param logger ein Logger, welcher den Export loggt.
*
* @return true, falls der Import erfolgreich durchgeführt wurde
*/
public boolean importDBInto(final DBConfig tgtConfig, final long maxUpdateRevision, final boolean devMode, final Logger logger) {
try (DBEntityManager conn = schemaManager.getUser().getEntityManager()) {
boolean success = true;
final long timeStart = System.currentTimeMillis();
logger.logLn("Exportiere aus der SQLite-Datenbank " + conn.getDBLocation());
logger.modifyIndent(2);
final String tgtSchema = tgtConfig.getDBSchema();
if ((tgtSchema == null) || "".equals(tgtSchema.trim())) {
logger.logLn("-> Import fehlgeschlagen! (Schemaname darf nicht null oder leer sein)");
logger.modifyIndent(-2);
return false;
}
if (!SVWSKonfiguration.get().lockSchema(tgtSchema)) {
logger.logLn("-> Import fehlgeschlagen! (Schema ist aktuell gesperrt und kann daher nicht überschrieben werden)");
logger.modifyIndent(-2);
return false;
}
try {
final Benutzer tgtUser = Benutzer.create(tgtConfig);
final DBSchemaManager tgtManager = DBSchemaManager.create(tgtUser, true, logger);
if (!tgtManager.dropSVWSSchema())
throw new DBException("Fehler beim Leeren des Schemas der Ziel-Datenbank.");
importDBInternal(conn, tgtConfig, maxUpdateRevision, devMode, logger);
logger.logLn("-> Speicherbelegung (frei/verfügbar/gesamt): " + (Math.round(Runtime.getRuntime().freeMemory() / 10000000.0) / 100.0) + "G/"
+ (Math.round(Runtime.getRuntime().totalMemory() / 10000000.0) / 100.0) + "G/"
+ (Math.round(Runtime.getRuntime().maxMemory() / 10000000.0) / 100.0) + "G");
logger.logLn("-> Import erfolgreich in " + ((System.currentTimeMillis() - timeStart) / 1000.0) + " Sekunden abgeschlossen.");
} catch (final DBException e) {
logger.logLn("-> Import fehlgeschlagen! (" + e.getMessage() + ")");
success = false;
}
if (!SVWSKonfiguration.get().unlockSchema(tgtSchema)) {
logger.logLn("-> Fehler beim Freigeben des Datenbank-Schemas. Schema ist nicht gesperrt - dies wird an dieser Stelle nicht erwartet!");
success = false;
}
logger.modifyIndent(-2);
return success;
}
}
/**
* Diese Methode führt einen import dieser SQLite-Datenbank in die übergebene SVWS-Datenbank aus.
* Das Schema in der Zieldatenbank wird dabei neu angelegt und muss beim Aufruf dieser Methode leer sein!
* Das Zielschema sollte zudem bereits in der SVWS-Kofniguration gespert sein.
*
* @param conn die Verbindung zur Quell-Datenbank
* @param tgtConfig die Datenbank-Konfiguration für den Zugriff auf die SVWS-Server-Datenbank
* @param maxUpdateRevision die Revision, bis zu welcher die Zieldatenbank aktualisiert wird
* @param devMode gibt an, ob auch Schema-Revision erlaubt werden, die nur für Entwickler zur Verfügung stehen
* @param logger ein Logger, welcher den Export loggt.
*
* @throws DBException falls ein Fehler beim Import auftritt
*/
private void importDBInternal(final DBEntityManager conn, final DBConfig tgtConfig, final long maxUpdateRevision, final boolean devMode,
final Logger logger) throws DBException {
logger.logLn("-> Bestimme die Revision der QuellDatenbank...");
logger.modifyIndent(2);
final DTOSchemaStatus version = conn.querySingle(DTOSchemaStatus.class);
logger.logLn(" - Revision " + version.Revision);
logger.modifyIndent(-2);
logger.log("-> Verbinde zum Ziel-Schema...");
final Benutzer tgtUser = Benutzer.create(tgtConfig);
try (DBEntityManager tgtConn = tgtUser.getEntityManager()) {
if (tgtConn == null) {
logger.logLn(0, " [Fehler]");
logger.log(LogLevel.ERROR, "Fehler bei der Erstellung der Datenbank-Verbindung (driver='" + tgtConfig.getDBDriver() + "', schema='"
+ tgtConfig.getDBSchema() + "', location='" + tgtConfig.getDBLocation() + "', user='" + tgtConfig.getUsername() + "')"
+ System.lineSeparator());
throw new DBException("Fehler beim Verbinden zur Zieldatenbank");
}
final DBSchemaManager tgtManager = DBSchemaManager.create(tgtUser, true, logger);
if (tgtManager == null) {
logger.logLn(0, " [Fehler]");
throw new DBException("Fehler beim Verbinden zur Zieldatenbank");
}
logger.logLn(0, " [OK]");
logger.logLn(" Datenbank-Verbindung erfolgreich aufgebaut (driver='" + tgtConfig.getDBDriver() + "', schema='" + tgtConfig.getDBSchema()
+ "', location='" + tgtConfig.getDBLocation() + "', user='" + tgtConfig.getUsername() + "')");
tgtManager.createSVWSSchema(tgtUser, version.Revision, false, false);
boolean result = true;
logger.logLn("-> Kopiere die Daten aus der Quell-DB in die Ziel-DB...");
logger.modifyIndent(2);
expimpCopyFrom(tgtManager, version.Revision);
logger.modifyIndent(-2);
logger.logLn("[OK]");
logger.logLn("-> Erstelle die Trigger in der Ziel-DB bei der Revision " + version.Revision);
logger.modifyIndent(2);
String error = "";
try {
tgtConn.transactionBegin();
result = DBSchemaManager.createAllTrigger(tgtConn, logger, version.Revision, true);
if (result)
tgtConn.transactionCommit();
} catch (final Exception e) {
error = "Fehler bei der Transaktion: " + e.getMessage();
result = false;
} finally {
tgtConn.transactionRollback();
}
logger.modifyIndent(-2);
if (!result) {
logger.logLn(" [Fehler]");
logger.logLn(error);
throw new DBException("Fehler beim Erstellen der Trigger bei der Revision " + version.Revision);
}
logger.logLn("[OK]");
if (maxUpdateRevision != 0) {
logger.logLn("-> Aktualisiere die Ziel-DB ggf. auf die " + ((maxUpdateRevision < 0) ? "neueste " : "") + "DB-Revision"
+ ((maxUpdateRevision > 0) ? " " + maxUpdateRevision : "") + "...");
logger.modifyIndent(2);
result = tgtManager.updater.update(tgtUser, (maxUpdateRevision < 0) ? -1 : maxUpdateRevision, devMode, false);
logger.modifyIndent(-2);
if (!result) {
logger.logLn("[Fehler]");
throw new DBException("Fehler beim Aktualsieren der Ziel-DB");
}
logger.logLn("[OK]");
}
} catch (final Exception e) {
final DBException result = (e instanceof final DBException de) ? de : new DBException("Unbekannter Fehler aufgetreten.");
SVWSKonfiguration.get().deactivateSchema(SVWSKonfiguration.get().getSchemanameCaseConfig(tgtConfig.getDBSchema()));
throw result;
}
// Falls das Schema deaktiviert wurde, dann hebe die Deaktivierung wieder auf
SVWSKonfiguration.get().activateSchema(SVWSKonfiguration.get().getSchemanameCaseConfig(tgtConfig.getDBSchema()));
}
/**
* Diese Methode führt einen Export von der SVWS-Datenbank in die SQLite-Datenbank
* mit dem angegebenen Dateinamen aus.
*
* @param filename der Dateiname für die SQLite-Datenbank
* @param logger ein Logger, welcher den Export loggt.
*
* @return true, falls der Export erfolgreich durchgeführt wurde
*/
public boolean exportDB(final String filename, final Logger logger) {
try (DBEntityManager conn = schemaManager.getUser().getEntityManager()) {
final DBConfig tgtConfig = new DBConfig(DBDriver.SQLITE, filename, null, false, null, null, true, true, 0, 0);
boolean success = true;
final long timeStart = System.currentTimeMillis();
logger.logLn("Exportiere von in die SQLite-Datenbank " + filename);
logger.modifyIndent(2);
DBSchemaManager tgtManager = null;
try {
if (!DBRootManager.recreateDB(tgtConfig, null, null, logger))
throw new DBException("Fehler beim Anlegen des Schemas in der SQlite-Export-Datei");
logger.log("-> Verbinde zur SQLite-Export-Datenbank...");
final Benutzer tgtUser = Benutzer.create(tgtConfig);
try (DBEntityManager tgtConn = tgtUser.getEntityManager()) {
if (tgtConn == null) {
logger.logLn(0, " [Fehler]");
logger.log(LogLevel.ERROR, "Fehler bei der Erstellung der Datenbank-Verbindung (driver='" + tgtConfig.getDBDriver() + "', location='"
+ tgtConfig.getDBLocation() + "', user='" + tgtConfig.getUsername() + "')" + System.lineSeparator());
throw new DBException("Fehler beim Verbinden zur SQLite-Export-Datenbank");
}
tgtManager = DBSchemaManager.create(tgtUser, true, logger);
if (tgtManager == null) {
logger.logLn(0, " [Fehler]");
throw new DBException("Fehler beim Verbinden zur SQLite-Export-Datenbank");
}
logger.logLn(0, " [OK]");
logger.logLn(" Datenbank-Verbindung erfolgreich aufgebaut (driver='" + tgtConfig.getDBDriver() + "', location='"
+ tgtConfig.getDBLocation() + "', user='" + tgtConfig.getUsername() + "')");
logger.logLn("-> Bestimme die Revision der QuellDatenbank...");
logger.modifyIndent(2);
final DTOSchemaStatus version = conn.querySingle(DTOSchemaStatus.class);
logger.logLn(" - Revision " + version.Revision);
logger.modifyIndent(-2);
tgtManager.createSVWSSchema(tgtUser, version.Revision, false, false);
boolean result = true;
logger.logLn("-> Kopiere die Daten aus der Quell-DB in die Ziel-DB...");
logger.modifyIndent(2);
expimpCopyFrom(tgtManager, version.Revision);
logger.modifyIndent(-2);
logger.logLn("[OK]");
logger.logLn("-> Erstelle die Trigger in der Ziel-DB bei der Revision " + version.Revision);
logger.modifyIndent(2);
String error = "";
try {
tgtConn.transactionBegin();
result = DBSchemaManager.createAllTrigger(tgtConn, logger, version.Revision, true);
if (result)
tgtConn.transactionCommit();
} catch (final Exception e) {
error = "Fehler bei der Transaktion: " + e.getMessage();
result = false;
} finally {
tgtConn.transactionRollback();
}
logger.modifyIndent(-2);
if (!result) {
logger.logLn(" [Fehler]");
logger.logLn(error);
throw new DBException("Fehler beim Erstellen der Trigger bei der Revision " + version.Revision);
}
logger.logLn("[OK]");
logger.logLn("-> Speicherbelegung (frei/verfügbar/gesamt): " + (Math.round(Runtime.getRuntime().freeMemory() / 10000000.0) / 100.0) + "G/"
+ (Math.round(Runtime.getRuntime().totalMemory() / 10000000.0) / 100.0) + "G/"
+ (Math.round(Runtime.getRuntime().maxMemory() / 10000000.0) / 100.0) + "G");
logger.logLn("-> Export erfolgreich in " + ((System.currentTimeMillis() - timeStart) / 1000.0) + " Sekunden abgeschlossen.");
}
} catch (final DBException e) {
logger.logLn("-> Export fehlgeschlagen! (" + e.getMessage() + ")");
success = false;
}
logger.modifyIndent(-2);
return success;
}
}
/**
* Schreibt mithilfe des Schema-Managers für die Ziel-Datenbank die übergebenen Datensätze in die angegebene Tabelle.
*
* @param tgtManager der Schema-Manager für die Ziel-Datenbank
* @param entities die zu schreibenden Datensätze
* @param tab die Tabelle
* @param rev die Revision der Tabelle
*/
@SuppressWarnings("resource")
private void writeEntities(final DBSchemaManager tgtManager, final List