如何使用 ActiveRecord 调用带有 OUT 参数的 DB2 存储过程?
How can I call a DB2 Stored Procedure with OUT params using ActiveRecord?
我需要使用 ActiveRecord 调用一些遗留存储过程。
我可以使用 ActiveRecords 调用没有参数或只有 IN 参数的过程。
例如
ActiveRecord::Base.connection.execute('call legacy_proc')
或
ActiveRecord::Base.connection.execute("call legacy_proc('param1', 'param2')")
上面的工作非常好。现在,当我需要调用带有 OUT 参数的过程时,问题就出现了。我尝试了不同的调用 SP 的方法,但我无法让它工作。
ActiveRecord::Base.connection.execute("call legacy_proc_with_out_params('param1', 'param2', ?, ?)")
ActiveRecord::Base.connection.execute("call legacy_proc_with_out_params('param1', 'param2', null, null)")
ActiveRecord::Base.connection.execute("call legacy_proc_with_out_params('param1', 'param2', '', '')")
None 以上工作(最后两个参数是 OUT 参数)。作为最后的选择,如果真的不可能的话,我什至可以跳过读取 OUT 参数,只要我至少可以执行该过程。
编辑:添加错误详细信息以澄清 "not working"。
ActiveRecord::StatementInvalid: ActiveRecord::JDBCError: com.ibm.db2.jcc.am.SqlException: DB2 SQL Error: SQLCODE=-313, SQLSTATE=07004, SQLERRMC=null, DRIVER=4.15.113: call legacy_proc_with_out_params('param1', 'param2', ?, ?)
from arjdbc/jdbc/RubyJdbcConnection.java:547:in `execute'
from /home/devusr.gem/gems/activerecord-jdbc-adapter-1.3.16/lib/arjdbc/jdbc/adapter.rb:581:in `_execute'
from /home/devusr.gem/gems/activerecord-jdbc-adapter-1.3.16/lib/arjdbc/jdbc/adapter.rb:557:in `execute'
from /home/devusr.gem/gems/activerecord-4.1.8/lib/active_record/connection_adapters/abstract_adapter.rb:373:in `log'
from /home/devusr.gem/gems/activesupport-4.1.8/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
from /home/devusr.gem/gems/activerecord-4.1.8/lib/active_record/connection_adapters/abstract_adapter.rb:367:in `log'
from /home/devusr.gem/gems/activerecord-jdbc-adapter-1.3.16/lib/arjdbc/jdbc/adapter.rb:557:in `execute'
from (irb):30:in `evaluate'
from org/jruby/RubyKernel.java:1119:in `eval'
from org/jruby/RubyKernel.java:1519:in `loop'
from org/jruby/RubyKernel.java:1282:in `catch'
from org/jruby/RubyKernel.java:1282:in `catch'
from /home/devusr.gem/gems/railties-4.1.8/lib/rails/commands/console.rb:90:in `start'
from /home/devusr.gem/gems/railties-4.1.8/lib/rails/commands/console.rb:9:in `start'
from /home/devusr.gem/gems/railties-4.1.8/lib/rails/commands/commands_tasks.rb:69:in `console'
from /home/devusr.gem/gems/railties-4.1.8/lib/rails/commands/commands_tasks.rb:40:in `run_command!'
from /home/devusr.gem/gems/railties-4.1.8/lib/rails/commands.rb:17:in `(root)'
from org/jruby/RubyKernel.java:1083:in `require'
编辑 2:尝试了@mustaccio 的建议(即通过 raw_connection
使用 prepare
方法,但 DB2 适配器未实现该方法。如果有替代路径来实现类似的DB2 中的方法,这肯定会解决问题。
我还忘了说我正在使用 jRuby。因此,我正在为 DB2 使用 jdbc 适配器。
irb(main):049:0> ActiveRecord::ConnectionAdapters.constants.grep /DB2/
=> [:DB2Column, :DB2JdbcConnection]
irb(main):050:0> ActiveRecord::Base.connection.raw_connection.methods.sort.grep /prep/
=> []
irb(main):051:0> ActiveRecord::Base.connection.raw_connection.prepare
NoMethodError: undefined method `prepare' for #<ActiveRecord::ConnectionAdapters::DB2JdbcConnection:0xe6bc1cd0>
from (irb):51:in `evaluate'
from org/jruby/RubyKernel.java:1119:in `eval'
from org/jruby/RubyKernel.java:1519:in `loop'
from org/jruby/RubyKernel.java:1282:in `catch'
from org/jruby/RubyKernel.java:1282:in `catch'
from /home/srpec/.gem/gems/railties-4.1.8/lib/rails/commands/console.rb:90:in `start'
from /home/srpec/.gem/gems/railties-4.1.8/lib/rails/commands/console.rb:9:in `start'
from /home/srpec/.gem/gems/railties-4.1.8/lib/rails/commands/commands_tasks.rb:69:in `console'
from /home/srpec/.gem/gems/railties-4.1.8/lib/rails/commands/commands_tasks.rb:40:in `run_command!'
from /home/srpec/.gem/gems/railties-4.1.8/lib/rails/commands.rb:17:in `(root)'
from org/jruby/RubyKernel.java:1083:in `require'
from bin/rails:4:in `(root)'
我找到的解决方案是使用 BEGIN-END 块并声明虚拟变量以绑定到 OUT 参数。
给出这个例子:
ActiveRecord::Base.connection.execute("call legacy_proc_with_out_params('param1', 'param2', ?, ?)")
第一个 OUT 参数是 INTEGER,第二个是 SMALLINT,完整的调用如下所示:
beginend_stmt = "BEGIN
DECLARE out_Param1 INTEGER;
DECLARE out_Param2 SMALLINT;
call legacy_proc_with_out_params('param1', 'param2', out_Param1, out_Param2);
END"
ActiveRecord::Base.connection.execute beginend_stmt
我需要使用 ActiveRecord 调用一些遗留存储过程。
我可以使用 ActiveRecords 调用没有参数或只有 IN 参数的过程。
例如
ActiveRecord::Base.connection.execute('call legacy_proc')
或
ActiveRecord::Base.connection.execute("call legacy_proc('param1', 'param2')")
上面的工作非常好。现在,当我需要调用带有 OUT 参数的过程时,问题就出现了。我尝试了不同的调用 SP 的方法,但我无法让它工作。
ActiveRecord::Base.connection.execute("call legacy_proc_with_out_params('param1', 'param2', ?, ?)")
ActiveRecord::Base.connection.execute("call legacy_proc_with_out_params('param1', 'param2', null, null)")
ActiveRecord::Base.connection.execute("call legacy_proc_with_out_params('param1', 'param2', '', '')")
None 以上工作(最后两个参数是 OUT 参数)。作为最后的选择,如果真的不可能的话,我什至可以跳过读取 OUT 参数,只要我至少可以执行该过程。
编辑:添加错误详细信息以澄清 "not working"。
ActiveRecord::StatementInvalid: ActiveRecord::JDBCError: com.ibm.db2.jcc.am.SqlException: DB2 SQL Error: SQLCODE=-313, SQLSTATE=07004, SQLERRMC=null, DRIVER=4.15.113: call legacy_proc_with_out_params('param1', 'param2', ?, ?)
from arjdbc/jdbc/RubyJdbcConnection.java:547:in `execute'
from /home/devusr.gem/gems/activerecord-jdbc-adapter-1.3.16/lib/arjdbc/jdbc/adapter.rb:581:in `_execute'
from /home/devusr.gem/gems/activerecord-jdbc-adapter-1.3.16/lib/arjdbc/jdbc/adapter.rb:557:in `execute'
from /home/devusr.gem/gems/activerecord-4.1.8/lib/active_record/connection_adapters/abstract_adapter.rb:373:in `log'
from /home/devusr.gem/gems/activesupport-4.1.8/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
from /home/devusr.gem/gems/activerecord-4.1.8/lib/active_record/connection_adapters/abstract_adapter.rb:367:in `log'
from /home/devusr.gem/gems/activerecord-jdbc-adapter-1.3.16/lib/arjdbc/jdbc/adapter.rb:557:in `execute'
from (irb):30:in `evaluate'
from org/jruby/RubyKernel.java:1119:in `eval'
from org/jruby/RubyKernel.java:1519:in `loop'
from org/jruby/RubyKernel.java:1282:in `catch'
from org/jruby/RubyKernel.java:1282:in `catch'
from /home/devusr.gem/gems/railties-4.1.8/lib/rails/commands/console.rb:90:in `start'
from /home/devusr.gem/gems/railties-4.1.8/lib/rails/commands/console.rb:9:in `start'
from /home/devusr.gem/gems/railties-4.1.8/lib/rails/commands/commands_tasks.rb:69:in `console'
from /home/devusr.gem/gems/railties-4.1.8/lib/rails/commands/commands_tasks.rb:40:in `run_command!'
from /home/devusr.gem/gems/railties-4.1.8/lib/rails/commands.rb:17:in `(root)'
from org/jruby/RubyKernel.java:1083:in `require'
编辑 2:尝试了@mustaccio 的建议(即通过 raw_connection
使用 prepare
方法,但 DB2 适配器未实现该方法。如果有替代路径来实现类似的DB2 中的方法,这肯定会解决问题。
我还忘了说我正在使用 jRuby。因此,我正在为 DB2 使用 jdbc 适配器。
irb(main):049:0> ActiveRecord::ConnectionAdapters.constants.grep /DB2/
=> [:DB2Column, :DB2JdbcConnection]
irb(main):050:0> ActiveRecord::Base.connection.raw_connection.methods.sort.grep /prep/
=> []
irb(main):051:0> ActiveRecord::Base.connection.raw_connection.prepare
NoMethodError: undefined method `prepare' for #<ActiveRecord::ConnectionAdapters::DB2JdbcConnection:0xe6bc1cd0>
from (irb):51:in `evaluate'
from org/jruby/RubyKernel.java:1119:in `eval'
from org/jruby/RubyKernel.java:1519:in `loop'
from org/jruby/RubyKernel.java:1282:in `catch'
from org/jruby/RubyKernel.java:1282:in `catch'
from /home/srpec/.gem/gems/railties-4.1.8/lib/rails/commands/console.rb:90:in `start'
from /home/srpec/.gem/gems/railties-4.1.8/lib/rails/commands/console.rb:9:in `start'
from /home/srpec/.gem/gems/railties-4.1.8/lib/rails/commands/commands_tasks.rb:69:in `console'
from /home/srpec/.gem/gems/railties-4.1.8/lib/rails/commands/commands_tasks.rb:40:in `run_command!'
from /home/srpec/.gem/gems/railties-4.1.8/lib/rails/commands.rb:17:in `(root)'
from org/jruby/RubyKernel.java:1083:in `require'
from bin/rails:4:in `(root)'
我找到的解决方案是使用 BEGIN-END 块并声明虚拟变量以绑定到 OUT 参数。
给出这个例子:
ActiveRecord::Base.connection.execute("call legacy_proc_with_out_params('param1', 'param2', ?, ?)")
第一个 OUT 参数是 INTEGER,第二个是 SMALLINT,完整的调用如下所示:
beginend_stmt = "BEGIN
DECLARE out_Param1 INTEGER;
DECLARE out_Param2 SMALLINT;
call legacy_proc_with_out_params('param1', 'param2', out_Param1, out_Param2);
END"
ActiveRecord::Base.connection.execute beginend_stmt