触发器到 PostgreSQL JDBC 第一次成功写入,之后失败

huangapple go评论62阅读模式
英文:

Trigger to PostgreSQL JDBC writes successfuly first time, fails after

问题

这段代码似乎是用RPGLE编写的,用于将数据从IBM系统上的DB2发送到RHEL系统上的PostgreSQL服务器。它使用了一个触发器来将信息发送到一个数据队列,然后通过JDBC连接上的SQL语句将数据发送到PostgreSQL服务器。以下是关于这段代码的一些关键信息:

  • doPostgreConn 过程用于建立到PostgreSQL服务器的连接。它设置了一些连接属性,如用户名、密码、数据库名称以及日志文件的路径,并返回连接对象 pgconn

  • doPGWriteMyTable 过程用于将数据插入到PostgreSQL中的 MYTABLE 表中。它准备SQL插入语句,并将数据绑定到预编译的语句中,然后执行插入操作。

  • doPGDeleteMYTABLE 过程用于从PostgreSQL中的 MYTABLE 表中删除记录。它准备了SQL删除语句并执行删除操作。

你提到的问题是在数据队列读取程序运行时出现的,第一次循环正常工作,但在第二次循环中失败,错误信息提示连接已关闭。这可能是由于连接在某个时刻被关闭,但后续的代码仍然尝试使用已关闭的连接。

建议检查以下几点来解决这个问题:

  1. 确保在每次循环开始时都创建一个新的连接,而不是尝试重复使用已关闭的连接对象。

  2. 确保在执行 JDBC_Close(pgconn) 后,及时将连接对象 pgconn 设置为 *NULL,以避免在后续循环中意外使用已关闭的连接。

  3. 检查数据队列读取程序的逻辑,确保它在每次循环中正确管理连接对象的生命周期。

如果问题仍然存在,可能需要进一步检查数据库连接池和资源管理,以确保连接正常工作并及时释放。此外,你还可以查看PostgreSQL服务器的日志以获取更多有关连接关闭的信息,以帮助进一步诊断问题。

英文:

I have a project for sending data from DB2 on an IBM system over to a PostgreSQL server on a RHEL system. I am using a trigger that sends information to a data queue, which then gets read and sent over to the PostgreSQL server using a SQL statement through a JDBC connection on RPGLE.

The code is (more or less) as follows (I had to remove actual column and table names for security reasons):


dcl-proc doPostgreConn export;
  dcl-pi doPostgreConn like(connection) end-pi;
  //Code to change and set CLASSPATH variable, if necessary is here
  //...

  prop = JDBC_Properties();
  JDBC_setProp(prop: 'user'         : 'user');
  JDBC_setProp(prop: 'password'     : 'password');
  JDBC_setProp(prop: 'databaseName' : 'database');
  JDBC_setProp(prop: 'loggerLevel'  : 'TRACE'     );
  JDBC_setProp(prop: 'loggerFile'   : '/home/PostgreSQL/log');

  pgconn = JDBC_ConnProp('org.postgresql.Driver'
                     :'jdbc:postgresql://[some_IP]:5432/database'
                     : prop );
  JDBC_freeProp(prop);
  return pgconn;
end-proc;


dcl-proc doPGWriteMyTable export;
  dcl-pi doPGWriteMyTable like(success);
    i#schm char(10);
    i#rec char(334);
  end-pi;

  dcl-ds record extname('MYTABLE') end-ds;
  dcl-s prepStmtTxt varchar(10000);

  record = i#rec;

  pgconn = doPostgreConn;
  if pgconn = *NULL;
    //Custom Error Handling
  endif;

  prepStmtTxt = 'INSERT INTO ' + %trim(i#schm) + '.MYTABLE ' +
    '  VALUES (?, ?, ?) ';

  if PGWriteMYTABLEPrep = *NULL;

    PGWriteMYTABLEPrep = JDBC_PrepStmt(pgconn:prepStmtTxt);

    if PGWriteMYTABLEPrep = *NULL;
        
    endif;
  endif;

  JDBC_setString (PGWriteMYTABLEPrep: 1: StrCol);
  JDBC_setDecimal (PGWriteMYTABLEPrep: 2: DecCol);
  JDBC_setDate (PGWriteMYTABLEPrep: 75: DateCol);

  if JDBC_execPrepUpd( PGWriteMYTABLEPrep ) < 0;
    //Custom Error Handling
  endif;

  JDBC_Close(pgconn);

  return *on;
end-proc;


dcl-proc doPGDeleteMYTABLE export;
  dcl-pi doPGDeleteMYTABLE like(success);
    i#schm char(10);
    i#rec char(334);
  end-pi;

  dcl-ds record extname('MYTABLE') end-ds;
  dcl-s sqlstmt varchar(32000);
  dcl-s deleteSuccess ind;

  record = i#rec;

  sqlstmt = 'DELETE FROM ' + %trim(i#schm) + '.MYTABLE WHERE '; //Basically the key

  pgconn = doPostgreConn;
  if JDBC_ExecUpd(pgconn:sqlstmt) < 0;
    //Custom error handling
  endif;

  DoPostgreClose(pgconn);

  return *on;
end-proc;

The data queue read program essentially calls DoPGDeleteMYTABLE and then DoPGWriteMYTABLE, in that order (There is no unique key, so we simply delete all of the matching records on the PostgreSQL server and then re-add them).

The problem is, while the data queue read program is running, the first loop works perfectly fine, and then fails. The order goes like this:

  1. Record updated
  2. Delete any existing PG records: successful
  3. Prepare the write statement: successful
  4. Write any existing DB2 records to PG: successful
  5. Record updated
  6. Delete any existing PG records: successful
  7. Prepare the statement: successful
  8. Write any existing DB2 records to PG: unsuccessful
  9. repeat 5 through 8 until data queue job is restarted

The errors I receive are not very helpful. The job log on the AS400 simply tells me

org.postgresql.util.PSQLException: This connection has been closed.

even though I can see the open connection on the PostgreSQL server, and closing it from RPGLE does still work.

The JDBC job log does not tell me any information around the time the write happens. It just says that the prepare was successful, and then nothing.

Version information:

IBM OS 7.4

PostgreSQL 13.7 on x86_64-redhat-linux-gnu, compiled by gcc (GCC) 11.2.1 20220127 (Red Hat 11.2.1-9), 64-bit

PostgreSQL JDBC Driver postgresql-42.2.19

RPGLE is utilizing Scott Klement's JDBCR4

Nothing I have found online has yet to help with the issue. If there is anything else I can provide or try in order to get more information, please let me know.

答案1

得分: 0

I don't see anything that jumps out in the code you've posted, but given that it works the first time and fails the second, I'd guess something is reset (or not reset) between loops.

Personally, I'd recommend opening the connection once outside the DELETE/WRITE procs; but I don't think it's a fix.

The "connection closed" is interesting...might be worthwhile to run a comm trace to see if in fact the connection is being closed and if so from what side.

Note, while I love RPG, I'm not a fan of calling Java from RPG. I did some benchmarking long, long ago and it was much faster to have a small java app handle JDBC rather than using it from RPG.

You might also consider an Open Source alternative to calling Java directly from RPG.

AppServer4RPG
Application Server to make Java Components available for IBM i RPG programs, runs on IBM i or any other Java platform. Packaged with ArdGate to access any JDBC database using all native SQL interfaces from IBM i.

ArdGate basically registers itself as a DRDA Application Requester Driver (ARD) and allows you to talk to any JDBC database like you would any other remote DRDA (aka Db2) database.

Which means, you could read/write to PostgreSQL from the green screen STRSQL.

英文:

I don't see anything that jumps out in the code you've posted, but given that it works the first time an fails the second, I'd guess something is reset (or not reset) between loops.

Personally, I'd recommend opening the connection once outside the DELETE/WRITE procs; but I don't think it's a fix.

The "connection closed" is interesting...might be worthwhile to run a comm trace to see if in fact the connection is being closed and if so from what side.

Note, while I love RPG, I'm not a fan of calling Java from RPG. I did some benchmarking long, long ago and it was much faster to have a small java app handle JDBC rather than using it from RPG.

You might also consider an Open Source alternative to calling Java directly from RPG.

AppServer4RPG
Application Server to make Java Components available for IBM i RPG programs, runs on IBM i or any other Java platform. Packaged with ArdGate to access any JDBC database using all native SQL interfaces from IBM i.

ArdGate basically registers itself as a DRDA Application Requester Driver (ARD) and allow you to talk to any JDBC database like you would any other remote DRDA (aka Db2) database.

Which means, you could read/write to PostgreSQL from the green screen STRSQL.

答案2

得分: 0

我终于搞清楚了。这是一件我没有意识到需要做的愚蠢的事情 - 结果发现第一次使用完预备语句后,你必须释放它。

在使用JDBCR4时,只需调用(使用我的示例)

JDBC_FreePrepStmt(PGWriteMYTABLEPrep);

如果有人需要不使用JDBCR4的信息,它看起来像这样:

      *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      * JDBC_FreePrepStmt(): 释放预备语句
      *
      *    prep =(输入)要释放的预备语句
      *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     P JDBC_FreePrepStmt...
     P                 B                   导出
     D JDBC_FreePrepStmt...
     D                 PI
     D   prep                              类似(PreparedStatement)
      /free
         stmt_close(prep);
         DeleteLocalRef(JNIENV_P: prep);
         prep = *NULL;
      /end-free
     P                 E

最后,一个措辞非常不清晰的错误,有一个非常简单的解决方案。

英文:

I finally got it figured out. It was a dumb thing that I didn't realize I needed to do - turns out you have to free the prepared statement after using it the first time.

Using JDBCR4, you just call (using my example)

JDBC_FreePrepStmt(PGWriteMYTABLEPrep);

Which looks like this, if anybody needs info that doesn't use JDBCR4:

      *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      * JDBC_FreePrepStmt(): Free prepared statement
      *
      *    prep = (input) Prepared Statement to Free
      *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     P JDBC_FreePrepStmt...
     P                 B                   export
     D JDBC_FreePrepStmt...
     D                 PI
     D   prep                              like(PreparedStatement)
      /free
         stmt_close(prep);
         DeleteLocalRef(JNIENV_P: prep);
         prep = *NULL;
      /end-free
     P                 E

In the end, a very poorly worded error, with a very simple solution.

huangapple
  • 本文由 发表于 2023年2月10日 03:34:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/75403579.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定