All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.svws_nrw.data.datenaustausch.DataKurs42 Maven / Gradle / Ivy

Go to download

Diese Bibliothek unterstützt bei dem Zugriff auf Datenbanken für die Schulverwaltungssoftware in NRW

There is a newer version: 1.0.1
Show newest version
package de.svws_nrw.data.datenaustausch;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;

import de.svws_nrw.base.CsvReader;
import de.svws_nrw.base.LogUtils;
import de.svws_nrw.base.kurs42.Kurs42Import;
import de.svws_nrw.config.SVWSKonfiguration;
import de.svws_nrw.core.adt.Pair;
import de.svws_nrw.core.data.SimpleOperationResponse;
import de.svws_nrw.core.data.gost.GostBlockungKurs;
import de.svws_nrw.core.data.gost.GostBlockungKursLehrer;
import de.svws_nrw.core.data.gost.GostBlockungRegel;
import de.svws_nrw.core.data.gost.GostBlockungSchiene;
import de.svws_nrw.core.logger.LogConsumerList;
import de.svws_nrw.core.logger.Logger;
import de.svws_nrw.core.types.gost.GostKursart;
import de.svws_nrw.core.types.kursblockung.GostKursblockungRegelParameterTyp;
import de.svws_nrw.core.types.kursblockung.GostKursblockungRegelTyp;
import de.svws_nrw.core.types.schule.Schulform;
import de.svws_nrw.data.SimpleBinaryMultipartBody;
import de.svws_nrw.db.DBEntityManager;
import de.svws_nrw.db.dto.current.gost.DTOGostJahrgangsdaten;
import de.svws_nrw.db.dto.current.gost.kursblockung.DTOGostBlockung;
import de.svws_nrw.db.dto.current.gost.kursblockung.DTOGostBlockungKurs;
import de.svws_nrw.db.dto.current.gost.kursblockung.DTOGostBlockungKurslehrer;
import de.svws_nrw.db.dto.current.gost.kursblockung.DTOGostBlockungRegel;
import de.svws_nrw.db.dto.current.gost.kursblockung.DTOGostBlockungRegelParameter;
import de.svws_nrw.db.dto.current.gost.kursblockung.DTOGostBlockungSchiene;
import de.svws_nrw.db.dto.current.gost.kursblockung.DTOGostBlockungZwischenergebnis;
import de.svws_nrw.db.dto.current.gost.kursblockung.DTOGostBlockungZwischenergebnisKursSchiene;
import de.svws_nrw.db.dto.current.gost.kursblockung.DTOGostBlockungZwischenergebnisKursSchueler;
import de.svws_nrw.db.dto.current.schema.DTOSchemaAutoInkremente;
import de.svws_nrw.db.dto.current.schild.faecher.DTOFach;
import de.svws_nrw.db.dto.current.schild.katalog.DTOKatalogRaum;
import de.svws_nrw.db.dto.current.schild.lehrer.DTOLehrer;
import de.svws_nrw.db.dto.current.schild.schueler.DTOSchueler;
import de.svws_nrw.db.dto.current.schild.schule.DTOEigeneSchule;
import de.svws_nrw.db.schema.Schema;
import de.svws_nrw.db.schema.SchemaTabelle;
import de.svws_nrw.db.utils.ApiOperationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;

/**
 * Diese Klasse stellt Methoden für den Import und Export von Kurs 42 - Blockungen
 * zur Vefügung.
 */
public final class DataKurs42 {

	private DataKurs42() {
		throw new IllegalStateException("Instantiation of " + DataKurs42.class.getName() + " not allowed");
	}

	private static final Random _random = new Random();


	private static boolean removeTempFiles(final Path path) {
		try {
			final Path pBlockung = path.resolve("Blockung.txt");
			if (Files.exists(pBlockung))
				Files.delete(pBlockung);
			final Path pSchueler = path.resolve("Schueler.txt");
			if (Files.exists(pSchueler))
				Files.delete(pSchueler);
			final Path pFaecher = path.resolve("Faecher.txt");
			if (Files.exists(pFaecher))
				Files.delete(pFaecher);
			final Path pKurse = path.resolve("Kurse.txt");
			if (Files.exists(pKurse))
				Files.delete(pKurse);
			final Path pSchienen = path.resolve("Schienen.txt");
			if (Files.exists(pSchienen))
				Files.delete(pSchienen);
			final Path pBlockplan = path.resolve("Blockplan.txt");
			if (Files.exists(pBlockplan))
				Files.delete(pBlockplan);
			final Path pFachwahlen = path.resolve("Fachwahlen.txt");
			if (Files.exists(pFachwahlen))
				Files.delete(pFachwahlen);
			Files.delete(path);
		} catch (@SuppressWarnings("unused") final IOException e) {
			return false;
		}
		return true;
	}


	private static boolean unzipMultipartData(final Path path, final byte[] data) {
		try {
			Files.createDirectories(path);
			try (ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(data))) {
				ZipEntry zipFile;
				while ((zipFile = zipInput.getNextEntry()) != null) {
					final String filename = switch (zipFile.getName()) {
						case "Blockung.txt", "Schueler.txt", "Faecher.txt", "Kurse.txt", "Schienen.txt", "Blockplan.txt", "Fachwahlen.txt" -> zipFile.getName();
						default -> null;
					};
					if (filename != null) {
						final Path filePath = path.resolve(filename);
						Files.write(filePath, zipInput.readAllBytes(), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE);
					}
					zipInput.closeEntry();
				}
			}
		} catch (@SuppressWarnings("unused") final IOException e) {
			return false;
		}
		return true;
	}


	/**
	 * Importiert die in dem Multipart übergebene Datei.
	 *
	 * @param conn        die Datenbank-Verbindung
	 * @param multipart   der Multipart-Body mmit der Datei
	 *
	 * @return die HTTP-Response mit dem Log
	 */
	public static Response importZip(final DBEntityManager conn, final SimpleBinaryMultipartBody multipart) {
		final Logger logger = new Logger();
		logger.copyConsumer(Logger.global());
		final LogConsumerList log = new LogConsumerList();
		logger.addConsumer(log);

		boolean success = true;
		Status statusCode = Status.OK;
		try {
			// Erstelle temporär eine Zip-Datei aus dem übergebenen Byte-Array
			final String tmpDirectory = SVWSKonfiguration.get().getTempPath();
			final String tmpFilename = conn.getUser().connectionManager.getConfig().getDBSchema() + "_" + _random.ints(48, 123)  // from 0 to z
					.filter(i -> ((i <= 57) || (i >= 65)) && ((i <= 90) || (i >= 97)))  // filter some unicode characters
					.limit(40)
					.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
					.toString() + ".zip";
			logger.logLn("Erstelle eine temporäres Verzechnis mit dem Inhalt der Zip-Datei unter dem Namen \"" + tmpDirectory + "/" + tmpFilename + "\"");
			final Path path = Paths.get(tmpDirectory).resolve(tmpFilename);
			if (!unzipMultipartData(path, multipart.data)) {
				logger.logLn(2, "Fehler beim Erstellen der temporären Zip-Datei unter dem Namen \"" + tmpDirectory + "/" + tmpFilename + "\"");
				throw new ApiOperationException(Status.CONFLICT, "Fehler beim Erstellen der temporären Zip-Datei unter dem Namen \"" + tmpDirectory + "/"
						+ tmpFilename + "\"");
			}

			logger.logLn("Importiere die Blockung mithilfe der extrahierten Daten:");

			try {
				importKurs42(conn, logger, path);
				logger.logLn("  Import beendet");
			} finally {
				// Entferne die temporär angelegten Dateien wieder...
				logger.logLn("Löschen des temporären Verzeichnis \"" + tmpDirectory + "/" + tmpFilename + "\".");
				if (removeTempFiles(path)) {
					logger.logLn(2, "[OK]");
				} else {
					success = false;
					logger.logLn(2, "[FEHLER]");
				}
			}
		} catch (final Exception e) {
			success = false;
			if (e instanceof final ApiOperationException aoe) {
				statusCode = aoe.getStatus();
			} else {
				logger.logLn("  [FEHLER] Unerwarteter Fehler: " + e.getLocalizedMessage());
				statusCode = Status.INTERNAL_SERVER_ERROR;
			}
		}
		final SimpleOperationResponse daten = new SimpleOperationResponse();
		daten.success = success;
		daten.log = log.getStrings();
		return Response.status(statusCode).type(MediaType.APPLICATION_JSON).entity(daten).build();
	}


	/** Funktion zum Bestimmen der nächsten freien ID bei einer Tabelle mit Autoinkrement */
	private static final BiFunction getNextID = (final DBEntityManager conn, final SchemaTabelle tab) -> {
		final DTOSchemaAutoInkremente dbID = conn.queryByKey(DTOSchemaAutoInkremente.class, tab.name());
		return (dbID == null) ? 1 : (dbID.MaxID + 1);
	};


	/**
	 * Importiert eine Kurs-42-Blockung in das Datenbank-Schema, welches durch die übergebene
	 * Verbindung festgelegt ist.
	 *
	 * @param conn     die Datenbank-Verbindung.
	 * @param logger   der Logger für Rückmeldungen zum Import-Prozess
	 * @param path     der Pfad, wo sich die Kurs-42-Import-Dateien befinden
	 *
	 * @return true im Erfolgsfall und false im Fehlerfall
	 *
	 * @throws IOException   Falls der Zugriff auf die Kurs42-Dateien fehlschlägt
	 *
	 * @throws ApiOperationException   im Fehlerfall
	 */
	public static boolean importKurs42(final DBEntityManager conn, final Logger logger, final Path path) throws IOException, ApiOperationException {
		// Lese zunächst die Informationen zur Schule aus der SVWS-Datenbank und überprüfe die Schulform
		logger.logLn("-> Lese Informationen zu der Schule ein...");
		logger.modifyIndent(2);
		final DTOEigeneSchule schule = conn.querySingle(DTOEigeneSchule.class);
		if (schule == null) {
			logger.logLn("[Fehler] - Konnte die Informationen zur Schule nicht aus der Datenbank lesen");
			throw new ApiOperationException(Status.NOT_FOUND, "Konnte die Informationen zur Schule nicht aus der Datenbank lesen.");
		}
		final Schulform schulform = schule.Schulform;
		if ((schulform == null) || (schulform.daten == null) || (!schulform.daten.hatGymOb)) {
			logger.logLn("[Fehler] - Die Schulform hat keine gymnasiale Oberstufe oder konnte nicht bestimmt werden");
			throw new ApiOperationException(Status.CONFLICT, "Die Schulform hat keine gymnasiale Oberstufe oder konnte nicht bestimmt werden.");
		}
		logger.logLn("[OK]");
		logger.modifyIndent(-2);
		// Bestimme nun die IDs der Schüler, welche in der Schule vorhanden sind
		logger.logLn("-> Bestimme die Schüler der Schule aus der Datenbank...");
		logger.modifyIndent(2);
		final List listeSchueler = conn.queryAll(DTOSchueler.class);
		if ((listeSchueler == null) || (listeSchueler.isEmpty())) {
			logger.logLn("[Fehler] - Konnte die Liste der Schüler nicht einlesen");
			throw new ApiOperationException(Status.NOT_FOUND, "Konnte die Liste der Schüler nicht einlesen.");
		}
		final Set setSchueler = listeSchueler.stream().map(s -> s.ID).collect(Collectors.toSet());
		logger.logLn("[OK]");
		logger.modifyIndent(-2);
		// Bestimme nun die Lehrkräfte, die an der Schule tätig sind.
		logger.logLn("-> Bestimme die Lehrkräfte der Schule aus der Datenbank...");
		logger.modifyIndent(2);
		final List listeLehrer = conn.queryAll(DTOLehrer.class);
		if ((listeLehrer == null) || (listeLehrer.isEmpty())) {
			logger.logLn("[Fehler] - Konnte die Liste der Lehrer nicht einlesen");
			throw new ApiOperationException(Status.NOT_FOUND, "Konnte die Liste der Lehrer nicht einlesen.");
		}
		final Map mapLehrer = listeLehrer.stream().collect(Collectors.toMap(l -> l.Kuerzel, l -> l.ID));
		logger.logLn("[OK]");
		logger.modifyIndent(-2);
		// Bestimme die Fächer, die an der Schule vorhanden sind.
		logger.logLn("-> Bestimme die Fächer der Schule aus der Datenbank...");
		logger.modifyIndent(2);
		final List listeFaecher = conn.queryList(DTOFach.QUERY_BY_ISTOBERSTUFENFACH, DTOFach.class, true);
		if ((listeFaecher == null) || (listeFaecher.isEmpty())) {
			logger.logLn("[Fehler] - Konnte die Liste der Fächer der Oberstufe nicht einlesen");
			throw new ApiOperationException(Status.NOT_FOUND, "Konnte die Liste der Fächer der Oberstufe nicht einlesen.");
		}
		final Map mapFaecher = listeFaecher.stream().collect(Collectors.toMap(f -> f.ID, f -> f.Kuerzel));
		logger.logLn("[OK]");
		logger.modifyIndent(-2);
		// Lese nun die Daten von Kurs 42 aus dem angegeben Pfad ein
		logger.logLn("-> Lese die Kurs 42 - Export - Textdateien ein...");
		logger.modifyIndent(2);
		Kurs42Import k42 = null;
		try {
			k42 = new Kurs42Import(path, schule.SchulNr, mapLehrer, setSchueler, logger);
		} catch (final IOException e) {
			logger.logLn("[Fehler] - Fehler beim Einlesen des Kurs42-Text-Exports: ");
			final StringWriter sw = new StringWriter();
			e.printStackTrace(new PrintWriter(sw));
			logger.logLn(sw.toString());
			throw new ApiOperationException(Status.BAD_REQUEST, "Fehler beim Einlesen des Kurs42-Text-Exports.");
		}
		// Prüfe, ob der Abiturjahrgang zuvor angelegt wurde
		logger.logLn("-> Prüfe, ob der Abiturjahrgang %d angelegt ist...".formatted(k42.abiturjahrgang));
		logger.modifyIndent(2);
		final DTOGostJahrgangsdaten jahrgangsdaten = conn.queryByKey(DTOGostJahrgangsdaten.class, k42.abiturjahrgang);
		if (jahrgangsdaten == null) {
			logger.logLn("[Fehler] - Der Abiturjahrgang %d ist nicht angelegt.".formatted(k42.abiturjahrgang));
			throw new ApiOperationException(Status.NOT_FOUND, "Der Abiturjahrgang %d ist nicht angelegt.".formatted(k42.abiturjahrgang));
		}
		logger.logLn("[OK]");
		logger.modifyIndent(-2);
		try {
			logger.logLn("-> Schreibe die Blockungsdaten...");
			logger.modifyIndent(2);
			conn.transactionBegin();
			// Bestimme zunächst die IDs für die neue Blockung, die Schienen, die Kurse, die Regeln und das Ergebnis
			final Long blockungID = getNextID.apply(conn, Schema.tab_Gost_Blockung);
			final Long schienenID = getNextID.apply(conn, Schema.tab_Gost_Blockung_Schienen);
			final Long kursID = getNextID.apply(conn, Schema.tab_Gost_Blockung_Kurse);
			final Long regelID = getNextID.apply(conn, Schema.tab_Gost_Blockung_Regeln);
			final Long ergebnisID = getNextID.apply(conn, Schema.tab_Gost_Blockung_Zwischenergebnisse);
			// Lege die Blockung und ein zugehöriges Ergebnis an
			final DTOGostBlockung blockung = new DTOGostBlockung(blockungID, k42.name + " (import)", k42.abiturjahrgang, k42.halbjahr, false);
			final DTOGostBlockungZwischenergebnis erg = new DTOGostBlockungZwischenergebnis(ergebnisID, blockungID, false);
			conn.transactionPersist(blockung);
			conn.transactionPersist(erg);
			// Lege die Schienen an
			for (final GostBlockungSchiene schiene : k42.schienen) {
				conn.transactionPersist(new DTOGostBlockungSchiene(schienenID + schiene.id,
						blockungID, schiene.nummer, schiene.bezeichnung, schiene.wochenstunden));
			}
			// Lege die Kurse an
			for (final GostBlockungKurs kurs : k42.kurse) {
				// Prüfe, ob die Fach ID in Ordnung ist
				if (mapFaecher.get(kurs.fach_id) == null) {
					conn.transactionRollback();
					logger.logLn("[Fehler] - Fach-ID " + kurs.fach_id + " ist in der Datenbank nicht bekannt");
					throw new ApiOperationException(Status.NOT_FOUND, "Fach-ID " + kurs.fach_id + " ist in der Datenbank nicht bekannt");
				}
				conn.transactionPersist(new DTOGostBlockungKurs(kursID + kurs.id, blockungID, kurs.fach_id,
						GostKursart.fromID(kurs.kursart), kurs.nummer, kurs.istKoopKurs, kurs.anzahlSchienen, kurs.wochenstunden));
				for (final GostBlockungKursLehrer lehrer : kurs.lehrer)
					conn.transactionPersist(new DTOGostBlockungKurslehrer(kursID + kurs.id,
							lehrer.id, lehrer.reihenfolge, lehrer.wochenstunden));
			}
			// Lege die Regeln an
			for (final GostBlockungRegel regel : k42.regeln) {
				final GostKursblockungRegelTyp typ = GostKursblockungRegelTyp.fromTyp(regel.typ);
				conn.transactionPersist(new DTOGostBlockungRegel(regelID + regel.id, blockungID, typ));
				for (int i = 0; i < regel.parameter.size(); i++) {
					Long param = regel.parameter.get(i);
					final GostKursblockungRegelParameterTyp paramTyp = typ.getParamType(i);
					param = switch (paramTyp) {
						case KURSART -> param;
						case SCHIENEN_NR -> param;
						case KURS_ID -> kursID + param;
						case SCHUELER_ID -> param;
						default -> param;
					};
					conn.transactionPersist(new DTOGostBlockungRegelParameter(regelID + regel.id, i, param));
				}
			}
			// Schreibe die Kurs-Schienen-Zuordnungen
			for (final Pair zuordnung : k42.zuordnung_kurs_schiene.getNonNullValuesAsList())
				conn.transactionPersist(new DTOGostBlockungZwischenergebnisKursSchiene(ergebnisID,
						kursID + zuordnung.a, schienenID + zuordnung.b));
			// Schreibe die Kurs-Schüler-Zuordnungen
			for (final Pair zuordnung : k42.zuordnung_kurs_schueler.getNonNullValuesAsList())
				conn.transactionPersist(new DTOGostBlockungZwischenergebnisKursSchueler(ergebnisID,
						kursID + zuordnung.a, zuordnung.b));
			if (!conn.transactionCommit()) {
				logger.logLn("[Fehler] Unerwarteter Fehler beim Schreiben in die Datenbank.");
				logger.modifyIndent(-2);
				throw new ApiOperationException(Status.INTERNAL_SERVER_ERROR);
			}
			logger.logLn("[OK]");
			logger.modifyIndent(-2);
			return true;
		} catch (@SuppressWarnings("unused") final Exception exception) {
			return false;
		} finally {
			conn.transactionRollback();
		}
	}



	/**
	 * Importiert die in dem Multipart übergebene Datei.
	 *
	 * @param conn        die Datenbank-Verbindung
	 * @param multipart   der Multipart-Body mmit der Datei
	 *
	 * @return die HTTP-Response mit dem Log
	 */
	public static Response importRaeume(final DBEntityManager conn, final SimpleBinaryMultipartBody multipart) {
		final Logger logger = new Logger();
		logger.copyConsumer(Logger.global());
		final LogConsumerList log = new LogConsumerList();
		logger.addConsumer(log);

		boolean success = true;
		Status statusCode = Status.OK;
		try {
			final String csv = new String(multipart.data, StandardCharsets.UTF_8);
			logger.logLn("Importiere die Räume aus der CSV-Datei:");
			importRaeumeCSV(conn, logger, csv);
			logger.logLn("  Import beendet");
		} catch (final Exception e) {
			success = false;
			if (e instanceof final ApiOperationException aoe) {
				statusCode = aoe.getStatus();
			} else {
				logger.logLn("  [FEHLER] Unerwarteter Fehler: " + e.getLocalizedMessage());
				statusCode = Status.INTERNAL_SERVER_ERROR;
			}
		}
		final SimpleOperationResponse daten = new SimpleOperationResponse();
		daten.success = success;
		daten.log = log.getStrings();
		return Response.status(statusCode).type(MediaType.APPLICATION_JSON).entity(daten).build();
	}



	/**
	 * Importiert Räume aus Kurs-42 in das Datenbank-Schema, welches durch die übergebene
	 * Verbindung festgelegt ist.
	 *
	 * @param conn     die Datenbank-Verbindung.
	 * @param logger   der Logger für Rückmeldungen zum Import-Prozess
	 * @param csv      die CSV-Datei mit den Räumen
	 *
	 * @return true im Erfolgsfall und false im Fehlerfall
	 *
	 * @throws ApiOperationException   im Fehlerfall
	 */
	public static boolean importRaeumeCSV(final DBEntityManager conn, final Logger logger, final String csv) throws ApiOperationException {
		// Lese zunächst die Informationen zur Schule aus der SVWS-Datenbank und überprüfe die Schulform
		logger.logLn("-> Lese Informationen zu der Schule ein...");
		logger.modifyIndent(2);
		final DTOEigeneSchule schule = conn.querySingle(DTOEigeneSchule.class);
		if (schule == null) {
			logger.logLn("[Fehler] - Konnte die Informationen zur Schule nicht aus der Datenbank lesen");
			throw new ApiOperationException(Status.NOT_FOUND, "Konnte die Informationen zur Schule nicht aus der Datenbank lesen.");
		}
		logger.logLn("[OK]");
		logger.modifyIndent(-2);
		try {
			logger.logLn("-> Lese die Räume aus der CSV-Datei ein...");
			logger.modifyIndent(2);
			final List raeume = CsvReader.from(csv, Kurs42DataRaum.class);
			logger.logLn("[OK]");
			logger.modifyIndent(-2);

			conn.transactionBegin();

			logger.logLn("-> Lese die bereits im Katalog vorhandenen Räume ein...");
			logger.modifyIndent(2);
			final Map raeumeVorhanden = conn.queryAll(DTOKatalogRaum.class).stream().collect(Collectors.toMap(r -> r.Kuerzel, r -> r));
			logger.logLn("[OK]");
			logger.modifyIndent(-2);

			logger.logLn("-> Schreibe die Räume...");
			logger.modifyIndent(2);
			long id = conn.transactionGetNextID(DTOKatalogRaum.class);
			for (final Kurs42DataRaum raum : raeume) {
				if (raum.RaumKuerzel == null) {
					logger.logLn("[Fehler] - Konnte die Informationen zur Schule nicht aus der Datenbank lesen");
					logger.modifyIndent(-2);
					throw new ApiOperationException(Status.BAD_REQUEST, "Jeder Raum muss ein gültiges Kürzel haben.");
				}
				if (raeumeVorhanden.containsKey(raum.RaumKuerzel)) {
					logger.logLn("Raum '%s' wird nicht übernommen, da er bereits vorhanden ist.".formatted(raum.RaumKuerzel));
					continue;
				}
				final DTOKatalogRaum dto = new DTOKatalogRaum(id++, raum.RaumKuerzel, raum.RaumKuerzel, 40);
				conn.transactionPersist(dto);
				logger.logLn("Raum '%s' hinzugefügt.".formatted(raum.RaumKuerzel));
			}

			if (!conn.transactionCommit()) {
				logger.logLn("[Fehler] Unerwarteter Fehler beim Schreiben in die Datenbank.");
				logger.modifyIndent(-2);
				throw new ApiOperationException(Status.INTERNAL_SERVER_ERROR);
			}
			logger.logLn("[OK]");
			logger.modifyIndent(-2);
			return true;
		} catch (final UnrecognizedPropertyException upe) {
			LogUtils.logStacktrace(logger, upe);
			logger.logLn("[Fehler] Konnte die CSV-Datei nicht einlesen. Prüfen sie ggf. auch die Zeichenkodierung der Datei."
					+ " Diese muss UTF-8 (ohne BOM) sein.");
			logger.modifyIndent(-2);
			return false;
		} catch (@SuppressWarnings("unused") final Exception exception) {
			logger.logLn("[Fehler]");
			logger.modifyIndent(-2);
			return false;
		} finally {
			conn.transactionRollback();
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy