Проблема с хранимой процедурой Java DB2

В моем классе я создал простой метод ниже. Этот класс также имеет основную функцию. Я вызываю getRes() из моей хранимой процедуры DB2.

public static void getRes() {
   System.out.println("Start");
   try{  
       Class.forName("com.ibm.db2.jcc.DB2Driver");  
       con = DriverManager.getConnection(  
       "jdbc:db2://url:50003/DB","user","Password");  
       Statement stmt=con.createStatement();  
       stmt.executeUpdate("INSERT INTO schema.TEST(ID) VALUES(1)"); 
   } catch(Exception e){
       System.out.println(e);
   } finally {
       if(con!=null) {
           try {
               con.close();
           } catch (SQLException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
           }
       }
   }
   System.out.println("End");
}

Я вызываю эту функцию из хранимой процедуры DB2.

CREATE OR REPLACE PROCEDURE schema.sp_TEST1()
 FENCED
 MODIFIES SQL DATA
EXTERNAL NAME 'connection.Connect.getRes()'
LANGUAGE JAVA
PARAMETER STYLE JAVA;
call schema.sp_TEST1()

Эта хранимая процедура успешно выполняется. Но я сомневаюсь, что мой метод выполняется или нет. Потому что после выполнения в тестовой таблице нет записи.

Ниже приведены шаги, которые я выполнил для развертывания этого файла jar. 1. Экспортировал банку из Eclipse. Проверено, работает ли банка. 2. Выполнен вызов sqlj.install_jar(). Проверена установка в SYSIBM.SYSJARCONTENTS. 3. УСТАНОВИТЕ путь класса на сервере db export CLASSPATH=/home/db2inse5/sqllib/function/jar/SchemaName/JarName.jar.

Ожидаемый результат - это запись, сделанная в таблице test. Но я не получаю ожидаемого результата, хотя выполнение sp прошло успешно.

Updated-20200423 Я попытался зафиксировать часть подключения к базе данных и заменил ее созданием файла на сервере БД. Это сработало, поэтому я могу подтвердить, что jar был правильно вызван из SP, и файл jar работал правильно. Но вышеуказанная часть еще не решена

public static void getRes() {
    System.out.println("Start");
            File myObj = new File("/opt/filename.txt");
            myObj.createNewFile();
}

person Db2Cramp    schedule 21.04.2020    source источник


Ответы (1)


Неверный метод получения объекта Connection в подпрограмме Parameter style Java. Прочтите следующий раздел документации: Процедуры JAVA в стиле параметров.
Вы не должны получать его, как в обычном приложении jdbc. Не используйте вызов Class.forName. Вместо этого используйте следующее:

con = DriverManager.getConnection("jdbc:default:connection");

Прочтите о Также ограничения на внешние подпрограммы. Не используйте System.out.println(...) внутри подпрограммы Java.

Пример

Сделайте это как есть на сервере базы данных от владельца экземпляра db2 без каких-либо изменений. Создайте указанную структуру каталогов. Убедитесь, что источник db2profile находится в вашем сеансе.

ExecAny.java:

package ru.ibm.db2udf;

import com.ibm.db2.jcc.DB2Diagnosable;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

public class ExecAny 
{
  /*
  --SQL statement for the class file placement w/o jar:
  --${DB2_HOME}/function/ru/ibm/db2udf/ExecAny.class

  --Uncomment the commented out line instead
  --if you placed this class into jar file and deployed it.

  CREATE OR REPLACE PROCEDURE EXEC_ANY 
  (
    SQL VARCHAR(4000)
  , LOG VARCHAR(512) DEFAULT NULL
  )
  LANGUAGE Java
  EXTERNAL NAME 'ru.ibm.db2udf.ExecAny.exec'
  --EXTERNAL NAME 'EXEC_ANY:ru.ibm.db2udf.ExecAny.exec'
  FENCED THREADSAFE
  MODIFIES SQL DATA
  PARAMETER STYLE JAVA;

  Usage:
    call exec_any('declare global temporary table test(i int) on commit preserve rows not logged', '/tmp/exec_any.txt');
    call exec_any('insert into session.test values 1', '/tmp/exec_any.txt');
    -- W/o logging
    call exec_any('insert into session.test values 2');

    select * from session.test;
  */
  public static void exec (String sql, String logfile) throws Exception 
  {
    Logger logger = null;
    FileHandler fhdl = null;
    Connection con = null;
    CallableStatement cst = null;
    try 
    {
      if (logfile != null && ! "".equals((logfile=logfile.trim())))
      {
        logger = Logger.getAnonymousLogger();
        fhdl = new FileHandler(logfile, true);
        fhdl.setFormatter(new SimpleFormatter());
        logger.addHandler(fhdl);
        logger.setLevel(Level.INFO);
        logger.info("***");
      }
      con = DriverManager.getConnection("jdbc:default:connection");
      con.setAutoCommit(false);
      if (logger != null) logger.info("Trying to prepare: " + sql);
      cst = con.prepareCall(sql);
      if (logger != null) logger.info("Prepared: " + sql);
      cst.execute();
      if (logger != null) logger.info("Executed: " + sql);
    } 
    catch (SQLException ex)
    {
      if (logger != null) 
      {
        while (ex != null) 
        {
          if (ex instanceof DB2Diagnosable) 
          {
            DB2Diagnosable db2ex = (DB2Diagnosable) ex;
            com.ibm.db2.jcc.DB2Sqlca sqlca = db2ex.getSqlca();
            if (sqlca != null) 
              logger.severe("\nSQLCODE: " + sqlca.getSqlCode() + "\nMESSAGE: " + sqlca.getMessage());
            else 
              logger.severe("\nError code: " + ex.getErrorCode() + "\nError msg : " + ex.getMessage());
          } 
          else 
            logger.severe("\nError code (no db2): " + ex.getErrorCode() + "\nError msg  (no db2): " + ex.getMessage());
          logger.log(Level.SEVERE, ex.getMessage(), ex);
          ex = ex.getNextException();
        }
      }
    } 
    finally
    {
      if (fhdl != null) {fhdl.close(); fhdl = null;}
      if (cst != null) {cst.close(); cst = null;}
      if (con != null) {con.close(); con = null;}
    }
  }

}

Инструкция по сборке и развертыванию:

# Suppose we are on a Db2 server and !!!db2profile is sourced in the session!!!
# java sources are in source
# java classes are in classes

$ ls -l
drwxr-xr-x classes
drwxr-xr-x source
drwxr-xr-x sql

$ find source -name '*.java'
source/ru/ibm/db2udf/ExecAny.java

# Compile
$ (cd source; ${DB2_HOME}/java/jdk64/bin/javac -d ../classes ru/ibm/db2udf/ExecAny.java)
# Create jar
$ ${DB2_HOME}/java/jdk64/bin/jar cvf exec_any.jar -C classes ru/ibm/db2udf/ExecAny.class

# Deploy jar
$ db2 connect to mydb
#$ db2 "call sqlj.replace_jar('file:${PWD}/exec_any.jar', 'EXEC_ANY')"
$ db2 "call sqlj.install_jar('file:${PWD}/exec_any.jar', 'EXEC_ANY')"
$ db2 "call sqlj.refresh_classes()"

# Jar file must appear in the ${DB2_HOME}/function/jar directory
$ find ${DB2_HOME}/function/jar -name '*.jar'
.../sqllib/function/jar/<USER_NAME>/EXEC_ANY.jar

# SP CREATE and CALL commands
$ cat sql/exec_any.sql
CREATE OR REPLACE PROCEDURE EXEC_ANY
(
  SQL VARCHAR(4000)
, LOG VARCHAR(512) DEFAULT NULL
)
LANGUAGE Java
EXTERNAL NAME 'EXEC_ANY:ru.ibm.db2udf.ExecAny.exec'
FENCED THREADSAFE
MODIFIES SQL DATA
PARAMETER STYLE JAVA;

(=
-- W/ logging to some file accessible by the db2 instance fenced user on the server
-- $ stat -c "%U" ${DB2_HOME}/ctrl/.fencedID
call exec_any('declare global temporary table test(i int) on commit preserve rows not logged', '/tmp/exec_any.txt');
call exec_any('insert into session.test values 1', '/tmp/exec_any.txt');
-- W/o logging
call exec_any('insert into session.test values 2');
select * from session.test;
=)
person Mark Barinstein    schedule 21.04.2020
comment
Пробовал это. Все та же проблема - person Db2Cramp; 21.04.2020
comment
Вызывали ли вы CALL SQLJ.REFRESH_CLASSES() после повторного развертывания jar, как описано в Изменения во внешней библиотеке подпрограмм и файлах классов? Если да, и вы все еще получаете какое-то непредвиденное поведение, попробуйте записать все выполнение подпрограммы в какой-нибудь файл на сервере. - person Mark Barinstein; 21.04.2020
comment
Возможно, вы также захотите рассмотреть возможность явной фиксации соединения перед его закрытием. - person mustaccio; 22.04.2020
comment
@mustaccio Я попробую. Хотя когда я запускаю метод Java, используя cmd. Данные отражаются в БД - person Db2Cramp; 22.04.2020
comment
Попробуйте убрать блок catch и все попытки распечатать исключение. Добавьте throws Exception к определению метода. Это позволяет получить некоторую информацию о возможных ошибках в db2diag.log. Вызовите db2diag -H 1m -o mydiag.txt (хронология за 1 минуту из файла db2diag.log) на сервере после вызова подпрограммы. Что вы получаете в txt файле? - person Mark Barinstein; 22.04.2020
comment
@MarkBarinstein Я пытаюсь выполнить команды db2diag по пути linux /home/db2inse5/sqllib/db2dump, но эта команда не распознана - person Db2Cramp; 22.04.2020
comment
Вы должны указать профиль db2 в своем сеансе с вызовом . /home/db2inse5/sqllib/db2profile, чтобы иметь возможность запускать команды/утилиты db2. Я предполагаю, что экземпляр db2inse5 — это экземпляр вашего сервера db2, на котором запущена ваша база данных. - person Mark Barinstein; 22.04.2020
comment
@MarkBarinstein Я обновил вопрос. Похоже, файл jar был установлен правильно, и вызов jar прошел успешно. Но код подключения к БД не работает, кажется - person Db2Cramp; 23.04.2020
comment
@Akashpoharkar Я обновил свой ответ примером и инструкцией. - person Mark Barinstein; 23.04.2020
comment
@MarkBarinstein Я зарегистрировал исключение в файле. Исключение говорит: «Для jdbc:db2://URL:50003/DB не найден подходящий драйвер». - person Db2Cramp; 23.04.2020