[JAVA] [Oracle] Windows-Batch vom Trigger ausführen

Einführung

Es wurde eine Reparaturanforderung gestellt, um das Protokoll der Tabelle auszugeben, die von der intern entwickelten Anwendung und der zugehörigen Tabelle verwendet wurde. Lesen Sie das Protokoll, damit eine andere externe Anwendung Informationen ausgibt. Der Zeitpunkt für die Ausgabe des Protokolls war, als die Anwendung die Daten in der Tabelle registrierte. In diesem Fall hatte ich das Gefühl, dass es möglich sein würde, einen Stapel mithilfe eines Datenbank-Triggers auszuführen, ohne die Anwendung zu ändern, und entschied mich daher, dies zu untersuchen. Ich möchte die Anwendung nicht so stark wie möglich ändern.

Umfrage

Es gibt zwei Möglichkeiten, einen Windows-Stapel aufzurufen.

Wenn Sie nur die Protokolldatei ausgeben möchten, können Sie das Paket UTL_FILE oder das Paket java.io auf Java verwenden. Angesichts der Vielseitigkeit des Speicherns auf einem Netzlaufwerksziel ist es jedoch besser, es an dem Ziel zu verarbeiten, das den Windows-Stapel aufgerufen hat. Etwas ist bequem. Referenz: Überlegen Sie, wie Protokolle aus PL / SQL ausgegeben werden

Java gespeicherte Methode

Oracle hat eine Java-VM implementiert, mit der Java-Programme in der Datenbank ausgeführt werden können. Ich habe bisher nur PL / SQL verwendet, daher ist dies eine gute Gelegenheit, gespeichertes Java zu lernen.

Zuerst Java gespeichert

Ich verwies auf die Website von "Java in PL / SQL verwendet".

Sobald Sie ein gespeichertes Java erstellt haben, müssen Sie auch einen Wrapper erstellen, um es aufzurufen. Der Wrapper muss nur einmal erstellt werden, wenn sich das Argument oder der Rückgabewert nicht ändert. Das Ergebnis wird auch dann wiedergegeben, wenn der gespeicherte Inhalt von Java später neu geschrieben wird.

CREATE OR REPLACE JAVA SOURCE NAMED java_test_src
AS
public class Test {
    public static int kasan(int a,int b){
        return a+b;
    }
}
/

CREATE OR REPLACE FUNCTION kasanf(a in number,b in number)
RETURN NUMBER
IS LANGUAGE java
NAME 'Test.kasan(int,int) return int'
;
/

Es kann genauso verwendet werden wie das Aufrufen einer PL / SQL-Benutzerfunktion. Interessant.

SELECT kasanf(2,3) FROM DUAL;

KASANF(2,3)
-----------
          5

Implementierung

Führen Sie einen Windows-Stapel (test.bat) aus, indem Sie ein Argument mit der Methode Runtime.getRuntime (). Exec übergeben, die einen Befehl von Java aus ausführt.

CREATE OR REPLACE JAVA SOURCE NAMED PLSQLExecByJava
AS
public class PLSQLExecByJava {
    public static String execByJava(
        String key
    ) {

        try {
            String cmd = "cmd /c start C:\\foo\\test.bat " + key;
            Runtime.getRuntime().exec(cmd);
            return "Success";

        } catch(Exception e) {
            return e.getMessage();
        }
    }
}
/

CREATE OR REPLACE FUNCTION FuncExec(key in VARCHAR2)
RETURN VARCHAR2
IS LANGUAGE java
NAME 'PLSQLExecByJava.execByJava(java.lang.String) return java.lang.String'
;
/

JAVA-Ausführungsberechtigung

Wenn Sie Befehle ausführen oder Dateien in einer gespeicherten Java-Prozedur eingeben / ausgeben, müssen Sie dem ausführenden Benutzer die entsprechenden Berechtigungen erteilen.

Das Erteilen der Ausführungsberechtigung ist die Prozedur GRANT_PERMISSION im Paket DBMS_JAVA. Als privilegierter Benutzer ausführen (SYS, DBA-Berechtigung).

CALL dbms_java.grant_permission( '<Berechtigungszielschema>', 'SYS:java.io.FilePermission',  '<<ALL FILES>>', 'execute' );

Im obigen Fall werden alle Dateien als Ziel ausgewählt. Wenn also ein Sicherheitsproblem vorliegt, ist der Bereich auf cmd.exe beschränkt.

call dbms_java.grant_permission('<Autorisierungszielschema>', 'SYS:java.io.FilePermission','C:\WINDOWS\system32\cmd.exe','execute');

So überprüfen Sie Berechtigungen

select * from DBA_JAVA_POLICY;

So entfernen Sie Berechtigungen

begin
  DBMS_JAVA.disable_permission(<SEQ>);
  DBMS_JAVA.delete_permission(<SEQ>);
end;

auslösen

Führen Sie den Befehl mit der Spalte KeyName der FUGA-Tabelle als Argument aus. Der Batch (test.bat) wird in FuncExec aufgerufen.

TRG_LOGOUTPUT


CREATE OR REPLACE TRIGGER "TEST"."TRG_LOGOUTPUT" 
AFTER INSERT
ON FUGA
FOR EACH ROW
DECLARE
  sResult VARCHAR2(200);
BEGIN
  IF INSERTING THEN
    SELECT FuncExec(:new.KeyName) INTO sResult FROM DUAL;
  END IF;
END;

Stapel

Ein Batch (test.bat) wird vom Trigger mit dem Schlüssel als Argument aufgerufen. Erhalten Sie diesen Schlüssel und geben Sie das Protokoll aus. Selbst wenn Sie zu diesem Zeitpunkt AUSWÄHLEN, können die Zieldaten nicht abgerufen werden, da sie noch nicht festgeschrieben wurden. Bereiten Sie daher eine weitere Charge (test2.bat) vor und legen Sie ein Kissen für die Ausgabe des Protokolls.

test.bat


cd /d %~dp0
start test2 %1

test2.bat


cd /d %~dp0
outputlog.exe %1
exit

Mit dieser Methode befinden Sie sich in einem festgeschriebenen Zustand und können die Zieldaten abrufen. Wenn es zurückgesetzt wird, wird kein Protokoll ausgegeben, da die Zieldaten nicht gefunden werden können.

DBMS_SCHEDULER-Methode

Jetzt, da ich es mit gespeichertem Java machen kann, habe ich auch die DBMS_SCHEDULER-Methode ausprobiert.

Berechtigung zur Jobausführung

Erteilen Sie Berechtigungen zum Ausführen von DBMS_SCHEDULER. Als privilegierter Benutzer ausführen (SYS, DBA-Berechtigung).

SQL>GRANT CREATE JOB TO <Benutzername>;
Die Autorisierung war erfolgreich.
SQL>GRANT CREATE REATE EXTERNAL JOB TO <Benutzername>;
Die Autorisierung war erfolgreich.

auslösen

Führen Sie den Befehl mit der Spalte KeyName der FUGA-Tabelle als Argument aus. Geben Sie zu diesem Zeitpunkt "autonomous_transaction" der autonomen Transaktion an.

TRG_LOGOUTPUT


CREATE OR REPLACE TRIGGER TRG_LOGOUTPUT 
BEFORE INSERT OR UPDATE
ON FUGA
FOR EACH ROW
DECLARE
  pragma autonomous_transaction;
BEGIN
  IF INSERTING THEN
    DBMS_SCHEDULER.CREATE_JOB (
      JOB_NAME   => 'job_test',
      JOB_TYPE   => 'EXECUTABLE',
      JOB_ACTION => 'C:\WINDOWS\system32\cmd.exe',
      number_of_arguments => 3,
      AUTO_DROP  => TRUE);

    DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE('job_test',1, '/c');
    DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE('job_test',2, 'C:\foo\test.bat');
    DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE('job_test',3, :new.KeyName);
    DBMS_SCHEDULER.ENABLE('job_test');
  END IF;
END;

Autonome Transaktion

Beim Aufrufen von DBMS_SCHEDULER.CREATE_JOB wird ein implizites Commit ausgegeben. Normalerweise schlägt es fehl, weil es nicht innerhalb des Triggers festgeschrieben werden kann, aber es kann vermieden werden, indem die autonome Transaktion "autonomous_transaction" verwendet wird.

Ergänzung

Der Teil, der durch "Erstellen eines dbms_scheduler.create_job mit Argumenten --stackoverflow" gelöst wurde. ist.

Bei der Eingabe von "enabled => TRUE" wie unten gezeigt wurde die Fehlermeldung "ORA-27457: Argument 1 (Job" TEST.JOB_TEST ") hat keinen Wert" angezeigt.

      number_of_arguments => 3,
      enabled    =>  TRUE,
      AUTO_DROP  => TRUE);

Dies ist ein Fehler, da der Job sofort beim Erstellen wirksam wird, die Argumente zu diesem Zeitpunkt jedoch noch nicht festgelegt wurden. "Enabled => TRUE" ist in Ordnung, wenn kein Argument vorhanden ist. Wenn jedoch ein Argument vorhanden ist, muss es nach dem Festlegen des Arguments aktiviert werden.

Stapel

Ein Batch (test.bat) wird vom Trigger mit dem Schlüssel als Argument aufgerufen. Erhalten Sie diesen Schlüssel und geben Sie das Protokoll aus. Im Gegensatz zu gespeichertem Java wird es bereits bei der Stapelausführung festgeschrieben, sodass kein Polster erforderlich ist. Wenn es zurückgesetzt wird, wird kein Protokoll ausgegeben, da die Zieldaten nicht gefunden werden können.

test.bat


cd /d %~dp0
outputlog.exe %1

Schließlich

Am Ende verschwand die Änderung zur Ausgabe des Protokolls und es wurde eine andere Methode, aber es war eine gute Studie. Ich dachte, ich könnte einen Weg finden, indem ich leicht suche, aber es ist unerwartet. Es fühlt sich an, als könnte ich es endlich an verschiedenen Orten herausfinden und kombinieren.

Referenz

Recommended Posts

[Oracle] Windows-Batch vom Trigger ausführen
Führen Sie Nicht-Java-Anweisungen von Java aus