[JAVA] [Oracle] Exécuter le lot Windows à partir du déclencheur

introduction

Il y avait une demande de modification des journaux de sortie des tables utilisées par les applications développées en interne et les tables associées. Reportez-vous au journal pour qu'une autre application externe produise des informations. Le moment de sortie du journal était lorsque l'application a enregistré les données dans la table. Dans ce cas, j'ai senti qu'il serait possible d'exécuter un lot en utilisant un déclencheur de base de données sans modifier l'application, j'ai donc décidé d'enquêter. Je ne veux pas modifier l'application autant que possible.

Enquête

Il existe deux façons d'appeler un lot Windows.

Si vous souhaitez simplement générer le fichier journal, vous pouvez utiliser le package UTL_FILE ou le package java.io sur Java stocké, mais compte tenu de la polyvalence de l'enregistrement sur une destination de lecteur réseau ou autre, il est préférable de le traiter à la destination qui a appelé le lot Windows. Quelque chose est pratique. Référence: Considérez comment générer des journaux à partir de PL / SQL

Méthode stockée Java

Oracle a implémenté une machine virtuelle Java qui permet aux programmes Java de s'exécuter sur la base de données. Je n'ai utilisé que PL / SQL auparavant, c'est donc une bonne occasion d'apprendre Java stocké.

Premier Java stocké

J'ai fait référence au site de "Java utilisé en PL / SQL".

Une fois que vous avez créé un Java stocké, vous devrez également créer un wrapper pour l'appeler. Le wrapper ne doit être créé qu'une seule fois s'il n'y a pas de changement dans l'argument ou la valeur de retour, et le résultat sera reflété même si le contenu de Java stocké est réécrit plus tard.

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'
;
/

Il peut être utilisé de la même manière que l'appel d'une fonction utilisateur PL / SQL. Intéressant.

SELECT kasanf(2,3) FROM DUAL;

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

la mise en oeuvre

Exécutez un lot Windows (test.bat) en passant un argument à l'aide de la méthode Runtime.getRuntime (). Exec qui exécute une commande à partir de Java.

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'
;
/

Autorisation d'exécution JAVA

Lors de l'exécution de commandes ou de l'entrée / sortie de fichiers dans une procédure stockée Java, il est nécessaire d'accorder les autorisations appropriées à l'utilisateur exécutant.

L'octroi de l'autorisation d'exécution est la procédure GRANT_PERMISSION du package DBMS_JAVA. Exécuter en tant qu'utilisateur privilégié (SYS, autorité DBA).

CALL dbms_java.grant_permission( '<Schéma de destination des privilèges>', 'SYS:java.io.FilePermission',  '<<ALL FILES>>', 'execute' );

Dans le cas ci-dessus, tous les fichiers sont ciblés, donc en cas de problème de sécurité, la plage est limitée à cmd.exe.

call dbms_java.grant_permission('<Schéma de destination de l'autorisation>', 'SYS:java.io.FilePermission','C:\WINDOWS\system32\cmd.exe','execute');

Comment vérifier les autorisations

select * from DBA_JAVA_POLICY;

Comment supprimer les autorisations

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

déclencheur

Exécutez la commande avec la colonne KeyName de la table FUGA comme argument. Le batch (test.bat) est appelé dans FuncExec.

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;

lot

Un batch (test.bat) est appelé à partir du déclencheur avec la clé comme argument. Recevez cette clé et sortez le journal. Cependant, même si vous SELECT à ce stade, les données cibles ne peuvent pas être obtenues car elles n'ont pas encore été validées. Par conséquent, préparez un autre lot (test2.bat) et placez un coussin pour sortir le journal.

test.bat


cd /d %~dp0
start test2 %1

test2.bat


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

Par cette méthode, vous serez dans un état validé et vous pourrez récupérer les données cibles. En outre, s'il est annulé, aucun journal ne sera généré car les données cibles sont introuvables.

Méthode DBMS_SCHEDULER

Maintenant que je peux le faire avec Java stocké, j'ai également essayé la méthode DBMS_SCHEDULER.

Autorisation d'exécution du travail

Accordez les autorisations pour exécuter DBMS_SCHEDULER. Exécuter en tant qu'utilisateur privilégié (SYS, autorité DBA).

SQL>GRANT CREATE JOB TO <nom d'utilisateur>;
L'autorisation a réussi.
SQL>GRANT CREATE REATE EXTERNAL JOB TO <nom d'utilisateur>;
L'autorisation a réussi.

déclencheur

Exécutez la commande avec la colonne KeyName de la table FUGA comme argument. À ce moment, spécifiez "transaction_autonome" de la transaction autonome.

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;

Transaction autonome

L'appel de DBMS_SCHEDULER.CREATE_JOB émet une validation implicite. Normalement, elle échoue car elle ne peut pas être validée à l'intérieur du déclencheur, mais elle peut être évitée en utilisant la transaction autonome "transaction_autonome".

Supplément

La partie qui a été résolue par "Création d'un dbms_scheduler.create_job avec des arguments --stackoverflow" est.

Lorsque j'ai entré "enabled => TRUE" comme indiqué ci-dessous, j'ai eu une erreur "ORA-27457: Argument 1 (job" TEST.JOB_TEST ") n'a pas de valeur".

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

Il s'agit d'une erreur car le travail prend effet immédiatement lors de sa création, mais les arguments n'ont pas encore été définis à ce stade. "Enabled => TRUE" convient s'il n'y a pas d'argument, mais s'il y a un argument, il doit être activé après avoir défini l'argument.

lot

Un batch (test.bat) est appelé à partir du déclencheur avec la clé comme argument. Recevez cette clé et sortez le journal. Contrairement à Java stocké, il est déjà validé lors de l'exécution par lots, il n'est donc pas nécessaire de mettre un coussin. S'il est annulé, aucun journal ne sera généré car les données cibles sont introuvables.

test.bat


cd /d %~dp0
outputlog.exe %1

finalement

En fin de compte, la modification pour sortir le journal a disparu et c'est devenu une autre méthode, mais c'était une bonne étude. Je pensais pouvoir trouver un moyen en cherchant facilement, mais c'est inattendu. J'ai l'impression que je peux enfin trouver de divers endroits et les combiner.

référence

Recommended Posts

[Oracle] Exécuter le lot Windows à partir du déclencheur
Exécuter des instructions non-Java à partir de Java