Search This Blog

Showing posts with label PLSQL. Show all posts
Showing posts with label PLSQL. Show all posts

Thursday, 13 January 2011

Retrieving DBMS_OUTPUT from PLSQL in a JRuby script

This demo shows how to get DBMS OUTPUT form PLSQL blocks and PLSQL program units using JRuby. In this example we obtain a JDBC connection which we then use to invoke a PLSQL block and get the DBMS OUTPUT back to the JRuby script.

1. Download ojdbc6.jar from the links below.

ojdbc6.jar (11.2.0.2)

2. Create a JRuby script for the JDBC connection named "jdbc_connection.rb" as follows
require 'java'
require 'C:/jdev/jdcbdrivers/11.2/ojdbc6.jar'

import 'oracle.jdbc.OracleDriver'
import 'java.sql.DriverManager'

class OracleConnection 

  @conn = nil
  
  def initialize (user, passwd, url)
    @user = user
    @passwd = passwd
    @url = url
    
    # load driver class
    oradriver = OracleDriver.new
    
    DriverManager.registerDriver(oradriver)
    @conn = DriverManager.getConnection(url,user,passwd);
    @conn.setAutoCommit(false);
    
  end
 
  #add getters and setters for all attrributes we wish to expose
  attr_reader :user, :passwd, :url
 
  def getConnection()
    return @conn
  end

  def closeConnection()
    @conn.close()
  end

  def to_s
    "OracleConnection [user=#{@user}, passwd=#{@passwd}, " +
    "url=#{@url}]"
  end
  alias_method :to_string, :to_s
  
end


- Edit the line below to indicate the PATH to the Oracle JDBC driver downloaded at step #1

require 'C:/jdev/jdcbdrivers/11.2/ojdbc6.jar'


3. Create a JRuby script for the DBMS OUTPUT class named "dbms_output.rb" as follows.
require 'java'

require 'C:/jdev/jdcbdrivers/11.2/ojdbc6.jar'

import 'java.sql.CallableStatement'
import 'java.sql.Connection'
import 'java.sql.SQLException'
import 'java.sql.Types'

class DbmsOutput

  def initialize (conn)
    @enable_stmt  = conn.getConnection.prepareCall( "begin dbms_output.enable(:1); end;" );
    @disable_stmt = conn.getConnection.prepareCall( "begin dbms_output.disable; end;" );
    
    @show_stmt = conn.getConnection.prepareCall( 
          "declare " +
          "    l_line varchar2(255); " +
          "    l_done number; " +
          "    l_buffer long; " +
          "begin " +
          "  loop " +
          "    exit when length(l_buffer)+255 > :maxbytes OR l_done = 1; " +
          "    dbms_output.get_line( l_line, l_done ); " +
          "    l_buffer := l_buffer || l_line || chr(10); " +
          "  end loop; " +
          " :done := l_done; " +
          " :buffer := l_buffer; " +
          "end;" );
  end
  
  def enable (size)
    @enable_stmt.setInt( 1, size );
    @enable_stmt.executeUpdate();
  end
  
  def disable
    @disable_stmt.executeUpdate();
  end
  
  def show
      output = ""
      done = 0;
  
      @show_stmt.registerOutParameter( 2, 4);
      @show_stmt.registerOutParameter( 3, 12 );
  
      while (done == 0)   
          @show_stmt.setInt( 1, 32000 );
          @show_stmt.executeUpdate();
          output = output , "" , @show_stmt.getString(3), "\n"
          if ( (done = @show_stmt.getInt(2)).to_i == 1 ) 
            break
          end
      end
      
      return output
  end
  
  def closeAll
    @enable_stmt.close();
    @disable_stmt.close();
    @show_stmt.close();
  end
  
end 

- Edit the line below to indicate the PATH to the Oracle JDBC driver downloaded at step #1

require 'C:/jdev/jdcbdrivers/11.2/ojdbc6.jar'


4. Create a JRuby test script named "test_dbms_output.rb" as follows
# test DBMS_OUTPUT from JRuby
#

require 'lib/jdbc_connection'
require 'lib/dbms_output'

print "Run at ", Time.now , " using JRuby ", RUBY_VERSION, "\n"
puts 

conn = nil

begin

  conn = OracleConnection.new("scott", "tiger", "jdbc:oracle:thin:@beast.au.oracle.com:1524/linux11gr2")
  puts conn
  dbms_output = DbmsOutput.new(conn)
  dbms_output.enable( 1000000 );
  
  plsql_block = 
    "begin " + 
    " for i in 1..10 loop " +
    "  dbms_output.put_line('Hello JRuby at position '||i); " + 
    " end loop; " +
    "end;"
 
  stmt = conn.getConnection.createStatement();
  stmt.execute(plsql_block);   
  
  print "** Output from PLSQL Block as follows **"
  puts dbms_output.show()
  dbms_output.closeAll()
  
rescue 
  print "\n** Error occured **\n"
 print "Failed executing Oracle JDBC DBMS_OUTPUT demo from JRuby ", $!, "\n"
  if (!conn.nil?)
    conn.closeConnection()
  end
  
end

print "\nEnded at ", Time.now , "\n"

- Edit the 2 lines above to refer to the files created at step #2 and step #3. In this example they exist of a lib directory from the current directory.

require 'lib/jdbc_connection'
require 'lib/dbms_output'

Note: Ensure you specify a connection to your database as follows

conn = OracleConnection.new("scott", "tiger", "jdbc:oracle:thin:@beast.au.oracle.com:1524/linux11gr2")

5. Run test_dbms_output.rb

Run at Thu Jan 13 07:45:21 +1100 2011 using JRuby 1.8.7

OracleConnection [user=scott, passwd=tiger, url=jdbc:oracle:thin:@beast.au.oracle.com:1524/linux11gr2]
** Output from PLSQL Block as follows **


Hello JRuby at position 1
Hello JRuby at position 2
Hello JRuby at position 3
Hello JRuby at position 4
Hello JRuby at position 5
Hello JRuby at position 6
Hello JRuby at position 7
Hello JRuby at position 8
Hello JRuby at position 9
Hello JRuby at position 10


Ended at Thu Jan 13 07:45:21 +1100 2011

Wednesday, 9 December 2009

PLSQL commit every X records and Coherence putAll every X records

Note for self:

Was using this in Write-Behind Caching Demo with Oracle Coherence using PLSQL Bulk Binds.

When using PLSQL to bulk insert code as follows to control the commit time.
-- control number of records to insert before calling COMMIT
NUM_OF_RECORDS_TO_INSERT constant number := 10000;

IF (mod(l_count, NUM_OF_RECORDS_TO_INSERT) = 0) THEN
FORALL i IN mid.FIRST..mid.LAST
insert into messages (message_id, message_type, message)
values (mid(i), mt(i), m(i));
END IF;

When loading many cache records ensure we only call putAll to control the amount of records to insert into the cache in one hit.
final private static int BATCH_SIZE = 1000;

Map buffer = new HashMap();

long start = System.currentTimeMillis();

for (int i = 1; i <= records; i++)
{
Message message = new Message
(new BigDecimal(i),
"M",
String.format("Message %s from test client", i));
buffer.put(String.valueOf(i), message);

if ((i % BATCH_SIZE) == 0)
{
messageCache.putAll(buffer);
buffer.clear();
}
}

if (!buffer.isEmpty())
{
messageCache.putAll(buffer);
buffer.clear();
}

Thursday, 5 November 2009

Oracle Coherence - read-write-backing-map-scheme

I spent the last 2 weeks in Boston working with the oracle coherence team and how that product works and I am impressed with it. One thing which I was keen on testing out / learning in depth was the read-write-backing-map-scheme. I specifically wanted to learn write-behind which when enabled the cache will delay writes to the back end cache store, which in my example would be an Oracle 11g database. To me there is really only 2 options for this to be as quick as possible and of course use connection pooling which UCP (Oracle Universal Connection Pool) is perfect for.

1. JDBC Batching
2. PLSQL bulk binds

Whats important though is that we only commit after X amount of records, and only go to the database when we are ready to insert those X amount of records in a single round trip. To me PLSQL bulk binds make sense here, as all it takes is to convert those cache entries into an SQL object which the database can interpret from PLSQL. Once that's done your PLSQL is as simple as this really.

FORALL i IN mid.FIRST..mid.LAST
insert into messages (message_id, message_type, message)
values (mid(i), mt(i), m(i));

COMMIT;

I will post a full example on this shortly which also shows how to read the data back into the cache.

More about this can be found here on what I want to setup. My initial testing showed 1,000,000 records inserted without too much issues BUT there are a couple of things to be aware from coherence and oracle itself.

http://coherence.oracle.com/display/COH35UG/Read-Through%2C+Write-Through%2C+Write-Behind+and+Refresh-Ahead+Caching

Tuesday, 1 September 2009

JDeveloper 11g - PLSQL Web Service Returning a Table of Objects

Got a few emails about problems using a Table of Objects within PLSQL Web Services now it's been reintroduced into JDeveloper 11g. Here is a small demo which worked fine for me.

1. Run the following SQL in the classic SCOTT schema

drop type dept_type;
drop type dept_list_table;

create type dept_type as object
(deptno NUMBER,
dname VARCHAR2(14),
loc varchar2(13),
cr_date date)
/

create type dept_list_table is table of dept_type
/

purge recyclebin;

create or replace package ws_package as

function test_dept_table return dept_list_table;

end ws_package;
/
show errors;

create or replace package body ws_package as

function test_dept_table return dept_list_table is

all_depts dept_list_table := dept_list_table();
dRecType dept_type;
i number := 0;

begin

-- iterate through all depts
for r_list in (select * from dept) loop

i := i + 1;

dRecType := dept_type(null, null, null, null);
dRecType.deptno := r_list.deptno;
dRecType.dname := r_list.dname;
dRecType.loc := r_list.loc;
dRecType.cr_date := sysdate;

all_depts.extend;
all_depts(i) := dRecType;

end loop;

return all_depts;

end test_dept_table;

end ws_package;
/
show errors;
2. Create a PLSQL Web Service called "DemoWS" using the defaults for the package WS_PACKAGE.

3. Right click on the Web Service "DemoWS" and select "Run". It may take a while to start but eventually you should see it's started as follows.

Run startup time: 10437 ms.
[Application WebServicesTest deployed to Server Instance DefaultServer]

Target URL -- http://localhost:7101/WebServicesTest-PLSQLTest-context-root/DemoWSPort
09/08/13 09:49:19 Web service DemoWS has been started on the embedded server

4. Copy the URL above into a browser.
5. Click on "Test Page"
6. Click on the only function "testDeptTable"
7. Verify response as follows.

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header />
<env:Body>
<m:testDeptTableResponse xmlns:m="http://au/support/ws/DemoWS.wsdl">
<result xmlns:typ="http://au/support/ws/DemoWS.wsdl/types/">
<typ:DeptTypeUser>
<typ:dname>ACCOUNTING</typ:dname>
<typ:loc>NEW YORK</typ:loc>
<typ:deptno>10</typ:deptno>
<typ:crDate>2009-08-13T08:36:53.000+10:00</typ:crDate>
</typ:DeptTypeUser>
<typ:DeptTypeUser>
<typ:dname>RESEARCH</typ:dname>
<typ:loc>DALLAS</typ:loc>
<typ:deptno>20</typ:deptno>
<typ:crDate>2009-08-13T08:36:53.000+10:00</typ:crDate>
</typ:DeptTypeUser>
<typ:DeptTypeUser>
<typ:dname>SALES</typ:dname>
<typ:loc>CHICAGO</typ:loc>
<typ:deptno>30</typ:deptno>
<typ:crDate>2009-08-13T08:36:53.000+10:00</typ:crDate>
</typ:DeptTypeUser>
<typ:DeptTypeUser>
<typ:dname>OPERATIONS</typ:dname>
<typ:loc>BOSTON</typ:loc>
<typ:deptno>40</typ:deptno>
<typ:crDate>2009-08-13T08:36:53.000+10:00</typ:crDate>
</typ:DeptTypeUser>
</result>
</m:testDeptTableResponse>
</env:Body>
</env:Envelope>

Note: If you wanted to test using the Table as an IN parameter simply create a method as follows in the PLSQL package which would enable you to test this as well.

function echo_dept_list_table (v1 in dept_list_table) return dept_list_table is

begin

return v1;

end echo_dept_list_table;

Monday, 10 August 2009

JDeveloper 11g R1 and PLSQL Web Services

Good to see PLSQL Web Services make it back into JDeveloper 11g R1 and I quickly gave it a test run today on some old but useful PLSQL code I previously used. By chance I stumbled upon the ability to test the web service in a browser once you have a running web service.

1. Right click on your created PLSQL Web Service and select Run
2. Wait for output as follows indicating the WS is running.

Warning: JMS queue 'weblogic.wsee.DefaultQueue' is not found, as a result, Web Service async responses via jms transport is not supported. If the target service uses JMS transport, the responses will not be able to come back. Run startup time: 6406 ms.
[Application WebServices deployed to Server Instance DefaultServer]

Target URL -- http://localhost:7101/WebServices-PLSQLWebServices-context-root/PLSQLWSPort

09/08/10 14:11:18 Web service PLSQLWS has been started on the embedded server


3. Copy and paste the URL into a browser and there is your web client.
4. Click on "Test Page"


Thursday, 8 May 2008

REF CURSOR parameters in PLSQL Web Services

If you have ever tried to publish a PLSQL package as a Web Service that has a method signature as follows JDeveloper 10.1.3.x doesn't allow you.

procedure test1 (pRefCursor1 out sys_refcursor, prefCursor2 out sys_refcursor);
However the following does work for a function returning a refcursor.

function retrieve_emps return sys_refcursor;
If you press the "Why Not" button in the PLSQL Web Service dialog you get this message.

"Ref cursor type arguments are unsupported due to a JDBC limitation. Ref cursor types are only supported for use as the return type".

Due to this limitation if you have the luxury of developing the PLSQL from scratch you can simple use PLSQL tables to overcome this. So lets assume we have a collection defined as follows.


DROP TYPE employee_list;
DROP TYPE employee_obj;

CREATE OR REPLACE TYPE employee_obj AS OBJECT
( empno NUMBER(4)
, ename VARCHAR2(10)
, job VARCHAR2(9)
, sal NUMBER(7,2)
, deptno NUMBER(2)
);
/
create or replace TYPE employee_list AS TABLE OF employee_obj;

So now we can use a method signature as follows and this will work fine.

procedure copy_emps (pInEmps in employee_list, pOutEmps out employee_list);

Tuesday, 27 November 2007

Debug PLSQL code from JDeveloper 10.1.3.x causing timed out error

You may find that when trying to debug PLSQL from JDeveloper an error as follows appears:

Connecting to the database MyHRConn.
Executing PL/SQL: ALTER SESSION SET PLSQL_DEBUG=TRUE
Executing PL/SQL: CALL DBMS_DEBUG_JDWP.CONNECT_TCP( '172.16.26.237', '4717' )
ORA-30683: failure establishing connection to debugger
ORA-12535: TNS:operation timed out
ORA-06512: at "SYS.DBMS_DEBUG_JDWP", line 68
ORA-06512: at line 1
Process exited.
Disconnecting from the database MyHRConn.

It's very likely from the error you have a firewall between the client (JDeveloper) and the database server in which case you can simply open up the port at the firewall level, but while it's dynamically assigned this won't work, so follow these steps to ensure you use a static port and then you can configure the firewall to enable the connection

1. If necessary, close JDeveloper.
2. In a text editor, open {jdev_install}/jdev/system/oracle.jdeveloper./ide.properties
3. Type the following:

DatabaseDebuggerPortOverride={port_number}

where is the {port_number} is the port number you want the debugger to use.

4. Save ide.properties.

When you restart JDeveloper, the port you specified will be used and hopefully you can connect without any issue.

Debug PLSQL code from JDeveloper 10.1.3.x causing timed out error.

You may find that when trying to debug PLSQL from JDeveloper an error as follows appears:

Connecting to the database MyHRConn.
Executing PL/SQL: ALTER SESSION SET PLSQL_DEBUG=TRUE
Executing PL/SQL: CALL DBMS_DEBUG_JDWP.CONNECT_TCP( '172.16.26.237', '4717' )
ORA-30683: failure establishing connection to debugger
ORA-12535: TNS:operation timed out
ORA-06512: at "SYS.DBMS_DEBUG_JDWP", line 68
ORA-06512: at line 1
Process exited.
Disconnecting from the database MyHRConn.

It's very likely from the error you have a firewall between the client (JDeveloper) and the database server in which case you can simply open up the port at the firewall level, but while it's dynamically assigned this won't work, so follow these steps to ensure you use a static port and then you can configure the firewall to enable the connection

1. If necessary, close JDeveloper. 2. In a text editor, open /ide.properties . 3. Type the following: DatabaseDebuggerPortOverride= where is the port number you want the debugger to use. 4. Save ide.properties.

When you restart JDeveloper, the port you specified will be used and hopefully you can connect without any issue.