Search the FirstSpirit Knowledge Base
Hallo zusammen,
ich möchte ganz stupide ein Datenbankschema in einer PostgreSQL Datenbank löschen. Dies muss mit FS-Mitteln erfolgen. Die Löschung über beispielsweise pgAdmin wäre zwar in wenigen Sekunden getan, aber nicht immer gibt es Zugriff auf die Systeme, daher der Umweg über FS.
Bisher habe ich folgendes Skript im Server-Auftrag laufen:
import de.espirit.firstspirit.access.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.Statement;
private final String url = "jdbc:postgresql://localhost:5432/fs5_project";
private final String user = "postgres";
private final String password = "secret";
Connection connection = context.getConnection();
connection.connect();
Class.forName("org.postgresql.Driver", true, connection.getClassLoader());
DriverManager.registerDriver(new org.postgresql.Driver ());
java.sql.Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
stmt.executeQuery("DROP SCHEMA delete_this CASCADE;");
conn.close();
Leider bricht es ab, weil es angeblich keinen verfügbaren Treiber findet. Das PostgreSQL-JDBC-FSM ist installiert und die Datenbankverbindung im Datenbank-Tab kann mit obigen Daten ebenfalls erfolgreich erreich werden. Es sind also alle Voraussetzungen erfüllt.
Vielen Dank schon mal.
Viele Grüße
Philipp
Hallo Holger,
in der Zwischenzeit habe ich mit dem Tech-Support gesprochen und hier kam noch ein altenativer Work-Around bei rum ohne den DriverManager zu nutzen. Mein Skript funktioniert damit super und sieht nun so aus:
import de.espirit.firstspirit.access.Connection;
import java.sql.Driver;
import java.sql.Statement;
import java.sql.ResultSet;
private final String url = "jdbc:postgresql://localhost:5432/fs5_project";
private final String user = "postgres";
private final String password = "secret";
private final String schemaName = "delete_this";
private final String statement = "DROP SCHEMA " + schemaName + " CASCADE;";
Properties props = new Properties();
props.setProperty("user", user);
props.setProperty("password", password);
try {
Connection connection = context.getConnection();
connection.connect();
Driver driver = Class.forName("org.postgresql.Driver", true, connection.getClassLoader()).newInstance();
context.logInfo("Connecting to database.");
conn = driver.connect(url, props);
context.logInfo("Check if schema '" + schemaName + "' exists.");
stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT schema_name FROM information_schema.schemata WHERE schema_name = '" + schemaName + "';");
// Check if there is a first row in the result set
if (rs.next()) {
context.logInfo("Sending statement: " + statement);
stmt.close();
stmt = conn.createStatement();
stmt.executeUpdate(statement);
context.logInfo("Statement executed.");
} else {
context.logInfo("Schema '" + schemaName + "' does not exist. Nothing to do.");
}
} catch (Exception e) {
context.logError("Error while database connection: " + e);
} finally {
context.logInfo("Closing connection to database.");
// stmt.close();
conn.close();
}
Danke für deine Anregungen, die Magie steckt nun letztendlich in diesen Zeilen:
Driver driver = Class.forName("org.postgresql.Driver", true, connection.getClassLoader()).newInstance();
conn = driver.connect(url, props);
Es ist hier zwingend erforderlich auf den DriverManager zu verzichten, da trotz der erfolgreich geladenen Klasse "org.postgresql.Driver" diese schlichtweg nicht gefunden wird (wieso auch immer?!?). Das wieso habe ich an den Tech-Support weitergegeben. Mir persönlich reicht aber der Work-Aorund so mehr als aus
Viele Grüße
Philipp
Hallo Philipp,
statt einer Antwort habe ich erst einmal eine Gegenfrage
Warum willst Du denn das Schema löschen?
Hintergrund der Frage: Ab einer der nächsten FirstSpirit Versionen (ich hoffe 2021-07, erwarte aber eher 2021-08) wird es beim Löschen eines FirstSpirit Projektes die Möglichkeit geben, die im Projekt benutzten Datenbank-Schemata mit zu löschen.
Wenn es also "nur" drum geht, die Datenbank beim Löschen eines FirstSpirit Projektes zu bereinigen, wird diese Funktionalität demnächst als Standard zur Verfügung stehen.
Falls es um etwas anderes geht: Kannst du mal die module.xml zw. module-isolated.xml Datei des Moduls posten und angeben, ob Du das Skript auf dem Server (z.B. als Auftrag) oder in einem Client (SiteArchitect/ContentCreator) ausführst?
Viele Grüße
Holger
Hallo Holger,
vielen Dank für deine Rückmeldung.
Das Schema soll tatsächlich beim Löschen des Projektes entfernt werden, im Optimalfall auch für ältere Versionen. Daher betrachte ich die zukünftige Funktion mit einem lachenden und einem weinenden Auge. Aber sehr gut, dass das kommen wird!
Falls du bei der Module.xml auf das scope="server | module" hinaus willst, das habe ich bereits versucht
Ich lasse das Skript aktuell im Rahmen eines Server-Auftrags laufen, wegen dem eingestellten Server scope. Wenn das im SA funktionieren würde, wäre das noch cooler.
Viele Grüße
Philipp
Hallo Philipp,
bei der module.xml will ich erst einmal sehen, welche Ressourcen eingebunden wurden und ja, auch in welchem Scope. Das wäre schon hilfreich so sheen, ansosten kann an nur raten.
Wo ich beim raten bin - funktioniert die Datenbank-Verbindung im Datenbanktab auch, wenn in der Konfiguration das Modul nicht angegeben wird (also die Klassen aus dem globalen Serverkontext geladen werden)?
Wenn nein - bist Du sicher, dass die <resource> im <resources> Tag auf scope="server" gesetzt wurde? (und nicht vielleicht im <web-resources> Tag, falls es das gibt [wie gesagt, ich bin im Ratemodus ] )?
Viele Grüße
Holger
Dann will ich dich mal aus dem Ratemodus befreien
<module>
<name>PostgreSQL_JDBC_4_2_Driver</name>
<version>42.2.22</version>
<description>JDBC Driver for PostgreSQL 8.2 and newer databases</description>
<vendor>PostgreSQL Global Development Group</vendor>
<resources>
<resource scope="server">lib/postgresql-42.2.22.jar</resource>
</resources>
<components>
</components>
<configuration>
<DRIVER>org.postgresql.Driver</DRIVER>
<layerclass>de.espirit.or.impl.postgres.PostgreSQLLayer</layerclass>
</configuration>
</module>
Ja, die im Datenbank-Tab habe ich die Konfiguration einmal abgeändert und es funktioniert alles einwandfrei:
# Projektvorlage_news
jdbc.DRIVER=org.postgresql.Driver
jdbc.PASSWORD=secret
jdbc.SCHEMA=news
jdbc.URL=jdbc:postgresql:fs5_project
jdbc.USER=postgres
jdbc.layerclass=de.espirit.or.impl.postgres.PostgreSQLLayer
jdbc.property.defaultRowFetchSize=5000
Hallo Philipp,
ich sehe den Fehler nicht - das muss irgendetwas damit zu tun haben, dass die global gekennzeichneten Libraries aus dem Modul in dem verwendeten Kontext nicht erreicht werden können. Aber warum das so ist, kann ich im Moment nicht sagen
Mögliche Workarounds, falls ihr eine schnelle Lösung benötigt wären:
* eigenes Modul schreiben, welches die PostGreSql Klasse mitbringt (Scope=module) und das über ein Executable die Löschoperation ausführt. Das sollte eigentlich funktionieren.
* die PostGreSql Klasse in den Ordner <FirstSpiritROOT>/shared/lib-isolated legen. Nach einem FirstSpirit Neustart müsste es dann klappen.
Bitte über den TechSupport entweder das bestehende Ticket oder ein neues Ticket verwenden, um prüfen zu lassen, ob das Ganze möglicherweise ein Bug ist.
Wie gesagt - ich sehe im Moment den Fehler nicht. Deshalb kann ich auch nicht sicher sagen, ob der erste der möglichen Workarounds so funktionieren wird. Der zweite hat bei mir lokal geklappt.
EDIT: Ich habe inzwischen etwas nachgeforscht. Das ist ein java Problem, da die Klassen nicht über den System-Classloader geladen werden (wie bei dem zwiten Workaround)
Wenn man den korrekten ClassLoader angibt, kann man die Klasse laden, also
myClass = Class.forName("org.postgresql.Driver",true,connection.getClassLoader());
Das hier hingegen führt zu einer java.lang.ClassNotFoundException: org.postgresql.Driver
noClass = Class.forName("org.postgresql.Driver");
--> Ursache ist also JavaSpezifisch/ClassLoaderSpezifisch und kein FirstSpirit Problem im eigentlichen Sinn.
Viele Grüße
Holger
Hallo Holger,
in der Zwischenzeit habe ich mit dem Tech-Support gesprochen und hier kam noch ein altenativer Work-Around bei rum ohne den DriverManager zu nutzen. Mein Skript funktioniert damit super und sieht nun so aus:
import de.espirit.firstspirit.access.Connection;
import java.sql.Driver;
import java.sql.Statement;
import java.sql.ResultSet;
private final String url = "jdbc:postgresql://localhost:5432/fs5_project";
private final String user = "postgres";
private final String password = "secret";
private final String schemaName = "delete_this";
private final String statement = "DROP SCHEMA " + schemaName + " CASCADE;";
Properties props = new Properties();
props.setProperty("user", user);
props.setProperty("password", password);
try {
Connection connection = context.getConnection();
connection.connect();
Driver driver = Class.forName("org.postgresql.Driver", true, connection.getClassLoader()).newInstance();
context.logInfo("Connecting to database.");
conn = driver.connect(url, props);
context.logInfo("Check if schema '" + schemaName + "' exists.");
stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT schema_name FROM information_schema.schemata WHERE schema_name = '" + schemaName + "';");
// Check if there is a first row in the result set
if (rs.next()) {
context.logInfo("Sending statement: " + statement);
stmt.close();
stmt = conn.createStatement();
stmt.executeUpdate(statement);
context.logInfo("Statement executed.");
} else {
context.logInfo("Schema '" + schemaName + "' does not exist. Nothing to do.");
}
} catch (Exception e) {
context.logError("Error while database connection: " + e);
} finally {
context.logInfo("Closing connection to database.");
// stmt.close();
conn.close();
}
Danke für deine Anregungen, die Magie steckt nun letztendlich in diesen Zeilen:
Driver driver = Class.forName("org.postgresql.Driver", true, connection.getClassLoader()).newInstance();
conn = driver.connect(url, props);
Es ist hier zwingend erforderlich auf den DriverManager zu verzichten, da trotz der erfolgreich geladenen Klasse "org.postgresql.Driver" diese schlichtweg nicht gefunden wird (wieso auch immer?!?). Das wieso habe ich an den Tech-Support weitergegeben. Mir persönlich reicht aber der Work-Aorund so mehr als aus
Viele Grüße
Philipp