psg
Returning Observer

Datenbank Schema über FirstSpirit löschen

Jump to solution

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.ConnectionManager;

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

0 Kudos
1 Solution

Accepted Solutions
psg
Returning Observer

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.ConnectionManager;

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 Smiley Happy

Viele Grüße

Philipp

View solution in original post

0 Kudos
6 Replies
hoebbel
Crownpeak employee

Hallo Philipp,

statt einer Antwort habe ich erst einmal eine Gegenfrage Smiley Wink

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

0 Kudos
psg
Returning Observer

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 Smiley Wink

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

0 Kudos
hoebbel
Crownpeak employee

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 Smiley Wink] )?

Viele Grüße

Holger

0 Kudos
psg
Returning Observer

Dann will ich dich mal aus dem Ratemodus befreien Smiley Wink

<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

0 Kudos
hoebbel
Crownpeak employee

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 Smiley Sad

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

0 Kudos
psg
Returning Observer

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.ConnectionManager;

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 Smiley Happy

Viele Grüße

Philipp

0 Kudos